const anchor = require("@project-serum/anchor"); const { TOKEN_PROGRAM_ID, Token } = require("@solana/spl-token"); const assert = require("assert"); describe("escrow", () => { const provider = anchor.Provider.env(); anchor.setProvider(provider); const program = anchor.workspace.Escrow; let mintA = null; let mintB = null; let initializerTokenAccountA = null; let initializerTokenAccountB = null; let takerTokenAccountA = null; let takerTokenAccountB = null; let pda = null; const takerAmount = 1000; const initializerAmount = 500; const escrowAccount = anchor.web3.Keypair.generate(); const payer = anchor.web3.Keypair.generate(); const mintAuthority = anchor.web3.Keypair.generate(); it("Initialise escrow state", async () => { // Airdropping tokens to a payer. await provider.connection.confirmTransaction( await provider.connection.requestAirdrop(payer.publicKey, 10000000000), "confirmed" ); mintA = await Token.createMint( provider.connection, payer, mintAuthority.publicKey, null, 0, TOKEN_PROGRAM_ID ); mintB = await Token.createMint( provider.connection, payer, mintAuthority.publicKey, null, 0, TOKEN_PROGRAM_ID ); initializerTokenAccountA = await mintA.createAccount(provider.wallet.publicKey); takerTokenAccountA = await mintA.createAccount(provider.wallet.publicKey); initializerTokenAccountB = await mintB.createAccount(provider.wallet.publicKey); takerTokenAccountB = await mintB.createAccount(provider.wallet.publicKey); await mintA.mintTo( initializerTokenAccountA, mintAuthority.publicKey, [mintAuthority], initializerAmount ); await mintB.mintTo( takerTokenAccountB, mintAuthority.publicKey, [mintAuthority], takerAmount ); let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA); let _takerTokenAccountB = await mintB.getAccountInfo(takerTokenAccountB); assert.ok(_initializerTokenAccountA.amount.toNumber() == initializerAmount); assert.ok(_takerTokenAccountB.amount.toNumber() == takerAmount); }); it("Initialize escrow", async () => { await program.rpc.initializeEscrow( new anchor.BN(initializerAmount), new anchor.BN(takerAmount), { accounts: { initializer: provider.wallet.publicKey, initializerDepositTokenAccount: initializerTokenAccountA, initializerReceiveTokenAccount: initializerTokenAccountB, escrowAccount: escrowAccount.publicKey, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, instructions: [ await program.account.escrowAccount.createInstruction(escrowAccount), ], signers: [escrowAccount], } ); // Get the PDA that is assigned authority to token account. const [_pda, _nonce] = await anchor.web3.PublicKey.findProgramAddress( [Buffer.from(anchor.utils.bytes.utf8.encode("escrow"))], program.programId ); pda = _pda; let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA); let _escrowAccount = await program.account.escrowAccount.fetch( escrowAccount.publicKey ); // Check that the new owner is the PDA. assert.ok(_initializerTokenAccountA.owner.equals(pda)); // Check that the values in the escrow account match what we expect. assert.ok(_escrowAccount.initializerKey.equals(provider.wallet.publicKey)); assert.ok(_escrowAccount.initializerAmount.toNumber() == initializerAmount); assert.ok(_escrowAccount.takerAmount.toNumber() == takerAmount); assert.ok( _escrowAccount.initializerDepositTokenAccount.equals(initializerTokenAccountA) ); assert.ok( _escrowAccount.initializerReceiveTokenAccount.equals(initializerTokenAccountB) ); }); it("Exchange escrow", async () => { await program.rpc.exchange({ accounts: { taker: provider.wallet.publicKey, takerDepositTokenAccount: takerTokenAccountB, takerReceiveTokenAccount: takerTokenAccountA, pdaDepositTokenAccount: initializerTokenAccountA, initializerReceiveTokenAccount: initializerTokenAccountB, initializerMainAccount: provider.wallet.publicKey, escrowAccount: escrowAccount.publicKey, pdaAccount: pda, tokenProgram: TOKEN_PROGRAM_ID, }, }); let _takerTokenAccountA = await mintA.getAccountInfo(takerTokenAccountA); let _takerTokenAccountB = await mintB.getAccountInfo(takerTokenAccountB); let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA); let _initializerTokenAccountB = await mintB.getAccountInfo(initializerTokenAccountB); // Check that the initializer gets back ownership of their token account. assert.ok(_takerTokenAccountA.owner.equals(provider.wallet.publicKey)); assert.ok(_takerTokenAccountA.amount.toNumber() == initializerAmount); assert.ok(_initializerTokenAccountA.amount.toNumber() == 0); assert.ok(_initializerTokenAccountB.amount.toNumber() == takerAmount); assert.ok(_takerTokenAccountB.amount.toNumber() == 0); }); let newEscrow = anchor.web3.Keypair.generate(); it("Initialize escrow and cancel escrow", async () => { // Put back tokens into initializer token A account. await mintA.mintTo( initializerTokenAccountA, mintAuthority.publicKey, [mintAuthority], initializerAmount ); await program.rpc.initializeEscrow( new anchor.BN(initializerAmount), new anchor.BN(takerAmount), { accounts: { initializer: provider.wallet.publicKey, initializerDepositTokenAccount: initializerTokenAccountA, initializerReceiveTokenAccount: initializerTokenAccountB, escrowAccount: newEscrow.publicKey, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, instructions: [await program.account.escrowAccount.createInstruction(newEscrow)], signers: [newEscrow], } ); let _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA); // Check that the new owner is the PDA. assert.ok(_initializerTokenAccountA.owner.equals(pda)); // Cancel the escrow. await program.rpc.cancelEscrow({ accounts: { initializer: provider.wallet.publicKey, pdaDepositTokenAccount: initializerTokenAccountA, pdaAccount: pda, escrowAccount: newEscrow.publicKey, tokenProgram: TOKEN_PROGRAM_ID, }, }); // Check the final owner should be the provider public key. _initializerTokenAccountA = await mintA.getAccountInfo(initializerTokenAccountA); assert.ok(_initializerTokenAccountA.owner.equals(provider.wallet.publicKey)); // Check all the funds are still there. assert.ok(_initializerTokenAccountA.amount.toNumber() == initializerAmount); }); });