Cleaning up
This commit is contained in:
		
							parent
							
								
									7cae807bfa
								
							
						
					
					
						commit
						0db69bf72b
					
				
							
								
								
									
										179
									
								
								Airdrop.fsx
									
									
									
									
									
								
							
							
						
						
									
										179
									
								
								Airdrop.fsx
									
									
									
									
									
								
							| @ -1,179 +0,0 @@ | |||||||
| #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 _ 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 , 16 ) |> Async.StartChild |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Console.ReadLine() |> ignore |  | ||||||
| @ -1,188 +0,0 @@ | |||||||
| #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 ( _ , rpcClientUrl )= devEnv.TryGetValue("RPC_CLIENT") |  | ||||||
| let ( _ , wssClientUrl )= devEnv.TryGetValue("WSS_CLIENT") |  | ||||||
| 
 |  | ||||||
| let keystore = SolanaKeyStoreService() |  | ||||||
| //let file = File.ReadAllText("/home/joe/.config/solana/devnet.json") |  | ||||||
| let file = File.ReadAllText("/home/joe/.config/solana/DegenzW7kWzac5zEdTWEuqVasoVMPKd16T3za2j6S3qR.json") |  | ||||||
| let authority = keystore.RestoreKeystore(file) |  | ||||||
| 
 |  | ||||||
| //let rpcClient = ClientFactory.GetClient(Cluster.DevNet) |  | ||||||
| let rpcClient = ClientFactory.GetClient(rpcClientUrl) |  | ||||||
| 
 |  | ||||||
| //let mintAccount = PublicKey("2iS6gcoB5VhiLC4eNB7NdcaLgEHjLrXHYpz7T2JMGBDw") |  | ||||||
| let mintAccount = PublicKey("4gN1u9LNBnvFERpDU3SrMusMJ1gKLbE9XB6EYraQkWTf") |  | ||||||
| //let ataAccount = PublicKey("CRa7GCMUaB4np32XoTD2sEyCXFVfYKKF4JRPkQTwV3EY") |  | ||||||
| let sourceAtaAccount = PublicKey("7CB459UJC3qtTYJzfC8bFNvdycQGXTPzKoEHxys8Ytfs") |  | ||||||
| //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) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 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( |  | ||||||
|                 sourceAtaAccount, |  | ||||||
|                 associatedTokenAccount, |  | ||||||
|                 amount, |  | ||||||
|                 authority.Account)) |  | ||||||
|             .Build(authority.Account); |  | ||||||
|     async { |  | ||||||
| //        let streamingClient = ClientFactory.GetStreamingClient(Cluster.DevNet) |  | ||||||
|         let streamingClient = ClientFactory.GetStreamingClient(wssClientUrl) |  | ||||||
|         do! streamingClient.ConnectAsync() |> Async.AwaitTask |  | ||||||
|         let blockHash = rpcClient.GetLatestBlockHash() |  | ||||||
|         let! amount = getTokenCountToDrop wallet |> Async.AwaitTask |  | ||||||
|         log $"Dropping {amount} tokens" |  | ||||||
|         let tx = buildTransaction blockHash.Result.Value amount wallet |  | ||||||
|         let! tx = rpcClient.SendTransactionAsync(tx) |> Async.AwaitTask |  | ||||||
|         if tx.ErrorData <> null then |  | ||||||
|             let! _ = updateError wallet (tx.ErrorData.Logs |> String.concat "\n") |> Async.AwaitTask |  | ||||||
|             () |  | ||||||
|             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 |> Async.AwaitTask |  | ||||||
|             log $"Odd Transaction Error" |  | ||||||
|             return () |  | ||||||
|         else |  | ||||||
|             log $"Successful, now waiting for RPC" |  | ||||||
|             let! _ = updatePending wallet tx.Result |> Async.AwaitTask |  | ||||||
|             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, check the tx id" |  | ||||||
|                     updatePendingError wallet msg tx.Result |> Async.AwaitTask |> Async.Ignore |> Async.Start) |> Async.AwaitTask |  | ||||||
|             return () |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| let targetWallets =  |  | ||||||
|     devConnStr |  | ||||||
|     |> Sql.connect |  | ||||||
|     |> Sql.query """ |  | ||||||
|             SELECT wallet_address FROM crypto WHERE status = 'Ready' AND total_dropped = 0; |  | ||||||
|         """ |  | ||||||
|     |> Sql.execute (fun reader -> reader.string "wallet_address") |  | ||||||
| 
 |  | ||||||
| printfn $"Got target wallets: {targetWallets.Length}" |  | ||||||
| 
 |  | ||||||
| let asyncs = |  | ||||||
|     targetWallets |  | ||||||
|     |> List.map executeDrop |  | ||||||
|     |> List.chunkBySize 5 |  | ||||||
| 
 |  | ||||||
| async { |  | ||||||
|     for a in asyncs do |  | ||||||
|         let! _ = Async.Parallel a |  | ||||||
|         do! Async.Sleep 500 |  | ||||||
| } |> Async.Start |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Console.ReadLine() |> ignore |  | ||||||
| 
 |  | ||||||
| @ -1,148 +0,0 @@ | |||||||
| #load "/home/joe/Development/DegenzGame/.paket/load/net6.0/main.group.fsx";; |  | ||||||
| 
 |  | ||||||
| open Npgsql.FSharp |  | ||||||
| open DSharpPlus |  | ||||||
| open DSharpPlus.Entities |  | ||||||
| open Solnet.Programs |  | ||||||
| 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 firstDrop = |  | ||||||
|     users |  | ||||||
|     |> Seq.filter (fun u ->  u.Roles |> Seq.exists (fun role -> |  | ||||||
|         role.Id = 978229149569286174uL)) |  | ||||||
|     |> Seq.distinct |  | ||||||
|     |> Seq.toList |  | ||||||
|      |  | ||||||
| printfn $"{firstDrop.Length}" |  | ||||||
|      |  | ||||||
| let changeStatus ids = |  | ||||||
|     devConnStr |  | ||||||
|     |> Sql.connect |  | ||||||
|     |> Sql.parameters [ "ids" , Sql.stringArray ids ] |  | ||||||
|     |> Sql.query """ |  | ||||||
|             UPDATE crypto SET status = 'Ready' WHERE discord_id = ANY (ARRAY[@ids]::varchar[]) AND status = 'Nothing' |  | ||||||
|         """ |  | ||||||
|     |> Sql.executeNonQuery |  | ||||||
| 
 |  | ||||||
| changeStatus (firstDrop |> List.map (fun m -> string m.Id) |> List.toArray) |  | ||||||
|      |  | ||||||
| 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 (discord_id) DO |  | ||||||
|             UPDATE SET wallet_address = @wallet, has_wl = @wl , has_og = @og WHERE crypto.discord_id = @did AND crypto.status = 'Nothing'; |  | ||||||
|         """ |  | ||||||
|     |> Sql.executeNonQuery |  | ||||||
|      |  | ||||||
| let updateUserWithWallet wallet wl og = |  | ||||||
|     devConnStr |  | ||||||
|     |> Sql.connect |  | ||||||
|     |> Sql.parameters [ "wallet" , Sql.string wallet ; "wl" , Sql.bool wl ; "og" , Sql.bool og ] |  | ||||||
|     |> Sql.query """ |  | ||||||
|             UPDATE crypto SET has_wl = @wl, has_og = @og WHERE crypto.wallet_address = @wallet AND crypto.status = 'Nothing'; |  | ||||||
|         """ |  | ||||||
|     |> Sql.executeNonQuery |  | ||||||
|      |  | ||||||
| let prune ids = |  | ||||||
|     devConnStr |  | ||||||
|     |> Sql.connect |  | ||||||
|     |> Sql.parameters [ "ids" , Sql.stringArray ids ] |  | ||||||
|     |> Sql.query """ |  | ||||||
|             DELETE FROM crypto WHERE discord_id = ANY (ARRAY[@ids]::varchar[]) AND status = 'Nothing' |  | ||||||
|         """ |  | ||||||
|     |> Sql.executeNonQuery |  | ||||||
|      |  | ||||||
| let getExisting () = |  | ||||||
|     devConnStr |  | ||||||
|     |> Sql.connect |  | ||||||
|     |> Sql.query """ |  | ||||||
|             SELECT discord_id, display_name, wallet_address FROM crypto |  | ||||||
|         """ |  | ||||||
|     |> Sql.execute (fun reader -> {| Id = reader.string "discord_id" |> uint64 ; Name = reader.string "display_name" ; Wallet = reader.string "wallet_address" |}) |  | ||||||
|      |  | ||||||
| let walletExists wallet = |  | ||||||
|     let list = |  | ||||||
|         devConnStr |  | ||||||
|         |> Sql.connect |  | ||||||
|         |> Sql.parameters [ "wallet" , Sql.string wallet ] |  | ||||||
|         |> Sql.query """ |  | ||||||
|                 SELECT wallet_address FROM crypto WHERE wallet_address = @wallet |  | ||||||
|             """ |  | ||||||
|         |> Sql.execute (fun reader -> reader.string "wallet_address") |  | ||||||
|     not list.IsEmpty  |  | ||||||
|      |  | ||||||
| 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 |  | ||||||
|                     if walletExists wa.Wallet then |  | ||||||
|                         let _ = updateUserWithWallet wa.Wallet hasWL hasOG |  | ||||||
|                         () |  | ||||||
|                     else |  | ||||||
|                         let _ = insert wa.Id wa.Name wa.Wallet hasWL hasOG |  | ||||||
|                         () |  | ||||||
|                 with ex -> printfn $"Huh? {ex.Message}" |  | ||||||
|         () |  | ||||||
|     () |  | ||||||
|     let existing = getExisting () |  | ||||||
|     let usersToFilter = |  | ||||||
|         existing |> List.filter (fun wa -> (whitelisted |> List.exists (fun wl -> wl.Id = wa.Id)) |> not)  |  | ||||||
|     let toPrune = usersToFilter |> List.map (fun u -> u.Id |> string) |> List.toArray |  | ||||||
|     let _ = prune toPrune |  | ||||||
|     if toPrune.Length > 0 then |  | ||||||
|         printfn $"Pruning {toPrune.Length} users: {toPrune}" |  | ||||||
|     let existing = getExisting () |  | ||||||
|     printfn $"Total Users: {existing.Length}" |  | ||||||
|          |  | ||||||
| } |> Async.RunSynchronously |  | ||||||
| 
 |  | ||||||
| bot.DisconnectAsync() |> Async.AwaitTask |> Async.RunSynchronously |  | ||||||
| @ -1,96 +0,0 @@ | |||||||
| #load "/home/joe/Development/DegenzGame/.paket/load/net6.0/main.group.fsx";; |  | ||||||
| 
 |  | ||||||
| open Npgsql.FSharp |  | ||||||
| open dotenv.net |  | ||||||
| open System.IO |  | ||||||
| open System.Diagnostics |  | ||||||
| open System.Threading.Tasks |  | ||||||
| 
 |  | ||||||
| let prodEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "./.prod.env" ])) |  | ||||||
| let ( _ , prodConnStr )= prodEnv.TryGetValue("DATABASE_URL") |  | ||||||
| 
 |  | ||||||
| type NftTransfer = { |  | ||||||
|     Id : uint64 |  | ||||||
|     Name : string |  | ||||||
|     NftName : string |  | ||||||
|     NftAddress : string |  | ||||||
|     UserWallet : string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| printfn "Getting Wallet Addresses:" |  | ||||||
| 
 |  | ||||||
| let pending = |  | ||||||
|     prodConnStr |  | ||||||
|     |> Sql.connect |  | ||||||
|     |> Sql.query """ |  | ||||||
|             SELECT u.id, u.display_name, token.name, token.address, mpa.tx_wallet FROM gen_one_token token |  | ||||||
|             JOIN mint_pass_assignment mpa on token.id = mpa.token_id |  | ||||||
|             JOIN "user" u on mpa.recipient_id = u.id |  | ||||||
|             WHERE mpa.fulfilled = false AND token.locked = false |  | ||||||
|             ORDER BY mpa.created_at; |  | ||||||
|         """ |  | ||||||
|     |> Sql.execute (fun reader -> |  | ||||||
|             { Id = reader.string "id" |> uint64 |  | ||||||
|               Name = reader.string "display_name" |  | ||||||
|               NftName = reader.string "name" |  | ||||||
|               NftAddress = reader.string "address" |  | ||||||
|               UserWallet = reader.string "tx_wallet" }) |  | ||||||
|      |  | ||||||
| 
 |  | ||||||
| let testSample = [ pending |> List.last ] |  | ||||||
| 
 |  | ||||||
| type CommandResult = {  |  | ||||||
|   ExitCode: int;  |  | ||||||
|   StandardOutput: string; |  | ||||||
|   StandardError: string  |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| let executeCommand executable args = |  | ||||||
|     async { |  | ||||||
|         let startInfo = ProcessStartInfo() |  | ||||||
|         startInfo.FileName <- executable |  | ||||||
| 
 |  | ||||||
|         for a in args do |  | ||||||
|             startInfo.ArgumentList.Add(a) |  | ||||||
| 
 |  | ||||||
|         startInfo.RedirectStandardOutput <- true |  | ||||||
|         startInfo.RedirectStandardError <- true |  | ||||||
|         startInfo.UseShellExecute <- false |  | ||||||
|         startInfo.CreateNoWindow <- true |  | ||||||
|         use p = new Process() |  | ||||||
|         p.StartInfo <- startInfo |  | ||||||
|         p.Start() |> ignore |  | ||||||
| 
 |  | ||||||
|         let outTask = |  | ||||||
|             Task.WhenAll( |  | ||||||
|                 [| p.StandardOutput.ReadToEndAsync() |  | ||||||
|                    p.StandardError.ReadToEndAsync() |] |  | ||||||
|             ) |  | ||||||
| 
 |  | ||||||
|         do! p.WaitForExitAsync() |> Async.AwaitTask |  | ||||||
|         let! out = outTask |> Async.AwaitTask |  | ||||||
| 
 |  | ||||||
|         return |  | ||||||
|             { ExitCode = p.ExitCode |  | ||||||
|               StandardOutput = out.[0] |  | ||||||
|               StandardError = out.[1] } |  | ||||||
|     } |  | ||||||
| let executeShellCommand command = |  | ||||||
|     executeCommand "/usr/bin/env" [ "-S"; "bash"; "-c"; command ] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| let transfer (nft : NftTransfer) = |  | ||||||
|     printfn $"Transferring {nft.NftName} to {nft.Name} " |  | ||||||
|     let result = |  | ||||||
|         $"spl-token transfer --allow-unfunded-recipient --fund-recipient {nft.NftAddress} 1 {nft.UserWallet}" |  | ||||||
|         |> executeShellCommand  |  | ||||||
|         |> Async.RunSynchronously |  | ||||||
|     if result.ExitCode = 0 then |  | ||||||
|         let user = $"{nft.Id} - {nft.Name} - {nft.NftName}" |  | ||||||
|         let tx = result.StandardOutput |  | ||||||
|         File.AppendAllLines("/home/joe/Downloads/transactions", [ user ; tx ]) |  | ||||||
|     else |  | ||||||
|         printfn $"{result.StandardError}" |  | ||||||
| 
 |  | ||||||
| for nft in pending do |  | ||||||
|     transfer nft |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user