|
@@ -1,169 +1,208 @@
|
|
|
-import { randomBytes } from 'node:crypto';
|
|
|
-import * as anchor from '@coral-xyz/anchor';
|
|
|
-import { BN, type Program } from '@coral-xyz/anchor';
|
|
|
-import { TOKEN_2022_PROGRAM_ID, type TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from '@solana/spl-token';
|
|
|
-import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
|
|
|
-import { assert } from 'chai';
|
|
|
-import type { Escrow } from '../target/types/escrow';
|
|
|
+import { assert } from 'node:console';
|
|
|
+import { describe, test } from 'node:test';
|
|
|
+import { IdlV01, rootNodeFromAnchor } from '@kinobi-so/nodes-from-anchor';
|
|
|
+import { renderJavaScriptVisitor } from '@kinobi-so/renderers';
|
|
|
+import { createSolanaRpc } from '@solana/web3.js';
|
|
|
+// From https://github.com/kinobi-so/kinobi?tab=readme-ov-file#from-program-to-kinobi
|
|
|
+import { createFromRoot } from 'kinobi';
|
|
|
+import anchorIdl from '../target/idl/escrow.json';
|
|
|
|
|
|
-import { confirmTransaction, createAccountsMintsAndTokenAccounts, makeKeypairs } from '@solana-developers/helpers';
|
|
|
+const rootNode = rootNodeFromAnchor(anchorIdl as IdlV01);
|
|
|
|
|
|
-// Work on both Token Program and new Token Extensions Program
|
|
|
-const TOKEN_PROGRAM: typeof TOKEN_2022_PROGRAM_ID | typeof TOKEN_PROGRAM_ID = TOKEN_2022_PROGRAM_ID;
|
|
|
+const kinobi = createFromRoot(rootNode);
|
|
|
|
|
|
-const SECONDS = 1000;
|
|
|
+const log = console.log;
|
|
|
|
|
|
-// Tests must complete within half this time otherwise
|
|
|
-// they are marked as slow. Since Anchor involves a little
|
|
|
-// network IO, these tests usually take about 15 seconds.
|
|
|
-const ANCHOR_SLOW_TEST_THRESHOLD = 40 * SECONDS;
|
|
|
+log(kinobi);
|
|
|
|
|
|
-const getRandomBigNumber = (size = 8) => {
|
|
|
- return new BN(randomBytes(size));
|
|
|
+// that the first argument of the renderJavaScriptVisitor function should be a folder not a file.
|
|
|
+const visitor = renderJavaScriptVisitor('generated-client', {
|
|
|
+ formatCode: true,
|
|
|
+});
|
|
|
+
|
|
|
+kinobi.accept(visitor);
|
|
|
+
|
|
|
+// import { TOKEN_2022_PROGRAM_ID, type TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from '@solana/spl-token';
|
|
|
+// import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
|
|
|
+
|
|
|
+// import type { Escrow } from '../target/types/escrow';
|
|
|
+
|
|
|
+const CLUSTERS = {
|
|
|
+ devnet: 'https://api.devnet.solana.com',
|
|
|
+ testnet: 'https://api.testnet.solana.com',
|
|
|
+ mainnet: 'https://api.mainnet-beta.solana.com',
|
|
|
+ localnet: 'http://127.0.0.1:8899',
|
|
|
+};
|
|
|
+
|
|
|
+const CLUSTER_NAMES = Object.keys(CLUSTERS);
|
|
|
+
|
|
|
+const connect = (clusterNameOrURL: string) => {
|
|
|
+ if (CLUSTER_NAMES.includes(clusterNameOrURL)) {
|
|
|
+ return createSolanaRpc(clusterNameOrURL);
|
|
|
+ }
|
|
|
+ return createSolanaRpc(clusterNameOrURL);
|
|
|
};
|
|
|
|
|
|
+const rpc = connect('localnet');
|
|
|
+
|
|
|
+// import * as Escrow from 'escrow-generated.ts'
|
|
|
+
|
|
|
+// // import { confirmTransaction, createAccountsMintsAndTokenAccounts, makeKeypairs } from '@solana-developers/helpers';
|
|
|
+
|
|
|
+// // Work on both Token Program and new Token Extensions Program
|
|
|
+// // const TOKEN_PROGRAM: typeof TOKEN_2022_PROGRAM_ID | typeof TOKEN_PROGRAM_ID = TOKEN_2022_PROGRAM_ID;
|
|
|
+
|
|
|
+// const SECONDS = 1000;
|
|
|
+
|
|
|
+// // Tests must complete within half this time otherwise
|
|
|
+// // they are marked as slow. Since Anchor involves a little
|
|
|
+// // network IO, these tests usually take about 15 seconds.
|
|
|
+// const ANCHOR_SLOW_TEST_THRESHOLD = 40 * SECONDS;
|
|
|
+
|
|
|
+// function getRandomBigInt(max = 256) {
|
|
|
+// return BigInt(Math.floor(Math.random() * max));
|
|
|
+// }
|
|
|
+
|
|
|
describe('escrow', async () => {
|
|
|
- // Use the cluster and the keypair from Anchor.toml
|
|
|
- const provider = anchor.AnchorProvider.env();
|
|
|
- anchor.setProvider(provider);
|
|
|
-
|
|
|
- // See https://github.com/coral-xyz/anchor/issues/3122
|
|
|
- const user = (provider.wallet as anchor.Wallet).payer;
|
|
|
- const payer = user;
|
|
|
-
|
|
|
- const connection = provider.connection;
|
|
|
-
|
|
|
- const program = anchor.workspace.Escrow as Program<Escrow>;
|
|
|
-
|
|
|
- // We're going to reuse these accounts across multiple tests
|
|
|
- const accounts: Record<string, PublicKey> = {
|
|
|
- tokenProgram: TOKEN_PROGRAM,
|
|
|
- };
|
|
|
-
|
|
|
- let alice: anchor.web3.Keypair;
|
|
|
- let bob: anchor.web3.Keypair;
|
|
|
- let tokenMintA: anchor.web3.Keypair;
|
|
|
- let tokenMintB: anchor.web3.Keypair;
|
|
|
-
|
|
|
- [alice, bob, tokenMintA, tokenMintB] = makeKeypairs(4);
|
|
|
-
|
|
|
- const tokenAOfferedAmount = new BN(1_000_000);
|
|
|
- const tokenBWantedAmount = new BN(1_000_000);
|
|
|
-
|
|
|
- before('Creates Alice and Bob accounts, 2 token mints, and associated token accounts for both tokens for both users', async () => {
|
|
|
- const usersMintsAndTokenAccounts = await createAccountsMintsAndTokenAccounts(
|
|
|
- [
|
|
|
- // Alice's token balances
|
|
|
- [
|
|
|
- // 1_000_000_000 of token A
|
|
|
- 1_000_000_000,
|
|
|
- // 0 of token B
|
|
|
- 0,
|
|
|
- ],
|
|
|
- // Bob's token balances
|
|
|
- [
|
|
|
- // 0 of token A
|
|
|
- 0,
|
|
|
- // 1_000_000_000 of token B
|
|
|
- 1_000_000_000,
|
|
|
- ],
|
|
|
- ],
|
|
|
- 1 * LAMPORTS_PER_SOL,
|
|
|
- connection,
|
|
|
- payer,
|
|
|
- );
|
|
|
-
|
|
|
- // Alice will be the maker (creator) of the offer
|
|
|
- // Bob will be the taker (acceptor) of the offer
|
|
|
- const users = usersMintsAndTokenAccounts.users;
|
|
|
- alice = users[0];
|
|
|
- bob = users[1];
|
|
|
-
|
|
|
- // tokenMintA represents the token Alice is offering
|
|
|
- // tokenMintB represents the token Alice wants in return
|
|
|
- const mints = usersMintsAndTokenAccounts.mints;
|
|
|
- tokenMintA = mints[0];
|
|
|
- tokenMintB = mints[1];
|
|
|
-
|
|
|
- const tokenAccounts = usersMintsAndTokenAccounts.tokenAccounts;
|
|
|
-
|
|
|
- // aliceTokenAccountA is Alice's account for tokenA (the token she's offering)
|
|
|
- // aliceTokenAccountB is Alice's account for tokenB (the token she wants)
|
|
|
- const aliceTokenAccountA = tokenAccounts[0][0];
|
|
|
- const aliceTokenAccountB = tokenAccounts[0][1];
|
|
|
-
|
|
|
- // bobTokenAccountA is Bob's account for tokenA (the token Alice is offering)
|
|
|
- // bobTokenAccountB is Bob's account for tokenB (the token Alice wants)
|
|
|
- const bobTokenAccountA = tokenAccounts[1][0];
|
|
|
- const bobTokenAccountB = tokenAccounts[1][1];
|
|
|
-
|
|
|
- // 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;
|
|
|
+ test('escrow', async () => {
|
|
|
+ assert(true);
|
|
|
});
|
|
|
-
|
|
|
- it('Puts the tokens Alice offers into the vault when Alice makes an offer', async () => {
|
|
|
- // Pick a random ID for the offer we'll make
|
|
|
- const offerId = getRandomBigNumber();
|
|
|
-
|
|
|
- // Then determine the account addresses we'll use for the offer and the vault
|
|
|
- const offer = PublicKey.findProgramAddressSync(
|
|
|
- [Buffer.from('offer'), accounts.maker.toBuffer(), offerId.toArrayLike(Buffer, 'le', 8)],
|
|
|
- program.programId,
|
|
|
- )[0];
|
|
|
-
|
|
|
- const vault = getAssociatedTokenAddressSync(accounts.tokenMintA, offer, true, TOKEN_PROGRAM);
|
|
|
-
|
|
|
- accounts.offer = offer;
|
|
|
- accounts.vault = vault;
|
|
|
-
|
|
|
- const transactionSignature = await program.methods
|
|
|
- .makeOffer(offerId, tokenAOfferedAmount, tokenBWantedAmount)
|
|
|
- .accounts({ ...accounts })
|
|
|
- .signers([alice])
|
|
|
- .rpc();
|
|
|
-
|
|
|
- await confirmTransaction(connection, transactionSignature);
|
|
|
-
|
|
|
- // Check our vault contains the tokens offered
|
|
|
- const vaultBalanceResponse = await connection.getTokenAccountBalance(vault);
|
|
|
- const vaultBalance = new BN(vaultBalanceResponse.value.amount);
|
|
|
- assert(vaultBalance.eq(tokenAOfferedAmount));
|
|
|
-
|
|
|
- // Check our Offer account contains the correct data
|
|
|
- const offerAccount = await program.account.offer.fetch(offer);
|
|
|
-
|
|
|
- assert(offerAccount.maker.equals(alice.publicKey));
|
|
|
- assert(offerAccount.tokenMintA.equals(accounts.tokenMintA));
|
|
|
- assert(offerAccount.tokenMintB.equals(accounts.tokenMintB));
|
|
|
- assert(offerAccount.tokenBWantedAmount.eq(tokenBWantedAmount));
|
|
|
- }).slow(ANCHOR_SLOW_TEST_THRESHOLD);
|
|
|
-
|
|
|
- it("Puts the tokens from the vault into Bob's account, and gives Alice Bob's tokens, when Bob takes an offer", async () => {
|
|
|
- const transactionSignature = await program.methods
|
|
|
- .takeOffer()
|
|
|
- .accounts({ ...accounts })
|
|
|
- .signers([bob])
|
|
|
- .rpc();
|
|
|
-
|
|
|
- await confirmTransaction(connection, transactionSignature);
|
|
|
-
|
|
|
- // 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)
|
|
|
- const bobTokenAccountBalanceAfterResponse = await connection.getTokenAccountBalance(accounts.takerTokenAccountA);
|
|
|
- const bobTokenAccountBalanceAfter = new BN(bobTokenAccountBalanceAfterResponse.value.amount);
|
|
|
- assert(bobTokenAccountBalanceAfter.eq(tokenAOfferedAmount));
|
|
|
-
|
|
|
- // 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)
|
|
|
- const aliceTokenAccountBalanceAfterResponse = await connection.getTokenAccountBalance(accounts.makerTokenAccountB);
|
|
|
- const aliceTokenAccountBalanceAfter = new BN(aliceTokenAccountBalanceAfterResponse.value.amount);
|
|
|
- assert(aliceTokenAccountBalanceAfter.eq(tokenBWantedAmount));
|
|
|
- }).slow(ANCHOR_SLOW_TEST_THRESHOLD);
|
|
|
+ // // See https://github.com/coral-xyz/anchor/issues/3122
|
|
|
+ // const payer = user;
|
|
|
+
|
|
|
+ // const connection = provider.connection;
|
|
|
+
|
|
|
+ // const program = anchor.workspace.Escrow as Program<Escrow>;
|
|
|
+
|
|
|
+ // // We're going to reuse these accounts across multiple tests
|
|
|
+ // const accounts: Record<string, PublicKey> = {
|
|
|
+ // tokenProgram: TOKEN_PROGRAM,
|
|
|
+ // };
|
|
|
+
|
|
|
+ // let alice: anchor.web3.Keypair;
|
|
|
+ // let bob: anchor.web3.Keypair;
|
|
|
+ // let tokenMintA: anchor.web3.Keypair;
|
|
|
+ // let tokenMintB: anchor.web3.Keypair;
|
|
|
+
|
|
|
+ // [alice, bob, tokenMintA, tokenMintB] = makeKeypairs(4);
|
|
|
+
|
|
|
+ // const tokenAOfferedAmount = new BN(1_000_000);
|
|
|
+ // const tokenBWantedAmount = new BN(1_000_000);
|
|
|
+
|
|
|
+ // before('Creates Alice and Bob accounts, 2 token mints, and associated token accounts for both tokens for both users', async () => {
|
|
|
+ // const usersMintsAndTokenAccounts = await createAccountsMintsAndTokenAccounts(
|
|
|
+ // [
|
|
|
+ // // Alice's token balances
|
|
|
+ // [
|
|
|
+ // // 1_000_000_000 of token A
|
|
|
+ // 1_000_000_000,
|
|
|
+ // // 0 of token B
|
|
|
+ // 0,
|
|
|
+ // ],
|
|
|
+ // // Bob's token balances
|
|
|
+ // [
|
|
|
+ // // 0 of token A
|
|
|
+ // 0,
|
|
|
+ // // 1_000_000_000 of token B
|
|
|
+ // 1_000_000_000,
|
|
|
+ // ],
|
|
|
+ // ],
|
|
|
+ // 1 * LAMPORTS_PER_SOL,
|
|
|
+ // connection,
|
|
|
+ // payer,
|
|
|
+ // );
|
|
|
+
|
|
|
+ // // Alice will be the maker (creator) of the offer
|
|
|
+ // // Bob will be the taker (acceptor) of the offer
|
|
|
+ // const users = usersMintsAndTokenAccounts.users;
|
|
|
+ // alice = users[0];
|
|
|
+ // bob = users[1];
|
|
|
+
|
|
|
+ // // tokenMintA represents the token Alice is offering
|
|
|
+ // // tokenMintB represents the token Alice wants in return
|
|
|
+ // const mints = usersMintsAndTokenAccounts.mints;
|
|
|
+ // tokenMintA = mints[0];
|
|
|
+ // tokenMintB = mints[1];
|
|
|
+
|
|
|
+ // const tokenAccounts = usersMintsAndTokenAccounts.tokenAccounts;
|
|
|
+
|
|
|
+ // // aliceTokenAccountA is Alice's account for tokenA (the token she's offering)
|
|
|
+ // // aliceTokenAccountB is Alice's account for tokenB (the token she wants)
|
|
|
+ // const aliceTokenAccountA = tokenAccounts[0][0];
|
|
|
+ // const aliceTokenAccountB = tokenAccounts[0][1];
|
|
|
+
|
|
|
+ // // bobTokenAccountA is Bob's account for tokenA (the token Alice is offering)
|
|
|
+ // // bobTokenAccountB is Bob's account for tokenB (the token Alice wants)
|
|
|
+ // const bobTokenAccountA = tokenAccounts[1][0];
|
|
|
+ // const bobTokenAccountB = tokenAccounts[1][1];
|
|
|
+
|
|
|
+ // // 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;
|
|
|
+ // });
|
|
|
+
|
|
|
+ // it('Puts the tokens Alice offers into the vault when Alice makes an offer', async () => {
|
|
|
+ // // Pick a random ID for the offer we'll make
|
|
|
+ // const offerId = getRandomBigNumber();
|
|
|
+
|
|
|
+ // // Then determine the account addresses we'll use for the offer and the vault
|
|
|
+ // const offer = PublicKey.findProgramAddressSync(
|
|
|
+ // [Buffer.from('offer'), accounts.maker.toBuffer(), offerId.toArrayLike(Buffer, 'le', 8)],
|
|
|
+ // program.programId,
|
|
|
+ // )[0];
|
|
|
+
|
|
|
+ // const vault = getAssociatedTokenAddressSync(accounts.tokenMintA, offer, true, TOKEN_PROGRAM);
|
|
|
+
|
|
|
+ // accounts.offer = offer;
|
|
|
+ // accounts.vault = vault;
|
|
|
+
|
|
|
+ // const transactionSignature = await program.methods
|
|
|
+ // .makeOffer(offerId, tokenAOfferedAmount, tokenBWantedAmount)
|
|
|
+ // .accounts({ ...accounts })
|
|
|
+ // .signers([alice])
|
|
|
+ // .rpc();
|
|
|
+
|
|
|
+ // await confirmTransaction(connection, transactionSignature);
|
|
|
+
|
|
|
+ // // Check our vault contains the tokens offered
|
|
|
+ // const vaultBalanceResponse = await connection.getTokenAccountBalance(vault);
|
|
|
+ // const vaultBalance = new BN(vaultBalanceResponse.value.amount);
|
|
|
+ // assert(vaultBalance.eq(tokenAOfferedAmount));
|
|
|
+
|
|
|
+ // // Check our Offer account contains the correct data
|
|
|
+ // const offerAccount = await program.account.offer.fetch(offer);
|
|
|
+
|
|
|
+ // assert(offerAccount.maker.equals(alice.publicKey));
|
|
|
+ // assert(offerAccount.tokenMintA.equals(accounts.tokenMintA));
|
|
|
+ // assert(offerAccount.tokenMintB.equals(accounts.tokenMintB));
|
|
|
+ // assert(offerAccount.tokenBWantedAmount.eq(tokenBWantedAmount));
|
|
|
+ // }).slow(ANCHOR_SLOW_TEST_THRESHOLD);
|
|
|
+
|
|
|
+ // it("Puts the tokens from the vault into Bob's account, and gives Alice Bob's tokens, when Bob takes an offer", async () => {
|
|
|
+ // const transactionSignature = await program.methods
|
|
|
+ // .takeOffer()
|
|
|
+ // .accounts({ ...accounts })
|
|
|
+ // .signers([bob])
|
|
|
+ // .rpc();
|
|
|
+
|
|
|
+ // await confirmTransaction(connection, transactionSignature);
|
|
|
+
|
|
|
+ // // 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)
|
|
|
+ // const bobTokenAccountBalanceAfterResponse = await connection.getTokenAccountBalance(accounts.takerTokenAccountA);
|
|
|
+ // const bobTokenAccountBalanceAfter = new BN(bobTokenAccountBalanceAfterResponse.value.amount);
|
|
|
+ // assert(bobTokenAccountBalanceAfter.eq(tokenAOfferedAmount));
|
|
|
+
|
|
|
+ // // 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)
|
|
|
+ // const aliceTokenAccountBalanceAfterResponse = await connection.getTokenAccountBalance(accounts.makerTokenAccountB);
|
|
|
+ // const aliceTokenAccountBalanceAfter = new BN(aliceTokenAccountBalanceAfterResponse.value.amount);
|
|
|
+ // assert(aliceTokenAccountBalanceAfter.eq(tokenBWantedAmount));
|
|
|
+ // }).slow(ANCHOR_SLOW_TEST_THRESHOLD);
|
|
|
});
|