|
@@ -1,32 +1,31 @@
|
|
-import { randomBytes } from 'node:crypto';
|
|
|
|
-import * as anchor from '@coral-xyz/anchor';
|
|
|
|
-import { BN, type Program } from '@coral-xyz/anchor';
|
|
|
|
|
|
+import { randomBytes } from "node:crypto";
|
|
|
|
+import * as anchor from "@coral-xyz/anchor";
|
|
|
|
+import { BN, Wallet, type Program } from "@coral-xyz/anchor";
|
|
import {
|
|
import {
|
|
- MINT_SIZE,
|
|
|
|
TOKEN_2022_PROGRAM_ID,
|
|
TOKEN_2022_PROGRAM_ID,
|
|
type TOKEN_PROGRAM_ID,
|
|
type TOKEN_PROGRAM_ID,
|
|
- createAssociatedTokenAccountIdempotentInstruction,
|
|
|
|
- createInitializeMint2Instruction,
|
|
|
|
- createMintToInstruction,
|
|
|
|
getAssociatedTokenAddressSync,
|
|
getAssociatedTokenAddressSync,
|
|
- getMinimumBalanceForRentExemptMint,
|
|
|
|
-} from '@solana/spl-token';
|
|
|
|
-import { LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, type TransactionInstruction } from '@solana/web3.js';
|
|
|
|
-import { assert } from 'chai';
|
|
|
|
-import type { Escrow } from '../target/types/escrow';
|
|
|
|
|
|
+} from "@solana/spl-token";
|
|
|
|
+import { LAMPORTS_PER_SOL, PublicKey, Keypair } from "@solana/web3.js";
|
|
|
|
+import { assert } from "chai";
|
|
|
|
+import type { Escrow } from "../target/types/escrow";
|
|
|
|
|
|
-import { confirmTransaction, makeKeypairs } from '@solana-developers/helpers';
|
|
|
|
|
|
+import {
|
|
|
|
+ confirmTransaction,
|
|
|
|
+ createAccountsMintsAndTokenAccounts,
|
|
|
|
+} from "@solana-developers/helpers";
|
|
|
|
|
|
-const TOKEN_PROGRAM: typeof TOKEN_2022_PROGRAM_ID | typeof TOKEN_PROGRAM_ID = TOKEN_2022_PROGRAM_ID;
|
|
|
|
|
|
+const TOKEN_PROGRAM: typeof TOKEN_2022_PROGRAM_ID | typeof TOKEN_PROGRAM_ID =
|
|
|
|
+ TOKEN_2022_PROGRAM_ID;
|
|
|
|
|
|
const getRandomBigNumber = (size = 8) => {
|
|
const getRandomBigNumber = (size = 8) => {
|
|
return new BN(randomBytes(size));
|
|
return new BN(randomBytes(size));
|
|
};
|
|
};
|
|
|
|
|
|
-describe('escrow', async () => {
|
|
|
|
- anchor.setProvider(anchor.AnchorProvider.env());
|
|
|
|
-
|
|
|
|
- const provider = anchor.getProvider();
|
|
|
|
|
|
+describe("escrow", async () => {
|
|
|
|
+ // Use the cluster and the keypair from Anchor.toml
|
|
|
|
+ const provider = anchor.AnchorProvider.env();
|
|
|
|
+ anchor.setProvider(provider);
|
|
|
|
|
|
const connection = provider.connection;
|
|
const connection = provider.connection;
|
|
|
|
|
|
@@ -37,68 +36,47 @@ describe('escrow', async () => {
|
|
tokenProgram: TOKEN_PROGRAM,
|
|
tokenProgram: TOKEN_PROGRAM,
|
|
};
|
|
};
|
|
|
|
|
|
- const [alice, bob, tokenMintA, tokenMintB] = makeKeypairs(4);
|
|
|
|
-
|
|
|
|
- before('Creates Alice and Bob accounts, 2 token mints, and associated token accounts for both tokens for both users', async () => {
|
|
|
|
- const [aliceTokenAccountA, aliceTokenAccountB, bobTokenAccountA, bobTokenAccountB] = [alice, bob].flatMap((keypair) =>
|
|
|
|
- [tokenMintA, tokenMintB].map((mint) => getAssociatedTokenAddressSync(mint.publicKey, keypair.publicKey, false, TOKEN_PROGRAM)),
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- // Airdrops to users, and creates two tokens mints 'A' and 'B'"
|
|
|
|
- const minimumLamports = await getMinimumBalanceForRentExemptMint(connection);
|
|
|
|
-
|
|
|
|
- const sendSolInstructions: Array<TransactionInstruction> = [alice, bob].map((account) =>
|
|
|
|
- SystemProgram.transfer({
|
|
|
|
- fromPubkey: provider.publicKey,
|
|
|
|
- toPubkey: account.publicKey,
|
|
|
|
- lamports: 10 * LAMPORTS_PER_SOL,
|
|
|
|
- }),
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- const createMintInstructions: Array<TransactionInstruction> = [tokenMintA, tokenMintB].map((mint) =>
|
|
|
|
- SystemProgram.createAccount({
|
|
|
|
- fromPubkey: provider.publicKey,
|
|
|
|
- newAccountPubkey: mint.publicKey,
|
|
|
|
- lamports: minimumLamports,
|
|
|
|
- space: MINT_SIZE,
|
|
|
|
- programId: TOKEN_PROGRAM,
|
|
|
|
- }),
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- // Make tokenA and tokenB mints, mint tokens and create ATAs
|
|
|
|
- const mintTokensInstructions: Array<TransactionInstruction> = [
|
|
|
|
- {
|
|
|
|
- mint: tokenMintA.publicKey,
|
|
|
|
- authority: alice.publicKey,
|
|
|
|
- ata: aliceTokenAccountA,
|
|
|
|
- },
|
|
|
|
- {
|
|
|
|
- mint: tokenMintB.publicKey,
|
|
|
|
- authority: bob.publicKey,
|
|
|
|
- ata: bobTokenAccountB,
|
|
|
|
- },
|
|
|
|
- ].flatMap((mintDetails) => [
|
|
|
|
- createInitializeMint2Instruction(mintDetails.mint, 6, mintDetails.authority, null, TOKEN_PROGRAM),
|
|
|
|
- createAssociatedTokenAccountIdempotentInstruction(provider.publicKey, mintDetails.ata, mintDetails.authority, mintDetails.mint, TOKEN_PROGRAM),
|
|
|
|
- createMintToInstruction(mintDetails.mint, mintDetails.ata, mintDetails.authority, 1_000_000_000, [], TOKEN_PROGRAM),
|
|
|
|
- ]);
|
|
|
|
-
|
|
|
|
- // Add all these instructions to our transaction
|
|
|
|
- const tx = new Transaction();
|
|
|
|
- tx.instructions = [...sendSolInstructions, ...createMintInstructions, ...mintTokensInstructions];
|
|
|
|
-
|
|
|
|
- const transactionSignature = await provider.sendAndConfirm(tx, [tokenMintA, tokenMintB, alice, bob]);
|
|
|
|
-
|
|
|
|
- // Save the accounts for later use
|
|
|
|
- accounts.maker = alice.publicKey;
|
|
|
|
- accounts.taker = bob.publicKey;
|
|
|
|
- accounts.tokenMintA = tokenMintA.publicKey;
|
|
|
|
- accounts.makerTokenAccountA = aliceTokenAccountA;
|
|
|
|
- accounts.takerTokenAccountA = bobTokenAccountA;
|
|
|
|
- accounts.tokenMintB = tokenMintB.publicKey;
|
|
|
|
- accounts.makerTokenAccountB = aliceTokenAccountB;
|
|
|
|
- accounts.takerTokenAccountB = bobTokenAccountB;
|
|
|
|
- });
|
|
|
|
|
|
+ let alice: Keypair;
|
|
|
|
+ let bob: Keypair;
|
|
|
|
+ let tokenMintA: Keypair;
|
|
|
|
+ let tokenMintB: Keypair;
|
|
|
|
+
|
|
|
|
+ before(
|
|
|
|
+ "Creates Alice and Bob accounts, 2 token mints, and associated token accounts for both tokens for both users",
|
|
|
|
+ async () => {
|
|
|
|
+ const connection = provider.connection;
|
|
|
|
+
|
|
|
|
+ const providerUser = (provider.wallet as Wallet).payer;
|
|
|
|
+
|
|
|
|
+ const SOL_BALANCE = 10 * LAMPORTS_PER_SOL;
|
|
|
|
+
|
|
|
|
+ const accountsMintsAndTokenAccounts =
|
|
|
|
+ await createAccountsMintsAndTokenAccounts(
|
|
|
|
+ [
|
|
|
|
+ [1_000_000_000, 0], // Alice has 1_000_000_000 of token A and 0 of token B
|
|
|
|
+ [0, 1_000_000_000], // Bob has 0 of token A and 1_000_000_000 of token B
|
|
|
|
+ ],
|
|
|
|
+ SOL_BALANCE,
|
|
|
|
+ connection,
|
|
|
|
+ providerUser
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ const tokenAccounts = accountsMintsAndTokenAccounts.tokenAccounts;
|
|
|
|
+
|
|
|
|
+ [alice, bob] = accountsMintsAndTokenAccounts.users;
|
|
|
|
+ [tokenMintA, tokenMintB] = accountsMintsAndTokenAccounts.mints;
|
|
|
|
+
|
|
|
|
+ // Save the accounts for later use
|
|
|
|
+ accounts.maker = alice.publicKey;
|
|
|
|
+ accounts.taker = bob.publicKey;
|
|
|
|
+ accounts.tokenMintA = tokenMintA.publicKey;
|
|
|
|
+ accounts.makerTokenAccountA = tokenAccounts[0][0];
|
|
|
|
+ accounts.takerTokenAccountA = tokenAccounts[1][0];
|
|
|
|
+ accounts.tokenMintB = tokenMintB.publicKey;
|
|
|
|
+ accounts.makerTokenAccountB = tokenAccounts[0][1];
|
|
|
|
+ accounts.takerTokenAccountB = tokenAccounts[1][1];
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
|
|
const tokenAOfferedAmount = new BN(1_000_000);
|
|
const tokenAOfferedAmount = new BN(1_000_000);
|
|
const tokenBWantedAmount = new BN(1_000_000);
|
|
const tokenBWantedAmount = new BN(1_000_000);
|
|
@@ -110,11 +88,20 @@ describe('escrow', async () => {
|
|
|
|
|
|
// Then determine the account addresses we'll use for the offer and the vault
|
|
// Then determine the account addresses we'll use for the offer and the vault
|
|
const offer = PublicKey.findProgramAddressSync(
|
|
const offer = PublicKey.findProgramAddressSync(
|
|
- [Buffer.from('offer'), accounts.maker.toBuffer(), offerId.toArrayLike(Buffer, 'le', 8)],
|
|
|
|
- program.programId,
|
|
|
|
|
|
+ [
|
|
|
|
+ Buffer.from("offer"),
|
|
|
|
+ accounts.maker.toBuffer(),
|
|
|
|
+ offerId.toArrayLike(Buffer, "le", 8),
|
|
|
|
+ ],
|
|
|
|
+ program.programId
|
|
)[0];
|
|
)[0];
|
|
|
|
|
|
- const vault = getAssociatedTokenAddressSync(accounts.tokenMintA, offer, true, TOKEN_PROGRAM);
|
|
|
|
|
|
+ const vault = getAssociatedTokenAddressSync(
|
|
|
|
+ accounts.tokenMintA,
|
|
|
|
+ offer,
|
|
|
|
+ true,
|
|
|
|
+ TOKEN_PROGRAM
|
|
|
|
+ );
|
|
|
|
|
|
accounts.offer = offer;
|
|
accounts.offer = offer;
|
|
accounts.vault = vault;
|
|
accounts.vault = vault;
|
|
@@ -153,18 +140,24 @@ describe('escrow', async () => {
|
|
|
|
|
|
// Check the offered tokens are now in Bob's account
|
|
// Check the offered tokens are now in Bob's account
|
|
// (note: there is no before balance as Bob didn't have any offered tokens before the transaction)
|
|
// (note: there is no before balance as Bob didn't have any offered tokens before the transaction)
|
|
- const bobTokenAccountBalanceAfterResponse = await connection.getTokenAccountBalance(accounts.takerTokenAccountA);
|
|
|
|
- const bobTokenAccountBalanceAfter = new BN(bobTokenAccountBalanceAfterResponse.value.amount);
|
|
|
|
|
|
+ const bobTokenAccountBalanceAfterResponse =
|
|
|
|
+ await connection.getTokenAccountBalance(accounts.takerTokenAccountA);
|
|
|
|
+ const bobTokenAccountBalanceAfter = new BN(
|
|
|
|
+ bobTokenAccountBalanceAfterResponse.value.amount
|
|
|
|
+ );
|
|
assert(bobTokenAccountBalanceAfter.eq(tokenAOfferedAmount));
|
|
assert(bobTokenAccountBalanceAfter.eq(tokenAOfferedAmount));
|
|
|
|
|
|
// Check the wanted tokens are now in Alice's account
|
|
// Check the wanted tokens are now in Alice's account
|
|
// (note: there is no before balance as Alice didn't have any wanted tokens before the transaction)
|
|
// (note: there is no before balance as Alice didn't have any wanted tokens before the transaction)
|
|
- const aliceTokenAccountBalanceAfterResponse = await connection.getTokenAccountBalance(accounts.makerTokenAccountB);
|
|
|
|
- const aliceTokenAccountBalanceAfter = new BN(aliceTokenAccountBalanceAfterResponse.value.amount);
|
|
|
|
|
|
+ const aliceTokenAccountBalanceAfterResponse =
|
|
|
|
+ await connection.getTokenAccountBalance(accounts.makerTokenAccountB);
|
|
|
|
+ const aliceTokenAccountBalanceAfter = new BN(
|
|
|
|
+ aliceTokenAccountBalanceAfterResponse.value.amount
|
|
|
|
+ );
|
|
assert(aliceTokenAccountBalanceAfter.eq(tokenBWantedAmount));
|
|
assert(aliceTokenAccountBalanceAfter.eq(tokenBWantedAmount));
|
|
};
|
|
};
|
|
|
|
|
|
- it('Puts the tokens Alice offers into the vault when Alice makes an offer', async () => {
|
|
|
|
|
|
+ it("Puts the tokens Alice offers into the vault when Alice makes an offer", async () => {
|
|
await make();
|
|
await make();
|
|
});
|
|
});
|
|
|
|
|