UNPKG

@streamflow/timelock

Version:

SDK to interact with StreamFlow Finance's Timelock program on Solana.

440 lines (408 loc) 13.8 kB
const assert = require("assert"); const anchor = require("@project-serum/anchor"); const common = require("@project-serum/common"); const { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, Token, NATIVE_MINT, } = require("@solana/spl-token"); const { PublicKey, SYSVAR_RENT_PUBKEY, LAMPORTS_PER_SOL, } = require("@solana/web3.js"); const { decode } = require("./layout"); const { SystemProgram, Keypair } = anchor.web3; const { BN } = anchor; // The stream recipient main wallet const recipient = Keypair.generate(); describe("timelock", () => { const provider = anchor.Provider.local(); //todo use env() anchor.setProvider(provider); const program = anchor.workspace.Timelock; const sender = provider.wallet; const metadata = Keypair.generate(); const MINT_DECIMALS = 8; let escrowTokens; let recipientTokens; let nonce; let mint; let senderTokens; // Divide by 1000 since Unix timestamp is seconds const start = new BN(+new Date() / 1000 + 4); // +60 seconds const end = new BN(+new Date() / 1000 + 60); // In seconds const period = new BN(1); // Amount to deposit // const depositedAmount = new BN(1337_000_000); const depositedAmount = new BN(1 * LAMPORTS_PER_SOL); //const depositedAmount = new BN(133769 * 10 ** MINT_DECIMALS); it("Initialize test state", async () => { [mint, senderTokens] = await common.createMintAndVault( provider, new anchor.BN(100_000_000_000), undefined, MINT_DECIMALS ); mint = NATIVE_MINT; //senderTokens = await Token.getAssociatedTokenAddress(ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, NATIVE_MINT, sender.publicKey); const oldBalance = await provider.connection.getBalance(sender.publicKey); senderTokens = await Token.createWrappedNativeAccount( provider.connection, TOKEN_PROGRAM_ID, sender.publicKey, sender.payer, 10 * LAMPORTS_PER_SOL ); //todo check for Number overflow here. const senderTokensData = common.token.parseTokenAccountData( (await program.provider.connection.getAccountInfo(senderTokens)).data ); const newBalance = await provider.connection.getBalance(sender.publicKey); console.log( "spent for creating wrapped SOL account\n", oldBalance - newBalance ); console.log("Sender Tokens:"); console.log( "account", senderTokens.toBase58(), "mint", senderTokensData.mint.toBase58(), "amount", senderTokensData.amount / LAMPORTS_PER_SOL, "owner", senderTokensData.owner.toBase58(), senderTokensData.owner.toBase58() === sender.publicKey.toBase58() ); [escrowTokens, nonce] = await PublicKey.findProgramAddress( [metadata.publicKey.toBuffer()], program.programId ); recipientTokens = await Token.getAssociatedTokenAddress( ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, mint, recipient.publicKey ); console.log("Accounts:"); console.log("sender wallet:", sender.publicKey.toBase58()); console.log("sender tokens:", senderTokens.toBase58()); console.log("escrow (metadata):", metadata.publicKey.toBase58()); console.log("escrow tokens:", escrowTokens.toBase58()); console.log("recipient wallet:", recipient.publicKey.toBase58()); console.log("recipient tokens:", recipientTokens.toBase58()); console.log("mint:", mint.toBase58()); }); it("Create Vesting Contract w/out the cliff", async () => { console.log("\n\n"); console.log("metadata:", metadata.publicKey.toBase58()); console.log("buffer:", metadata.publicKey.toBuffer()); const tx = await program.rpc.create( // Order of the parameters must match the ones in the program depositedAmount, start, end, period, new BN(0), //cliff new BN(0), //cliff amount { accounts: { sender: sender.publicKey, senderTokens, recipient: recipient.publicKey, recipientTokens, metadata: metadata.publicKey, escrowTokens, mint, rent: SYSVAR_RENT_PUBKEY, timelockProgram: program.programId, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, }, signers: [metadata], } ); const _escrowTokens = await program.provider.connection.getAccountInfo( escrowTokens ); const _senderTokens = await program.provider.connection.getAccountInfo( senderTokens ); const _metadata = await program.provider.connection.getAccountInfo( metadata.publicKey ); const _escrowTokensData = common.token.parseTokenAccountData( _escrowTokens.data ); const _senderTokensData = common.token.parseTokenAccountData( _senderTokens.data ); let strm_data = decode(_metadata.data); console.log("Raw data:\n", _metadata.data); console.log("Stream Data:\n", strm_data); console.log( "deposited during contract creation: ", depositedAmount.toNumber(), _escrowTokensData.amount ); assert.ok(depositedAmount.toNumber() === _escrowTokensData.amount); }); it("Withdraws from a contract", async () => { setTimeout(async () => { console.log("Withdraw:\n"); console.log("recipient tokens", recipientTokens.toBase58()); const oldEscrowAta = await program.provider.connection.getAccountInfo( escrowTokens ); const oldEscrowAmount = common.token.parseTokenAccountData( oldEscrowAta.data ).amount; const oldRecipientAta = await program.provider.connection.getAccountInfo( recipientTokens ); const oldRecipientAmount = common.token.parseTokenAccountData( oldRecipientAta.data ).amount; const withdrawAmount = new BN(0); //0 == MAX console.log( "metadata", metadata.publicKey.toBase58(), "escrow_ata", escrowTokens.toBase58() ); console.log("seed", metadata.publicKey.toBuffer()); console.log("metadata", metadata.publicKey.toBase58()); await program.rpc.withdraw(withdrawAmount, { accounts: { withdrawAuthority: recipient.publicKey, sender: sender.publicKey, recipient: recipient.publicKey, recipientTokens, metadata: metadata.publicKey, escrowTokens, mint, tokenProgram: TOKEN_PROGRAM_ID, }, signers: [recipient], }); const _metadata = await program.provider.connection.getAccountInfo( metadata.publicKey ); let strm_data = decode(_metadata.data); console.log("Stream Data:\n", strm_data); const newRecipientAta = await program.provider.connection.getAccountInfo( recipientTokens ); const newRecipientAmount = common.token.parseTokenAccountData( newRecipientAta.data ).amount; const escrow = await program.provider.connection.getAccountInfo( metadata.publicKey ); const data = decode(escrow.data); let newEscrowAmount = null; const newEscrowAta = await program.provider.connection.getAccountInfo( escrowTokens ); if (newEscrowAta) { newEscrowAmount = common.token.parseTokenAccountData( newEscrowAta.data ).amount; } console.log( "depositedAmount", depositedAmount.toNumber(), "withdrawn", withdrawAmount ); console.log( "Escrow token balance: previous: ", oldEscrowAmount, "after: ", newEscrowAmount ); console.log( "Recipient token balance: previous: ", oldRecipientAmount, "after: ", newRecipientAmount ); // assert.ok(withdrawAmount.eq(new BN(oldEscrowAmount - newEscrowAmount))); assert.ok( withdrawAmount.eq(new BN(newRecipientAmount - oldRecipientAmount)) ); assert.ok(data.withdrawn.eq(withdrawAmount)); }, 6100); }); it("Transfers vesting contract recipient", async () => { let escrow = await program.provider.connection.getAccountInfo( metadata.publicKey ); const oldRecipient = decode(escrow.data).recipient; const newRecipient = Keypair.generate(); const newRecipientTokens = await Token.getAssociatedTokenAddress( ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, mint, newRecipient.publicKey ); //airdrop const tx = await program.provider.connection.requestAirdrop( recipient.publicKey, 2 * LAMPORTS_PER_SOL ); //wait for the airdrop setTimeout(async () => { console.log( "\nTransfer:\n" // "SOL balance: ", // await program.provider.connection.getBalance(recipient.publicKey) ); console.log("old recipient", oldRecipient.toBase58()); console.log( "new recipient", newRecipient.publicKey.toBase58(), "new recipient ata:", newRecipientTokens.toBase58() ); await program.rpc.transfer_recipient({ accounts: { existingRecipient: recipient.publicKey, newRecipient: newRecipient.publicKey, newRecipientTokens, metadata: metadata.publicKey, escrowTokens, mint, rent: SYSVAR_RENT_PUBKEY, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, system: SystemProgram.programId, }, signers: [recipient], }); console.log("Update recipient success."); escrow = await program.provider.connection.getAccountInfo( metadata.publicKey ); console.log("parsed", decode(escrow.data)); const escrowNewRecipient = decode(escrow.data).recipient; console.log( "Transfer: old recipient:", oldRecipient.toBase58(), "new recipient: ", escrowNewRecipient.toBase58() ); console.log( "Transfer: old recipient:", recipient.publicKey.toBase58(), "new recipient: ", newRecipient.publicKey.toBase58() ); console.log( "old recipient tokens:", recipientTokens.toBase58(), "new recipient tokens: ", newRecipientTokens.toBase58(), "new recipient tokens", escrowNewRecipient.recipient_tokens.toBase58() ); assert.ok(oldRecipient !== escrowNewRecipient); assert.ok( escrowNewRecipient.toBase58() === newRecipient.publicKey.toBase58() ); await provider.connection.getBalance(sender.publicKey); }, 7000); }); it("Cancels the stream", async () => { const oldBalance = await provider.connection.getBalance(sender.publicKey); setTimeout(async () => { console.log("\nCancel:\n"); const oldSenderAta = await program.provider.connection.getAccountInfo( senderTokens ); const oldSenderAmount = common.token.parseTokenAccountData( oldSenderAta.data ).amount; const oldEscrowAta = await program.provider.connection.getAccountInfo( escrowTokens ); const oldEscrowAmount = common.token.parseTokenAccountData( oldEscrowAta.data ).amount; const oldRecipientAta = await program.provider.connection.getAccountInfo( recipientTokens ); const oldRecipientAmount = common.token.parseTokenAccountData( oldRecipientAta.data ).amount; await program.rpc.cancel({ accounts: { cancelAuthority: sender.publicKey, sender: sender.publicKey, senderTokens, recipient: recipient.publicKey, recipientTokens, metadata: metadata.publicKey, escrowTokens, mint, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, }, signers: [sender.payer], }); const _metadata = await program.provider.connection.getAccountInfo( metadata.publicKey ); let strm_data = decode(_metadata.data); console.log("Stream Data:\n", strm_data); let newEscrowAmount = null; const newEscrowAta = await program.provider.connection.getAccountInfo( escrowTokens ); if (newEscrowAta) { newEscrowAmount = common.token.parseTokenAccountData( newEscrowAta.data ).amount; } const newRecipientAta = await program.provider.connection.getAccountInfo( recipientTokens ); const newRecipientAmount = common.token.parseTokenAccountData( newRecipientAta.data ).amount; const newSenderAta = await program.provider.connection.getAccountInfo( senderTokens ); const newSenderAmount = common.token.parseTokenAccountData( newSenderAta.data ).amount; const escrow = await program.provider.connection.getAccountInfo( metadata.publicKey ); console.log("cancel:"); console.log( "old sender", oldSenderAmount, "old recipient", oldRecipientAmount, "old escrow", oldEscrowAmount ); console.log( "new sender", newSenderAmount, "new recipient", newRecipientAmount, "new escrow:" ); const newBalance = await provider.connection.getBalance(sender.publicKey); console.log("Returned:", newBalance - oldBalance); assert.ok(newEscrowAmount === 0); assert.ok(decode(escrow.data).amount.eq(0)); assert.ok(newRecipientAmount.add(newSenderAmount).eq(depositedAmount)); }, 8000); }); });