Scripts for whitelist airdrop
This commit is contained in:
parent
7fbc8bd868
commit
8c14e97c83
@ -28,6 +28,8 @@
|
|||||||
<Compile Include="Whitelist.fs" />
|
<Compile Include="Whitelist.fs" />
|
||||||
<Compile Include="Admin.fs" />
|
<Compile Include="Admin.fs" />
|
||||||
<Compile Include="Bot.fs" />
|
<Compile Include="Bot.fs" />
|
||||||
|
<None Include="Scripts\Whitelist.fsx" />
|
||||||
|
<None Include="Scripts\Airdrop.fsx" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="..\.paket\Paket.Restore.targets" />
|
<Import Project="..\.paket\Paket.Restore.targets" />
|
||||||
</Project>
|
</Project>
|
||||||
|
180
Bot/Scripts/Airdrop.fsx
Normal file
180
Bot/Scripts/Airdrop.fsx
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#load "/home/joe/Development/DegenzGame/.paket/load/net6.0/main.group.fsx";;
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.IO
|
||||||
|
open Npgsql.FSharp
|
||||||
|
open dotenv.net
|
||||||
|
open Solnet.Rpc
|
||||||
|
open Solnet.Rpc.Models
|
||||||
|
open Solnet.Rpc.Builders
|
||||||
|
open Solnet.Wallet
|
||||||
|
open Solnet.Programs
|
||||||
|
open Solnet.KeyStore
|
||||||
|
|
||||||
|
let devEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.dev.env" ]))
|
||||||
|
|
||||||
|
let ( _ , devConnStr )= devEnv.TryGetValue("DATABASE_URL")
|
||||||
|
|
||||||
|
let keystore = SolanaKeyStoreService()
|
||||||
|
let file = File.ReadAllText("/home/joe/.config/solana/devnet.json")
|
||||||
|
let authority = keystore.RestoreKeystore(file)
|
||||||
|
|
||||||
|
//let rpcClient = ClientFactory.GetClient("https://still-empty-field.solana-mainnet.quiknode.pro/5b1b6b5c913ec79a20bef19d5ba5f63023e470d6/")
|
||||||
|
let rpcClient = ClientFactory.GetClient(Cluster.DevNet)
|
||||||
|
|
||||||
|
let mintAccount = PublicKey("2iS6gcoB5VhiLC4eNB7NdcaLgEHjLrXHYpz7T2JMGBDw")
|
||||||
|
//let associatedTokenAccountOwner = PublicKey("GutKESfJw8PDMbFVqByxTr4f5TUSHUVmkf5gtsWWWqrU")
|
||||||
|
|
||||||
|
type AirdropStatus =
|
||||||
|
| Hold = 0
|
||||||
|
| Ready = 1
|
||||||
|
| Pending = 2
|
||||||
|
| Error = 3
|
||||||
|
| PendingError = 4
|
||||||
|
| Completed = 5
|
||||||
|
| Verified = 6
|
||||||
|
|
||||||
|
let updateError wallet msg =
|
||||||
|
devConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "wallet" , Sql.string wallet ; "msg" , Sql.string msg ]
|
||||||
|
|> Sql.query """
|
||||||
|
UPDATE crypto SET error_msg = @msg, status = 'Error' WHERE crypto.wallet_address = @wallet;
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|
||||||
|
let updatePendingError wallet msg txId =
|
||||||
|
devConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "wallet" , Sql.string wallet ; "msg" , Sql.string msg ; "txId" , Sql.string txId ]
|
||||||
|
|> Sql.query """
|
||||||
|
UPDATE crypto SET error_msg = @msg, pending_tx = @txId, status = 'PendingError' WHERE crypto.wallet_address = @wallet;
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|
||||||
|
let updatePending wallet txId =
|
||||||
|
devConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ]
|
||||||
|
|> Sql.query """
|
||||||
|
UPDATE crypto SET pending_tx = @txId, status = 'Pending' WHERE crypto.wallet_address = @wallet;
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|
||||||
|
let updateCompleted wallet txId =
|
||||||
|
devConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ]
|
||||||
|
|> Sql.query """
|
||||||
|
UPDATE crypto SET successful_tx = pending_tx, status = 'Completed' WHERE crypto.wallet_address = @wallet;
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|
||||||
|
let updateVerified wallet txId amount =
|
||||||
|
devConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "wallet" , Sql.string wallet ; "txId" , Sql.string txId ; "amount" , Sql.int amount ]
|
||||||
|
|> Sql.query """
|
||||||
|
UPDATE crypto SET pending_tx = @txId, status = 'Verified', total_dropped = @amount WHERE crypto.wallet_address = @wallet;
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQueryAsync
|
||||||
|
|
||||||
|
|
||||||
|
let getTokenCountToDrop wallet =
|
||||||
|
task {
|
||||||
|
let! wl =
|
||||||
|
devConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "wallet" , Sql.string wallet ]
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT has_wl, has_og FROM crypto WHERE wallet_address = @wallet;
|
||||||
|
"""
|
||||||
|
|> Sql.executeRowAsync (fun reader -> {| HasWhitelist = reader.bool "has_wl" ; HasOg = reader.bool "has_og" |})
|
||||||
|
return (if wl.HasWhitelist then 1uL else 0uL) + (if wl.HasOg then 2uL else 0uL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Change was_successful to status and check if attempted, pending, errored, or completed
|
||||||
|
let executeDrop (wallet : string) =
|
||||||
|
let associatedTokenAccountOwner = PublicKey(wallet)
|
||||||
|
let associatedTokenAccount = AssociatedTokenAccountProgram.DeriveAssociatedTokenAccount(associatedTokenAccountOwner, mintAccount)
|
||||||
|
let log msg = printfn $"{wallet} || {msg}"
|
||||||
|
|
||||||
|
log $"ATA: {associatedTokenAccount.Key}"
|
||||||
|
let buildTransaction (block : LatestBlockHash) amount (targetWallet : string) =
|
||||||
|
TransactionBuilder()
|
||||||
|
.SetRecentBlockHash(block.Blockhash)
|
||||||
|
.SetFeePayer(authority.Account)
|
||||||
|
.AddInstruction(AssociatedTokenAccountProgram.CreateAssociatedTokenAccount(
|
||||||
|
authority.Account,
|
||||||
|
PublicKey(targetWallet),
|
||||||
|
mintAccount))
|
||||||
|
.AddInstruction(TokenProgram.Transfer(
|
||||||
|
PublicKey("CRa7GCMUaB4np32XoTD2sEyCXFVfYKKF4JRPkQTwV3EY"),
|
||||||
|
associatedTokenAccount,
|
||||||
|
amount,
|
||||||
|
authority.Account))
|
||||||
|
.Build(authority.Account);
|
||||||
|
task {
|
||||||
|
// let streamingClient = ClientFactory.GetStreamingClient("wss://still-empty-field.solana-mainnet.quiknode.pro/5b1b6b5c913ec79a20bef19d5ba5f63023e470d6/")
|
||||||
|
let streamingClient = ClientFactory.GetStreamingClient(Cluster.DevNet)
|
||||||
|
do! streamingClient.ConnectAsync()
|
||||||
|
let blockHash = rpcClient.GetLatestBlockHash()
|
||||||
|
let! amount = getTokenCountToDrop wallet
|
||||||
|
log $"Dropping {amount} tokens"
|
||||||
|
let tx = buildTransaction blockHash.Result.Value amount wallet
|
||||||
|
let! tx = rpcClient.SendTransactionAsync(tx)
|
||||||
|
if tx.ErrorData <> null then
|
||||||
|
let! _ = updateError wallet (tx.ErrorData.Logs |> String.concat "\n")
|
||||||
|
()
|
||||||
|
log $"Transaction error: {wallet}"
|
||||||
|
return ()
|
||||||
|
elif String.IsNullOrWhiteSpace(tx.Result) then
|
||||||
|
let msg = $"Transaction did not have an ID but the ErrorData was null: {tx.Reason}"
|
||||||
|
let! _ = updateError wallet msg
|
||||||
|
log $"Odd Transaction Error"
|
||||||
|
return ()
|
||||||
|
else
|
||||||
|
log $"Successful, now waiting for RPC"
|
||||||
|
let! _ = updatePending wallet tx.Result
|
||||||
|
let! _ = streamingClient.SubscribeSignatureAsync(tx.Result, fun sub result ->
|
||||||
|
if result.Value.Error = null then
|
||||||
|
log "RPC Finished"
|
||||||
|
task {
|
||||||
|
let! _ = updateCompleted wallet tx.Result
|
||||||
|
log "Getting Transaction and Token Balance"
|
||||||
|
let! txInfo = rpcClient.GetTransactionAsync(tx.Result)
|
||||||
|
let! tokenBalance = rpcClient.GetTokenAccountBalanceAsync(associatedTokenAccount)
|
||||||
|
|
||||||
|
log $"Transaction Successful? {txInfo.WasSuccessful} - Token Balance {tokenBalance.Result.Value.AmountUlong}"
|
||||||
|
if txInfo.WasSuccessful = true && tokenBalance.Result.Value.AmountUlong = amount then
|
||||||
|
let! _ = updateVerified wallet tx.Result (int amount)
|
||||||
|
()
|
||||||
|
return ()
|
||||||
|
} |> Async.AwaitTask |> Async.Start
|
||||||
|
else
|
||||||
|
let msg = $"Got an error, let's check it out {result.Value.Error}"
|
||||||
|
updatePendingError wallet msg tx.Result |> Async.AwaitTask |> Async.Ignore |> Async.Start)
|
||||||
|
return ()
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetWallets =
|
||||||
|
devConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT wallet_address FROM crypto WHERE status = 'Ready';
|
||||||
|
"""
|
||||||
|
|> Sql.execute (fun reader -> reader.string "wallet_address")
|
||||||
|
|
||||||
|
printfn $"Got target wallets: {targetWallets.Length}"
|
||||||
|
|
||||||
|
// "GK7rkZYrdAEpTm9n9TkHWK1T5nDXeRfVUVfcHQwSDyuJ"
|
||||||
|
let asyncs =
|
||||||
|
targetWallets
|
||||||
|
|> List.map executeDrop
|
||||||
|
|> List.map Async.AwaitTask
|
||||||
|
|
||||||
|
Async.Parallel ( asyncs , 10 ) |> Async.StartChild
|
||||||
|
|
||||||
|
|
||||||
|
Console.ReadLine() |> ignore
|
||||||
|
|
73
Bot/Scripts/Whitelist.fsx
Normal file
73
Bot/Scripts/Whitelist.fsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#load "/home/joe/Development/DegenzGame/.paket/load/net6.0/main.group.fsx";;
|
||||||
|
|
||||||
|
open Npgsql.FSharp
|
||||||
|
open DSharpPlus
|
||||||
|
open dotenv.net
|
||||||
|
|
||||||
|
let prodEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.prod.env" ]))
|
||||||
|
let devEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.dev.env" ]))
|
||||||
|
|
||||||
|
let ( _ , prodConnStr )= prodEnv.TryGetValue("DATABASE_URL")
|
||||||
|
let ( _ , devConnStr )= devEnv.TryGetValue("DATABASE_URL")
|
||||||
|
let ( _ , adminBotToken )= prodEnv.TryGetValue("TOKEN_ADMINBOT")
|
||||||
|
|
||||||
|
let botConfig = DiscordConfiguration()
|
||||||
|
botConfig.TokenType <- TokenType.Bot
|
||||||
|
botConfig.Intents <- DiscordIntents.All
|
||||||
|
botConfig.Token <- adminBotToken
|
||||||
|
|
||||||
|
printfn "Connecting"
|
||||||
|
let bot = new DiscordClient(botConfig)
|
||||||
|
bot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
||||||
|
|
||||||
|
printfn "Getting Guild"
|
||||||
|
let prodGuild = 933888229776703559uL
|
||||||
|
let guild = bot.GetGuildAsync(prodGuild) |> Async.AwaitTask |> Async.RunSynchronously
|
||||||
|
printfn "Getting Members"
|
||||||
|
let users = guild.GetAllMembersAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
||||||
|
printfn $"Total Members: {users.Count}"
|
||||||
|
|
||||||
|
let whitelisted =
|
||||||
|
users
|
||||||
|
|> Seq.filter (fun u -> u.Roles |> Seq.exists (fun role -> role.Name.Contains("Confirmed")))
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
printfn $"Total Whitelist Confirmed: {whitelisted.Length}"
|
||||||
|
|
||||||
|
printfn "Getting Wallet Addresses:"
|
||||||
|
let walletAddresses =
|
||||||
|
prodConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.query """
|
||||||
|
SELECT DISTINCT discord_id, display_name, wallet_address FROM "user" WHERE wallet_address IS NOT NULL;
|
||||||
|
"""
|
||||||
|
|> Sql.execute (fun reader -> {| Id = reader.string "discord_id" |> uint64 ; Name = reader.string "display_name" ; Wallet = reader.string "wallet_address" |})
|
||||||
|
|
||||||
|
printfn $"Total Wallet Addresses: {walletAddresses.Length}"
|
||||||
|
|
||||||
|
let insert did name wallet wl og =
|
||||||
|
devConnStr
|
||||||
|
|> Sql.connect
|
||||||
|
|> Sql.parameters [ "did" , Sql.string (string did) ; "name" , Sql.string name ; "wallet" , Sql.string wallet
|
||||||
|
"wl" , Sql.bool wl ; "og" , Sql.bool og ]
|
||||||
|
|> Sql.query """
|
||||||
|
INSERT INTO crypto(discord_id, display_name, wallet_address, has_wl, has_og) VALUES (@did, @name, @wallet, @wl, @og)
|
||||||
|
ON CONFLICT (wallet_address) DO
|
||||||
|
UPDATE SET display_name = @name, discord_id = @did , has_wl = @wl , has_og = @og WHERE crypto.wallet_address = @wallet;
|
||||||
|
"""
|
||||||
|
|> Sql.executeNonQuery
|
||||||
|
|
||||||
|
printfn "Inserting"
|
||||||
|
async {
|
||||||
|
for user in whitelisted do
|
||||||
|
for wa in walletAddresses do
|
||||||
|
if wa.Id = user.Id then
|
||||||
|
let hasWL = user.Roles |> Seq.exists (fun role -> role.Name = "Whitelist Confirmed")
|
||||||
|
let hasOG = user.Roles |> Seq.exists (fun role -> role.Name = "OG Whitelist Confirmed")
|
||||||
|
try
|
||||||
|
let _ = insert wa.Id wa.Name wa.Wallet hasWL hasOG
|
||||||
|
()
|
||||||
|
with ex -> printfn $"{ex.Message}"
|
||||||
|
} |> Async.RunSynchronously
|
||||||
|
|
||||||
|
bot.DisconnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
|
@ -13,4 +13,7 @@ nuget MongoDB.Driver
|
|||||||
nuget dotenv.net 3.1.1
|
nuget dotenv.net 3.1.1
|
||||||
nuget Npgsql.FSharp
|
nuget Npgsql.FSharp
|
||||||
nuget mixpanel-csharp 5.0.0
|
nuget mixpanel-csharp 5.0.0
|
||||||
|
nuget Solnet.Extensions
|
||||||
|
nuget Solnet.KeyStore
|
||||||
|
nuget Solnet.Programs
|
||||||
nuget Solnet.Rpc
|
nuget Solnet.Rpc
|
||||||
|
16
paket.lock
16
paket.lock
@ -176,11 +176,21 @@ NUGET
|
|||||||
SharpCompress (0.30.1)
|
SharpCompress (0.30.1)
|
||||||
System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461))
|
System.Memory (>= 4.5.4) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net461))
|
||||||
System.Text.Encoding.CodePages (>= 5.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (== netstandard2.1)
|
System.Text.Encoding.CodePages (>= 5.0) - restriction: || (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (== netstandard2.0) (== netstandard2.1)
|
||||||
Solnet.Rpc (6.0.10)
|
Solnet.Extensions (6.0.11)
|
||||||
|
Solnet.Programs (>= 6.0.11) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
|
Solnet.Rpc (>= 6.0.11) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
|
Solnet.Wallet (>= 6.0.11) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
|
Solnet.KeyStore (6.0.11)
|
||||||
|
Chaos.NaCl.Standard (>= 1.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
|
Portable.BouncyCastle (>= 1.9) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
|
Solnet.Wallet (>= 6.0.11) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
|
Solnet.Programs (6.0.11)
|
||||||
|
Solnet.Rpc (>= 6.0.11) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
|
Solnet.Rpc (6.0.11)
|
||||||
Microsoft.Extensions.Logging (>= 6.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
Microsoft.Extensions.Logging (>= 6.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
Microsoft.Extensions.Logging.Console (>= 6.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
Microsoft.Extensions.Logging.Console (>= 6.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
Solnet.Wallet (>= 6.0.10) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
Solnet.Wallet (>= 6.0.11) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
Solnet.Wallet (6.0.10) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
Solnet.Wallet (6.0.11) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
Chaos.NaCl.Standard (>= 1.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
Chaos.NaCl.Standard (>= 1.0) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
Portable.BouncyCastle (>= 1.9) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
Portable.BouncyCastle (>= 1.9) - restriction: || (== net6.0) (&& (== netstandard2.0) (>= net6.0)) (&& (== netstandard2.1) (>= net6.0))
|
||||||
System.Buffers (4.5.1)
|
System.Buffers (4.5.1)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user