From b19249a4755dfe1be0e5fd30a9f0b99c7c2e2e93 Mon Sep 17 00:00:00 2001 From: Jon C Date: Wed, 8 Nov 2023 23:08:35 +0100 Subject: [PATCH] Move tests into test file, maybe that'll do it? --- token-swap/js/test/main.test.ts | 853 +++++++++++++++++++++++++- token-swap/js/test/token-swap-test.ts | 724 ---------------------- 2 files changed, 844 insertions(+), 733 deletions(-) delete mode 100644 token-swap/js/test/token-swap-test.ts diff --git a/token-swap/js/test/main.test.ts b/token-swap/js/test/main.test.ts index 91927f78fea..d1c683bc247 100644 --- a/token-swap/js/test/main.test.ts +++ b/token-swap/js/test/main.test.ts @@ -1,13 +1,28 @@ import { - createAccountAndSwapAtomic, - createTokenSwap, - swap, - depositAllTokenTypes, - withdrawAllTokenTypes, - depositSingleTokenTypeExactAmountIn, - withdrawSingleTokenTypeExactAmountOut, -} from './token-swap-test'; -import {CurveType} from '../src'; + Keypair, + Connection, + PublicKey, + SystemProgram, + Transaction, + sendAndConfirmTransaction, +} from '@solana/web3.js'; +import { + approve, + createMint, + createAccount, + createApproveInstruction, + createInitializeAccountInstruction, + getAccount, + getMint, + getMinimumBalanceForRentExemptAccount, + mintTo, + AccountLayout, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token'; + +import {TokenSwap, CurveType, TOKEN_SWAP_PROGRAM_ID} from '../src'; +import {newAccountWithLamports} from '../src/util/new-account-with-lamports'; +import {sleep} from '../src/util/sleep'; describe('spl-token-swap instructions', () => { it('executes properly', async () => { @@ -35,3 +50,823 @@ describe('spl-token-swap instructions', () => { console.log('Success\n'); }); }); + +// The following globals are created by `createTokenSwap` and used by subsequent tests +// Token swap +let tokenSwap: TokenSwap; +// authority of the token and accounts +let authority: PublicKey; +// bump seed used to generate the authority public key +let bumpSeed: number; +// owner of the user accounts +let owner: Keypair; +// payer for transactions +let payer: Keypair; +// Token pool +let tokenPool: PublicKey; +let tokenAccountPool: PublicKey; +let feeAccount: PublicKey; +// Tokens swapped +let mintA: PublicKey; +const mintAProgramId: PublicKey = TOKEN_PROGRAM_ID; +let mintB: PublicKey; +const mintBProgramId: PublicKey = TOKEN_PROGRAM_ID; +let tokenAccountA: PublicKey; +let tokenAccountB: PublicKey; + +// Hard-coded fee address, for testing production mode +const SWAP_PROGRAM_OWNER_FEE_ADDRESS = + process.env.SWAP_PROGRAM_OWNER_FEE_ADDRESS; + +// Pool fees +const TRADING_FEE_NUMERATOR = 25n; +const TRADING_FEE_DENOMINATOR = 10000n; +const OWNER_TRADING_FEE_NUMERATOR = 5n; +const OWNER_TRADING_FEE_DENOMINATOR = 10000n; +const OWNER_WITHDRAW_FEE_NUMERATOR = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 0n : 1n; +const OWNER_WITHDRAW_FEE_DENOMINATOR = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 0n : 6n; +const HOST_FEE_NUMERATOR = 20n; +const HOST_FEE_DENOMINATOR = 100n; + +// Initial amount in each swap token +let currentSwapTokenA = 1000000n; +let currentSwapTokenB = 1000000n; +let currentFeeAmount = 0n; + +// Swap instruction constants +// Because there is no withdraw fee in the production version, these numbers +// need to get slightly tweaked in the two cases. +const SWAP_AMOUNT_IN = 100000n; +const SWAP_AMOUNT_OUT = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 90661n : 90674n; +const SWAP_FEE = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 22727n : 22730n; +const HOST_SWAP_FEE = SWAP_PROGRAM_OWNER_FEE_ADDRESS + ? (SWAP_FEE * HOST_FEE_NUMERATOR) / HOST_FEE_DENOMINATOR + : 0n; +const OWNER_SWAP_FEE = SWAP_FEE - HOST_SWAP_FEE; + +// Pool token amount minted on init +const DEFAULT_POOL_TOKEN_AMOUNT = 1000000000n; +// Pool token amount to withdraw / deposit +const POOL_TOKEN_AMOUNT = 10000000n; + +function assert(condition: boolean, message?: string) { + if (!condition) { + console.log(Error().stack + ':token-test.js'); + throw message || 'Assertion failed'; + } +} + +let connection: Connection; +async function getConnection(): Promise { + if (connection) return connection; + + const url = 'http://127.0.0.1:8899'; + connection = new Connection(url, 'recent'); + const version = await connection.getVersion(); + + console.log('Connection to cluster established:', url, version); + return connection; +} + +export async function createTokenSwap( + curveType: number, + curveParameters?: Uint8Array, +): Promise { + const connection = await getConnection(); + payer = await newAccountWithLamports(connection, 1000000000); + owner = await newAccountWithLamports(connection, 1000000000); + const tokenSwapAccount = Keypair.generate(); + + [authority, bumpSeed] = await PublicKey.findProgramAddress( + [tokenSwapAccount.publicKey.toBuffer()], + TOKEN_SWAP_PROGRAM_ID, + ); + + console.log('creating pool mint'); + tokenPool = await createMint( + connection, + payer, + authority, + null, + 2, + Keypair.generate(), + undefined, + TOKEN_PROGRAM_ID, + ); + + console.log('creating pool account'); + tokenAccountPool = await createAccount( + connection, + payer, + tokenPool, + owner.publicKey, + Keypair.generate(), + ); + const ownerKey = SWAP_PROGRAM_OWNER_FEE_ADDRESS || owner.publicKey.toString(); + feeAccount = await createAccount( + connection, + payer, + tokenPool, + new PublicKey(ownerKey), + Keypair.generate(), + ); + + console.log('creating token A'); + mintA = await createMint( + connection, + payer, + owner.publicKey, + null, + 2, + Keypair.generate(), + undefined, + mintAProgramId, + ); + + console.log('creating token A account'); + tokenAccountA = await createAccount( + connection, + payer, + mintA, + authority, + Keypair.generate(), + ); + console.log('minting token A to swap'); + await mintTo( + connection, + payer, + mintA, + tokenAccountA, + owner, + currentSwapTokenA, + ); + + console.log('creating token B'); + mintB = await createMint( + connection, + payer, + owner.publicKey, + null, + 2, + Keypair.generate(), + undefined, + mintBProgramId, + ); + + console.log('creating token B account'); + tokenAccountB = await createAccount( + connection, + payer, + mintB, + authority, + Keypair.generate(), + ); + console.log('minting token B to swap'); + await mintTo( + connection, + payer, + mintB, + tokenAccountB, + owner, + currentSwapTokenB, + ); + + console.log('creating token swap'); + const swapPayer = await newAccountWithLamports(connection, 10000000000); + tokenSwap = await TokenSwap.createTokenSwap( + connection, + swapPayer, + tokenSwapAccount, + authority, + tokenAccountA, + tokenAccountB, + tokenPool, + mintA, + mintB, + feeAccount, + tokenAccountPool, + TOKEN_SWAP_PROGRAM_ID, + TOKEN_PROGRAM_ID, + TRADING_FEE_NUMERATOR, + TRADING_FEE_DENOMINATOR, + OWNER_TRADING_FEE_NUMERATOR, + OWNER_TRADING_FEE_DENOMINATOR, + OWNER_WITHDRAW_FEE_NUMERATOR, + OWNER_WITHDRAW_FEE_DENOMINATOR, + HOST_FEE_NUMERATOR, + HOST_FEE_DENOMINATOR, + curveType, + curveParameters, + ); + + console.log('loading token swap'); + const fetchedTokenSwap = await TokenSwap.loadTokenSwap( + connection, + tokenSwapAccount.publicKey, + TOKEN_SWAP_PROGRAM_ID, + swapPayer, + ); + + assert(fetchedTokenSwap.poolTokenProgramId.equals(TOKEN_PROGRAM_ID)); + assert(fetchedTokenSwap.tokenAccountA.equals(tokenAccountA)); + assert(fetchedTokenSwap.tokenAccountB.equals(tokenAccountB)); + assert(fetchedTokenSwap.mintA.equals(mintA)); + assert(fetchedTokenSwap.mintB.equals(mintB)); + assert(fetchedTokenSwap.poolToken.equals(tokenPool)); + assert(fetchedTokenSwap.feeAccount.equals(feeAccount)); + assert(TRADING_FEE_NUMERATOR == fetchedTokenSwap.tradeFeeNumerator); + assert(TRADING_FEE_DENOMINATOR == fetchedTokenSwap.tradeFeeDenominator); + assert( + OWNER_TRADING_FEE_NUMERATOR == fetchedTokenSwap.ownerTradeFeeNumerator, + ); + assert( + OWNER_TRADING_FEE_DENOMINATOR == fetchedTokenSwap.ownerTradeFeeDenominator, + ); + assert( + OWNER_WITHDRAW_FEE_NUMERATOR == fetchedTokenSwap.ownerWithdrawFeeNumerator, + ); + assert( + OWNER_WITHDRAW_FEE_DENOMINATOR == + fetchedTokenSwap.ownerWithdrawFeeDenominator, + ); + assert(HOST_FEE_NUMERATOR == fetchedTokenSwap.hostFeeNumerator); + assert(HOST_FEE_DENOMINATOR == fetchedTokenSwap.hostFeeDenominator); + assert(curveType == fetchedTokenSwap.curveType); +} + +export async function depositAllTokenTypes(): Promise { + const poolMintInfo = await getMint(connection, tokenPool); + const supply = poolMintInfo.supply; + const swapTokenA = await getAccount(connection, tokenAccountA); + const tokenA = (swapTokenA.amount * BigInt(POOL_TOKEN_AMOUNT)) / supply; + const swapTokenB = await getAccount(connection, tokenAccountB); + const tokenB = (swapTokenB.amount * BigInt(POOL_TOKEN_AMOUNT)) / supply; + + const userTransferAuthority = Keypair.generate(); + console.log('Creating depositor token a account'); + const userAccountA = await createAccount( + connection, + payer, + mintA, + owner.publicKey, + Keypair.generate(), + ); + await mintTo(connection, payer, mintA, userAccountA, owner, tokenA); + await approve( + connection, + payer, + userAccountA, + userTransferAuthority.publicKey, + owner, + tokenA, + ); + console.log('Creating depositor token b account'); + const userAccountB = await createAccount( + connection, + payer, + mintB, + owner.publicKey, + Keypair.generate(), + ); + await mintTo(connection, payer, mintB, userAccountB, owner, tokenB); + await approve( + connection, + payer, + userAccountB, + userTransferAuthority.publicKey, + owner, + tokenB, + ); + console.log('Creating depositor pool token account'); + const newAccountPool = await createAccount( + connection, + payer, + tokenPool, + owner.publicKey, + Keypair.generate(), + ); + + const confirmOptions = { + skipPreflight: true, + }; + + console.log('Depositing into swap'); + await tokenSwap.depositAllTokenTypes( + userAccountA, + userAccountB, + newAccountPool, + TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + userTransferAuthority, + POOL_TOKEN_AMOUNT, + tokenA, + tokenB, + confirmOptions, + ); + + let info; + info = await getAccount(connection, userAccountA); + assert(info.amount == 0n); + info = await getAccount(connection, userAccountB); + assert(info.amount == 0n); + info = await getAccount(connection, tokenAccountA); + assert(info.amount == currentSwapTokenA + tokenA); + currentSwapTokenA += tokenA; + info = await getAccount(connection, tokenAccountB); + assert(info.amount == currentSwapTokenB + tokenB); + currentSwapTokenB += tokenB; + info = await getAccount(connection, newAccountPool); + assert(info.amount == POOL_TOKEN_AMOUNT); +} + +export async function withdrawAllTokenTypes(): Promise { + const poolMintInfo = await getMint(connection, tokenPool); + const supply = poolMintInfo.supply; + let swapTokenA = await getAccount(connection, tokenAccountA); + let swapTokenB = await getAccount(connection, tokenAccountB); + let feeAmount = 0n; + if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) { + feeAmount = + (POOL_TOKEN_AMOUNT * OWNER_WITHDRAW_FEE_NUMERATOR) / + OWNER_WITHDRAW_FEE_DENOMINATOR; + } + const poolTokenAmount = POOL_TOKEN_AMOUNT - feeAmount; + const tokenA = (swapTokenA.amount * BigInt(poolTokenAmount)) / supply; + const tokenB = (swapTokenB.amount * BigInt(poolTokenAmount)) / supply; + + console.log('Creating withdraw token A account'); + let userAccountA = await createAccount( + connection, + payer, + mintA, + owner.publicKey, + Keypair.generate(), + ); + console.log('Creating withdraw token B account'); + let userAccountB = await createAccount( + connection, + payer, + mintB, + owner.publicKey, + Keypair.generate(), + ); + + const userTransferAuthority = Keypair.generate(); + console.log('Approving withdrawal from pool account'); + await approve( + connection, + payer, + tokenAccountPool, + userTransferAuthority.publicKey, + owner, + POOL_TOKEN_AMOUNT, + ); + + const confirmOptions = { + skipPreflight: true, + }; + + console.log('Withdrawing pool tokens for A and B tokens'); + await tokenSwap.withdrawAllTokenTypes( + userAccountA, + userAccountB, + tokenAccountPool, + TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + userTransferAuthority, + POOL_TOKEN_AMOUNT, + tokenA, + tokenB, + confirmOptions, + ); + + //const poolMintInfo = await tokenPool.getMintInfo(); + swapTokenA = await getAccount(connection, tokenAccountA); + swapTokenB = await getAccount(connection, tokenAccountB); + + let info = await getAccount(connection, tokenAccountPool); + assert(info.amount == DEFAULT_POOL_TOKEN_AMOUNT - POOL_TOKEN_AMOUNT); + assert(swapTokenA.amount == currentSwapTokenA - tokenA); + currentSwapTokenA -= tokenA; + assert(swapTokenB.amount == currentSwapTokenB - tokenB); + currentSwapTokenB -= tokenB; + info = await getAccount(connection, userAccountA); + assert(info.amount == tokenA); + info = await getAccount(connection, userAccountB); + assert(info.amount == tokenB); + info = await getAccount(connection, feeAccount); + assert(info.amount == feeAmount); + currentFeeAmount = feeAmount; +} + +export async function createAccountAndSwapAtomic(): Promise { + console.log('Creating swap token a account'); + let userAccountA = await createAccount( + connection, + payer, + mintA, + owner.publicKey, + Keypair.generate(), + ); + await mintTo(connection, payer, mintA, userAccountA, owner, SWAP_AMOUNT_IN); + + // @ts-ignore + const balanceNeeded = await getMinimumBalanceForRentExemptAccount(connection); + const newAccount = Keypair.generate(); + const transaction = new Transaction(); + transaction.add( + SystemProgram.createAccount({ + fromPubkey: owner.publicKey, + newAccountPubkey: newAccount.publicKey, + lamports: balanceNeeded, + space: AccountLayout.span, + programId: mintBProgramId, + }), + ); + + transaction.add( + createInitializeAccountInstruction( + newAccount.publicKey, + mintB, + owner.publicKey, + mintBProgramId, + ), + ); + + const userTransferAuthority = Keypair.generate(); + transaction.add( + createApproveInstruction( + userAccountA, + userTransferAuthority.publicKey, + owner.publicKey, + SWAP_AMOUNT_IN, + [], + mintAProgramId, + ), + ); + + transaction.add( + TokenSwap.swapInstruction( + tokenSwap.tokenSwap, + tokenSwap.authority, + userTransferAuthority.publicKey, + userAccountA, + tokenSwap.tokenAccountA, + tokenSwap.tokenAccountB, + newAccount.publicKey, + tokenSwap.poolToken, + tokenSwap.feeAccount, + null, + tokenSwap.mintA, + tokenSwap.mintB, + tokenSwap.swapProgramId, + TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + tokenSwap.poolTokenProgramId, + SWAP_AMOUNT_IN, + 0n, + ), + ); + + const confirmOptions = { + skipPreflight: true, + }; + + // Send the instructions + console.log('sending big instruction'); + await sendAndConfirmTransaction( + connection, + transaction, + [payer, owner, newAccount, userTransferAuthority], + confirmOptions, + ); + + let info; + info = await getAccount(connection, tokenAccountA); + currentSwapTokenA = info.amount; + info = await getAccount(connection, tokenAccountB); + currentSwapTokenB = info.amount; +} + +export async function swap(): Promise { + console.log('Creating swap token a account'); + let userAccountA = await createAccount( + connection, + payer, + mintA, + owner.publicKey, + Keypair.generate(), + ); + await mintTo(connection, payer, mintA, userAccountA, owner, SWAP_AMOUNT_IN); + const userTransferAuthority = Keypair.generate(); + await approve( + connection, + payer, + userAccountA, + userTransferAuthority.publicKey, + owner, + SWAP_AMOUNT_IN, + ); + console.log('Creating swap token b account'); + let userAccountB = await createAccount( + connection, + payer, + mintB, + owner.publicKey, + Keypair.generate(), + ); + let poolAccount = SWAP_PROGRAM_OWNER_FEE_ADDRESS + ? await createAccount( + connection, + payer, + tokenPool, + owner.publicKey, + Keypair.generate(), + ) + : null; + + const confirmOptions = { + skipPreflight: true, + }; + + console.log('Swapping'); + await tokenSwap.swap( + userAccountA, + tokenAccountA, + tokenAccountB, + userAccountB, + tokenSwap.mintA, + tokenSwap.mintB, + TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + poolAccount, + userTransferAuthority, + SWAP_AMOUNT_IN, + SWAP_AMOUNT_OUT, + confirmOptions, + ); + + await sleep(500); + + let info; + info = await getAccount(connection, userAccountA); + assert(info.amount == 0n); + + info = await getAccount(connection, userAccountB); + assert(info.amount == SWAP_AMOUNT_OUT); + + info = await getAccount(connection, tokenAccountA); + assert(info.amount == currentSwapTokenA + SWAP_AMOUNT_IN); + currentSwapTokenA += SWAP_AMOUNT_IN; + + info = await getAccount(connection, tokenAccountB); + assert(info.amount == currentSwapTokenB - SWAP_AMOUNT_OUT); + currentSwapTokenB -= SWAP_AMOUNT_OUT; + + info = await getAccount(connection, tokenAccountPool); + assert(info.amount == DEFAULT_POOL_TOKEN_AMOUNT - POOL_TOKEN_AMOUNT); + + info = await getAccount(connection, feeAccount); + assert(info.amount == currentFeeAmount + OWNER_SWAP_FEE); + + if (poolAccount != null) { + info = await getAccount(connection, poolAccount); + assert(info.amount == HOST_SWAP_FEE); + } +} + +function tradingTokensToPoolTokens( + sourceAmount: bigint, + swapSourceAmount: bigint, + poolAmount: bigint, +): bigint { + const tradingFee = + (sourceAmount / 2n) * (TRADING_FEE_NUMERATOR / TRADING_FEE_DENOMINATOR); + const ownerTradingFee = + (sourceAmount / 2n) * + (OWNER_TRADING_FEE_NUMERATOR / OWNER_TRADING_FEE_DENOMINATOR); + const sourceAmountPostFee = sourceAmount - tradingFee - ownerTradingFee; + const root = Math.sqrt( + Number(sourceAmountPostFee) / Number(swapSourceAmount) + 1, + ); + return BigInt(Math.floor(Number(poolAmount) * (root - 1))); +} + +export async function depositSingleTokenTypeExactAmountIn(): Promise { + // Pool token amount to deposit on one side + const depositAmount = 10000n; + + const poolMintInfo = await getMint(connection, tokenPool); + const supply = poolMintInfo.supply; + const swapTokenA = await getAccount(connection, tokenAccountA); + //const poolTokenA = tradingTokensToPoolTokens( + // depositAmount, + // swapTokenA.amount, + // supply, + //); + const poolTokenA = 0n; // maybe do this better eventually + const swapTokenB = await getAccount(connection, tokenAccountB); + //const poolTokenB = tradingTokensToPoolTokens( + // depositAmount, + // swapTokenB.amount, + // supply, + //; + const poolTokenB = 0n; // maybe do this better eventually + + const userTransferAuthority = Keypair.generate(); + console.log('Creating depositor token a account'); + const userAccountA = await createAccount( + connection, + payer, + mintA, + owner.publicKey, + Keypair.generate(), + ); + await mintTo(connection, payer, mintA, userAccountA, owner, depositAmount); + await approve( + connection, + payer, + userAccountA, + userTransferAuthority.publicKey, + owner, + depositAmount, + ); + console.log('Creating depositor token b account'); + const userAccountB = await createAccount( + connection, + payer, + mintB, + owner.publicKey, + Keypair.generate(), + ); + await mintTo(connection, payer, mintB, userAccountB, owner, depositAmount); + await approve( + connection, + payer, + userAccountB, + userTransferAuthority.publicKey, + owner, + depositAmount, + ); + console.log('Creating depositor pool token account'); + const newAccountPool = await createAccount( + connection, + payer, + tokenPool, + owner.publicKey, + Keypair.generate(), + ); + + const confirmOptions = { + skipPreflight: true, + }; + + console.log('Depositing token A into swap'); + await tokenSwap.depositSingleTokenTypeExactAmountIn( + userAccountA, + newAccountPool, + tokenSwap.mintA, + TOKEN_PROGRAM_ID, + userTransferAuthority, + depositAmount, + poolTokenA, + confirmOptions, + ); + + let info; + info = await getAccount(connection, userAccountA); + assert(info.amount == 0n); + info = await getAccount(connection, tokenAccountA); + assert(info.amount == currentSwapTokenA + depositAmount); + currentSwapTokenA += depositAmount; + + console.log('Depositing token B into swap'); + await tokenSwap.depositSingleTokenTypeExactAmountIn( + userAccountB, + newAccountPool, + tokenSwap.mintB, + TOKEN_PROGRAM_ID, + userTransferAuthority, + depositAmount, + poolTokenB, + confirmOptions, + ); + + info = await getAccount(connection, userAccountB); + assert(info.amount == 0n); + info = await getAccount(connection, tokenAccountB); + assert(info.amount == currentSwapTokenB + depositAmount); + currentSwapTokenB += depositAmount; + info = await getAccount(connection, newAccountPool); + assert(info.amount >= poolTokenA + poolTokenB); +} + +export async function withdrawSingleTokenTypeExactAmountOut(): Promise { + // Pool token amount to withdraw on one side + const withdrawAmount = 50000n; + + const poolMintInfo = await getMint(connection, tokenPool); + const supply = poolMintInfo.supply; + + const swapTokenA = await getAccount(connection, tokenAccountA); + const swapTokenAPost = swapTokenA.amount - withdrawAmount; + //const poolTokenA = tradingTokensToPoolTokens( + // withdrawAmount, + // swapTokenAPost, + // supply, + //); + //if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) { + // adjustedPoolTokenA *= + // 1n + OWNER_WITHDRAW_FEE_NUMERATOR / OWNER_WITHDRAW_FEE_DENOMINATOR; + //} + let adjustedPoolTokenA = 1_000_000_000_000n; // maybe do this better + + const swapTokenB = await getAccount(connection, tokenAccountB); + const swapTokenBPost = swapTokenB.amount - withdrawAmount; + //const poolTokenB = tradingTokensToPoolTokens( + // withdrawAmount, + // swapTokenBPost, + // supply, + //); + //if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) { + // adjustedPoolTokenB *= + // 1n + OWNER_WITHDRAW_FEE_NUMERATOR / OWNER_WITHDRAW_FEE_DENOMINATOR; + //} + let adjustedPoolTokenB = 1_000_000_000_000n; // maybe do this better + + const userTransferAuthority = Keypair.generate(); + console.log('Creating withdraw token a account'); + const userAccountA = await createAccount( + connection, + payer, + mintA, + owner.publicKey, + Keypair.generate(), + ); + console.log('Creating withdraw token b account'); + const userAccountB = await createAccount( + connection, + payer, + mintB, + owner.publicKey, + Keypair.generate(), + ); + console.log('Creating withdraw pool token account'); + const poolAccount = await getAccount(connection, tokenAccountPool); + const poolTokenAmount = poolAccount.amount; + await approve( + connection, + payer, + tokenAccountPool, + userTransferAuthority.publicKey, + owner, + adjustedPoolTokenA + adjustedPoolTokenB, + ); + + const confirmOptions = { + skipPreflight: true, + }; + + console.log('Withdrawing token A only'); + await tokenSwap.withdrawSingleTokenTypeExactAmountOut( + userAccountA, + tokenAccountPool, + tokenSwap.mintA, + TOKEN_PROGRAM_ID, + userTransferAuthority, + withdrawAmount, + adjustedPoolTokenA, + confirmOptions, + ); + + let info; + info = await getAccount(connection, userAccountA); + assert(info.amount == withdrawAmount); + info = await getAccount(connection, tokenAccountA); + assert(info.amount == currentSwapTokenA - withdrawAmount); + currentSwapTokenA += withdrawAmount; + info = await getAccount(connection, tokenAccountPool); + assert(info.amount >= poolTokenAmount - adjustedPoolTokenA); + + console.log('Withdrawing token B only'); + await tokenSwap.withdrawSingleTokenTypeExactAmountOut( + userAccountB, + tokenAccountPool, + tokenSwap.mintB, + TOKEN_PROGRAM_ID, + userTransferAuthority, + withdrawAmount, + adjustedPoolTokenB, + confirmOptions, + ); + + info = await getAccount(connection, userAccountB); + assert(info.amount == withdrawAmount); + info = await getAccount(connection, tokenAccountB); + assert(info.amount == currentSwapTokenB - withdrawAmount); + currentSwapTokenB += withdrawAmount; + info = await getAccount(connection, tokenAccountPool); + assert( + info.amount >= poolTokenAmount - adjustedPoolTokenA - adjustedPoolTokenB, + ); +} diff --git a/token-swap/js/test/token-swap-test.ts b/token-swap/js/test/token-swap-test.ts deleted file mode 100644 index 9f0728934ed..00000000000 --- a/token-swap/js/test/token-swap-test.ts +++ /dev/null @@ -1,724 +0,0 @@ -import { - Keypair, - Connection, - PublicKey, - SystemProgram, - Transaction, - sendAndConfirmTransaction -} from '@solana/web3.js'; -import {approve, createMint, createAccount, createApproveInstruction, createInitializeAccountInstruction, getAccount, getMint, getMinimumBalanceForRentExemptAccount, mintTo, AccountLayout, TOKEN_PROGRAM_ID} from '@solana/spl-token'; - -import {TokenSwap, CurveType, TOKEN_SWAP_PROGRAM_ID} from '../src'; -import {newAccountWithLamports} from '../src/util/new-account-with-lamports'; -import {sleep} from '../src/util/sleep'; - -// The following globals are created by `createTokenSwap` and used by subsequent tests -// Token swap -let tokenSwap: TokenSwap; -// authority of the token and accounts -let authority: PublicKey; -// bump seed used to generate the authority public key -let bumpSeed: number; -// owner of the user accounts -let owner: Keypair; -// payer for transactions -let payer: Keypair; -// Token pool -let tokenPool: PublicKey; -let tokenAccountPool: PublicKey; -let feeAccount: PublicKey; -// Tokens swapped -let mintA: PublicKey; -const mintAProgramId: PublicKey = TOKEN_PROGRAM_ID; -let mintB: PublicKey; -const mintBProgramId: PublicKey = TOKEN_PROGRAM_ID; -let tokenAccountA: PublicKey; -let tokenAccountB: PublicKey; - -// Hard-coded fee address, for testing production mode -const SWAP_PROGRAM_OWNER_FEE_ADDRESS = - process.env.SWAP_PROGRAM_OWNER_FEE_ADDRESS; - -// Pool fees -const TRADING_FEE_NUMERATOR = 25n; -const TRADING_FEE_DENOMINATOR = 10000n; -const OWNER_TRADING_FEE_NUMERATOR = 5n; -const OWNER_TRADING_FEE_DENOMINATOR = 10000n; -const OWNER_WITHDRAW_FEE_NUMERATOR = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 0n : 1n; -const OWNER_WITHDRAW_FEE_DENOMINATOR = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 0n : 6n; -const HOST_FEE_NUMERATOR = 20n; -const HOST_FEE_DENOMINATOR = 100n; - -// Initial amount in each swap token -let currentSwapTokenA = 1000000n; -let currentSwapTokenB = 1000000n; -let currentFeeAmount = 0n; - -// Swap instruction constants -// Because there is no withdraw fee in the production version, these numbers -// need to get slightly tweaked in the two cases. -const SWAP_AMOUNT_IN = 100000n; -const SWAP_AMOUNT_OUT = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 90661n : 90674n; -const SWAP_FEE = SWAP_PROGRAM_OWNER_FEE_ADDRESS ? 22727n : 22730n; -const HOST_SWAP_FEE = SWAP_PROGRAM_OWNER_FEE_ADDRESS - ? SWAP_FEE * HOST_FEE_NUMERATOR / HOST_FEE_DENOMINATOR - : 0n; -const OWNER_SWAP_FEE = SWAP_FEE - HOST_SWAP_FEE; - -// Pool token amount minted on init -const DEFAULT_POOL_TOKEN_AMOUNT = 1000000000n; -// Pool token amount to withdraw / deposit -const POOL_TOKEN_AMOUNT = 10000000n; - -function assert(condition: boolean, message?: string) { - if (!condition) { - console.log(Error().stack + ':token-test.js'); - throw message || 'Assertion failed'; - } -} - -let connection: Connection; -async function getConnection(): Promise { - if (connection) return connection; - - const url = 'http://127.0.0.1:8899'; - connection = new Connection(url, 'recent'); - const version = await connection.getVersion(); - - console.log('Connection to cluster established:', url, version); - return connection; -} - -export async function createTokenSwap( - curveType: number, - curveParameters?: Uint8Array, -): Promise { - const connection = await getConnection(); - payer = await newAccountWithLamports(connection, 1000000000); - owner = await newAccountWithLamports(connection, 1000000000); - const tokenSwapAccount = Keypair.generate(); - - [authority, bumpSeed] = await PublicKey.findProgramAddress( - [tokenSwapAccount.publicKey.toBuffer()], - TOKEN_SWAP_PROGRAM_ID, - ); - - console.log('creating pool mint'); - tokenPool = await createMint( - connection, - payer, - authority, - null, - 2, - Keypair.generate(), - undefined, - TOKEN_PROGRAM_ID, - ); - - console.log('creating pool account'); - tokenAccountPool = await createAccount(connection, payer, tokenPool, owner.publicKey, Keypair.generate()); - const ownerKey = SWAP_PROGRAM_OWNER_FEE_ADDRESS || owner.publicKey.toString(); - feeAccount = await createAccount(connection, payer, tokenPool, new PublicKey(ownerKey), Keypair.generate()); - - console.log('creating token A'); - mintA = await createMint( - connection, - payer, - owner.publicKey, - null, - 2, - Keypair.generate(), - undefined, - mintAProgramId, - ); - - console.log('creating token A account'); - tokenAccountA = await createAccount(connection, payer, mintA, authority, Keypair.generate()); - console.log('minting token A to swap'); - await mintTo(connection, payer, mintA, tokenAccountA, owner, currentSwapTokenA); - - console.log('creating token B'); - mintB = await createMint( - connection, - payer, - owner.publicKey, - null, - 2, - Keypair.generate(), - undefined, - mintBProgramId, - ); - - console.log('creating token B account'); - tokenAccountB = await createAccount(connection, payer, mintB, authority, Keypair.generate()); - console.log('minting token B to swap'); - await mintTo(connection, payer, mintB, tokenAccountB, owner, currentSwapTokenB); - - console.log('creating token swap'); - const swapPayer = await newAccountWithLamports(connection, 10000000000); - tokenSwap = await TokenSwap.createTokenSwap( - connection, - swapPayer, - tokenSwapAccount, - authority, - tokenAccountA, - tokenAccountB, - tokenPool, - mintA, - mintB, - feeAccount, - tokenAccountPool, - TOKEN_SWAP_PROGRAM_ID, - TOKEN_PROGRAM_ID, - TRADING_FEE_NUMERATOR, - TRADING_FEE_DENOMINATOR, - OWNER_TRADING_FEE_NUMERATOR, - OWNER_TRADING_FEE_DENOMINATOR, - OWNER_WITHDRAW_FEE_NUMERATOR, - OWNER_WITHDRAW_FEE_DENOMINATOR, - HOST_FEE_NUMERATOR, - HOST_FEE_DENOMINATOR, - curveType, - curveParameters, - ); - - console.log('loading token swap'); - const fetchedTokenSwap = await TokenSwap.loadTokenSwap( - connection, - tokenSwapAccount.publicKey, - TOKEN_SWAP_PROGRAM_ID, - swapPayer, - ); - - assert(fetchedTokenSwap.poolTokenProgramId.equals(TOKEN_PROGRAM_ID)); - assert(fetchedTokenSwap.tokenAccountA.equals(tokenAccountA)); - assert(fetchedTokenSwap.tokenAccountB.equals(tokenAccountB)); - assert(fetchedTokenSwap.mintA.equals(mintA)); - assert(fetchedTokenSwap.mintB.equals(mintB)); - assert(fetchedTokenSwap.poolToken.equals(tokenPool)); - assert(fetchedTokenSwap.feeAccount.equals(feeAccount)); - assert( - TRADING_FEE_NUMERATOR == fetchedTokenSwap.tradeFeeNumerator - ); - assert( - TRADING_FEE_DENOMINATOR == fetchedTokenSwap.tradeFeeDenominator - ); - assert( - OWNER_TRADING_FEE_NUMERATOR == - fetchedTokenSwap.ownerTradeFeeNumerator - ); - assert( - OWNER_TRADING_FEE_DENOMINATOR == - fetchedTokenSwap.ownerTradeFeeDenominator - ); - assert( - OWNER_WITHDRAW_FEE_NUMERATOR == - fetchedTokenSwap.ownerWithdrawFeeNumerator - ); - assert( - OWNER_WITHDRAW_FEE_DENOMINATOR == - fetchedTokenSwap.ownerWithdrawFeeDenominator - ); - assert(HOST_FEE_NUMERATOR == fetchedTokenSwap.hostFeeNumerator); - assert( - HOST_FEE_DENOMINATOR == fetchedTokenSwap.hostFeeDenominator - ); - assert(curveType == fetchedTokenSwap.curveType); -} - -export async function depositAllTokenTypes(): Promise { - const poolMintInfo = await getMint(connection, tokenPool); - const supply = poolMintInfo.supply; - const swapTokenA = await getAccount(connection, tokenAccountA); - const tokenA = swapTokenA.amount * BigInt(POOL_TOKEN_AMOUNT) / supply; - const swapTokenB = await getAccount(connection, tokenAccountB); - const tokenB = swapTokenB.amount * BigInt(POOL_TOKEN_AMOUNT) / supply; - - const userTransferAuthority = Keypair.generate(); - console.log('Creating depositor token a account'); - const userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate()); - await mintTo(connection, payer, mintA, userAccountA, owner, tokenA); - await approve( - connection, - payer, - userAccountA, - userTransferAuthority.publicKey, - owner, - tokenA, - ); - console.log('Creating depositor token b account'); - const userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate()); - await mintTo(connection, payer, mintB, userAccountB, owner, tokenB); - await approve( - connection, - payer, - userAccountB, - userTransferAuthority.publicKey, - owner, - tokenB, - ); - console.log('Creating depositor pool token account'); - const newAccountPool = await createAccount(connection, payer, tokenPool, owner.publicKey, Keypair.generate()); - - const confirmOptions = { - skipPreflight: true - } - - console.log('Depositing into swap'); - await tokenSwap.depositAllTokenTypes( - userAccountA, - userAccountB, - newAccountPool, - TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - userTransferAuthority, - POOL_TOKEN_AMOUNT, - tokenA, - tokenB, - confirmOptions - ); - - let info; - info = await getAccount(connection, userAccountA); - assert(info.amount == 0n); - info = await getAccount(connection, userAccountB); - assert(info.amount == 0n); - info = await getAccount(connection, tokenAccountA); - assert(info.amount == currentSwapTokenA + tokenA); - currentSwapTokenA += tokenA; - info = await getAccount(connection, tokenAccountB); - assert(info.amount == currentSwapTokenB + tokenB); - currentSwapTokenB += tokenB; - info = await getAccount(connection, newAccountPool); - assert(info.amount == POOL_TOKEN_AMOUNT); -} - -export async function withdrawAllTokenTypes(): Promise { - const poolMintInfo = await getMint(connection, tokenPool); - const supply = poolMintInfo.supply; - let swapTokenA = await getAccount(connection, tokenAccountA); - let swapTokenB = await getAccount(connection, tokenAccountB); - let feeAmount = 0n; - if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) { - feeAmount = - (POOL_TOKEN_AMOUNT * OWNER_WITHDRAW_FEE_NUMERATOR) / - OWNER_WITHDRAW_FEE_DENOMINATOR; - } - const poolTokenAmount = POOL_TOKEN_AMOUNT - feeAmount; - const tokenA = swapTokenA.amount * BigInt(poolTokenAmount) / supply; - const tokenB = swapTokenB.amount * BigInt(poolTokenAmount) / supply; - - console.log('Creating withdraw token A account'); - let userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate()); - console.log('Creating withdraw token B account'); - let userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate()); - - const userTransferAuthority = Keypair.generate(); - console.log('Approving withdrawal from pool account'); - await approve( - connection, - payer, - tokenAccountPool, - userTransferAuthority.publicKey, - owner, - POOL_TOKEN_AMOUNT, - ); - - const confirmOptions = { - skipPreflight: true - } - - console.log('Withdrawing pool tokens for A and B tokens'); - await tokenSwap.withdrawAllTokenTypes( - userAccountA, - userAccountB, - tokenAccountPool, - TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - userTransferAuthority, - POOL_TOKEN_AMOUNT, - tokenA, - tokenB, - confirmOptions - ); - - //const poolMintInfo = await tokenPool.getMintInfo(); - swapTokenA = await getAccount(connection, tokenAccountA); - swapTokenB = await getAccount(connection, tokenAccountB); - - let info = await getAccount(connection, tokenAccountPool); - assert( - info.amount == DEFAULT_POOL_TOKEN_AMOUNT - POOL_TOKEN_AMOUNT, - ); - assert(swapTokenA.amount == currentSwapTokenA - tokenA); - currentSwapTokenA -= tokenA; - assert(swapTokenB.amount == currentSwapTokenB - tokenB); - currentSwapTokenB -= tokenB; - info = await getAccount(connection, userAccountA); - assert(info.amount == tokenA); - info = await getAccount(connection, userAccountB); - assert(info.amount == tokenB); - info = await getAccount(connection, feeAccount); - assert(info.amount == feeAmount); - currentFeeAmount = feeAmount; -} - -export async function createAccountAndSwapAtomic(): Promise { - console.log('Creating swap token a account'); - let userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate()); - await mintTo(connection, payer, mintA, userAccountA, owner, SWAP_AMOUNT_IN); - - // @ts-ignore - const balanceNeeded = await getMinimumBalanceForRentExemptAccount( - connection, - ); - const newAccount = Keypair.generate(); - const transaction = new Transaction(); - transaction.add( - SystemProgram.createAccount({ - fromPubkey: owner.publicKey, - newAccountPubkey: newAccount.publicKey, - lamports: balanceNeeded, - space: AccountLayout.span, - programId: mintBProgramId, - }), - ); - - transaction.add( - createInitializeAccountInstruction( - newAccount.publicKey, - mintB, - owner.publicKey, - mintBProgramId, - ), - ); - - const userTransferAuthority = Keypair.generate(); - transaction.add( - createApproveInstruction( - userAccountA, - userTransferAuthority.publicKey, - owner.publicKey, - SWAP_AMOUNT_IN, - [], - mintAProgramId, - ), - ); - - transaction.add( - TokenSwap.swapInstruction( - tokenSwap.tokenSwap, - tokenSwap.authority, - userTransferAuthority.publicKey, - userAccountA, - tokenSwap.tokenAccountA, - tokenSwap.tokenAccountB, - newAccount.publicKey, - tokenSwap.poolToken, - tokenSwap.feeAccount, - null, - tokenSwap.mintA, - tokenSwap.mintB, - tokenSwap.swapProgramId, - TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - tokenSwap.poolTokenProgramId, - SWAP_AMOUNT_IN, - 0n, - ), - ); - - const confirmOptions = { - skipPreflight: true - } - - // Send the instructions - console.log('sending big instruction'); - await sendAndConfirmTransaction( - connection, - transaction, - [payer, owner, newAccount, userTransferAuthority], - confirmOptions - ); - - let info; - info = await getAccount(connection, tokenAccountA); - currentSwapTokenA = info.amount; - info = await getAccount(connection, tokenAccountB); - currentSwapTokenB = info.amount; -} - -export async function swap(): Promise { - console.log('Creating swap token a account'); - let userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate()); - await mintTo(connection, payer, mintA, userAccountA, owner, SWAP_AMOUNT_IN); - const userTransferAuthority = Keypair.generate(); - await approve( - connection, - payer, - userAccountA, - userTransferAuthority.publicKey, - owner, - SWAP_AMOUNT_IN, - ); - console.log('Creating swap token b account'); - let userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate()); - let poolAccount = SWAP_PROGRAM_OWNER_FEE_ADDRESS - ? await createAccount(connection, payer, tokenPool, owner.publicKey, Keypair.generate()) - : null; - - const confirmOptions = { - skipPreflight: true - } - - console.log('Swapping'); - await tokenSwap.swap( - userAccountA, - tokenAccountA, - tokenAccountB, - userAccountB, - tokenSwap.mintA, - tokenSwap.mintB, - TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - poolAccount, - userTransferAuthority, - SWAP_AMOUNT_IN, - SWAP_AMOUNT_OUT, - confirmOptions - ); - - await sleep(500); - - let info; - info = await getAccount(connection, userAccountA); - assert(info.amount == 0n); - - info = await getAccount(connection, userAccountB); - assert(info.amount == SWAP_AMOUNT_OUT); - - info = await getAccount(connection, tokenAccountA); - assert(info.amount == currentSwapTokenA + SWAP_AMOUNT_IN); - currentSwapTokenA += SWAP_AMOUNT_IN; - - info = await getAccount(connection, tokenAccountB); - assert(info.amount == currentSwapTokenB - SWAP_AMOUNT_OUT); - currentSwapTokenB -= SWAP_AMOUNT_OUT; - - info = await getAccount(connection, tokenAccountPool); - assert( - info.amount == DEFAULT_POOL_TOKEN_AMOUNT - POOL_TOKEN_AMOUNT, - ); - - info = await getAccount(connection, feeAccount); - assert(info.amount == currentFeeAmount + OWNER_SWAP_FEE); - - if (poolAccount != null) { - info = await getAccount(connection, poolAccount); - assert(info.amount == HOST_SWAP_FEE); - } -} - -function tradingTokensToPoolTokens( - sourceAmount: bigint, - swapSourceAmount: bigint, - poolAmount: bigint, -): bigint { - const tradingFee = - (sourceAmount / 2n) * (TRADING_FEE_NUMERATOR / TRADING_FEE_DENOMINATOR); - const ownerTradingFee = - (sourceAmount / 2n) * (OWNER_TRADING_FEE_NUMERATOR / OWNER_TRADING_FEE_DENOMINATOR); - const sourceAmountPostFee = sourceAmount - tradingFee - ownerTradingFee; - const root = Math.sqrt(Number(sourceAmountPostFee) / Number(swapSourceAmount) + 1); - return BigInt(Math.floor(Number(poolAmount) * (root - 1))); -} - -export async function depositSingleTokenTypeExactAmountIn(): Promise { - // Pool token amount to deposit on one side - const depositAmount = 10000n; - - const poolMintInfo = await getMint(connection, tokenPool); - const supply = poolMintInfo.supply; - const swapTokenA = await getAccount(connection, tokenAccountA); - //const poolTokenA = tradingTokensToPoolTokens( - // depositAmount, - // swapTokenA.amount, - // supply, - //); - const poolTokenA = 0n; // maybe do this better eventually - const swapTokenB = await getAccount(connection, tokenAccountB); - //const poolTokenB = tradingTokensToPoolTokens( - // depositAmount, - // swapTokenB.amount, - // supply, - //; - const poolTokenB = 0n; // maybe do this better eventually - - const userTransferAuthority = Keypair.generate(); - console.log('Creating depositor token a account'); - const userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate()); - await mintTo(connection, payer, mintA, userAccountA, owner, depositAmount); - await approve( - connection, - payer, - userAccountA, - userTransferAuthority.publicKey, - owner, - depositAmount, - ); - console.log('Creating depositor token b account'); - const userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate()); - await mintTo(connection, payer, mintB, userAccountB, owner, depositAmount); - await approve( - connection, - payer, - userAccountB, - userTransferAuthority.publicKey, - owner, - depositAmount, - ); - console.log('Creating depositor pool token account'); - const newAccountPool = await createAccount(connection, payer, tokenPool, owner.publicKey, Keypair.generate()); - - const confirmOptions = { - skipPreflight: true - } - - console.log('Depositing token A into swap'); - await tokenSwap.depositSingleTokenTypeExactAmountIn( - userAccountA, - newAccountPool, - tokenSwap.mintA, - TOKEN_PROGRAM_ID, - userTransferAuthority, - depositAmount, - poolTokenA, - confirmOptions - ); - - let info; - info = await getAccount(connection, userAccountA); - assert(info.amount == 0n); - info = await getAccount(connection, tokenAccountA); - assert(info.amount == currentSwapTokenA + depositAmount); - currentSwapTokenA += depositAmount; - - console.log('Depositing token B into swap'); - await tokenSwap.depositSingleTokenTypeExactAmountIn( - userAccountB, - newAccountPool, - tokenSwap.mintB, - TOKEN_PROGRAM_ID, - userTransferAuthority, - depositAmount, - poolTokenB, - confirmOptions - ); - - info = await getAccount(connection, userAccountB); - assert(info.amount == 0n); - info = await getAccount(connection, tokenAccountB); - assert(info.amount == currentSwapTokenB + depositAmount); - currentSwapTokenB += depositAmount; - info = await getAccount(connection, newAccountPool); - assert(info.amount >= poolTokenA + poolTokenB); -} - -export async function withdrawSingleTokenTypeExactAmountOut(): Promise { - // Pool token amount to withdraw on one side - const withdrawAmount = 50000n; - - const poolMintInfo = await getMint(connection, tokenPool); - const supply = poolMintInfo.supply; - - const swapTokenA = await getAccount(connection, tokenAccountA); - const swapTokenAPost = swapTokenA.amount - withdrawAmount; - //const poolTokenA = tradingTokensToPoolTokens( - // withdrawAmount, - // swapTokenAPost, - // supply, - //); - //if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) { - // adjustedPoolTokenA *= - // 1n + OWNER_WITHDRAW_FEE_NUMERATOR / OWNER_WITHDRAW_FEE_DENOMINATOR; - //} - let adjustedPoolTokenA = 1_000_000_000_000n; // maybe do this better - - const swapTokenB = await getAccount(connection, tokenAccountB); - const swapTokenBPost = swapTokenB.amount - withdrawAmount; - //const poolTokenB = tradingTokensToPoolTokens( - // withdrawAmount, - // swapTokenBPost, - // supply, - //); - //if (OWNER_WITHDRAW_FEE_NUMERATOR !== 0n) { - // adjustedPoolTokenB *= - // 1n + OWNER_WITHDRAW_FEE_NUMERATOR / OWNER_WITHDRAW_FEE_DENOMINATOR; - //} - let adjustedPoolTokenB = 1_000_000_000_000n; // maybe do this better - - const userTransferAuthority = Keypair.generate(); - console.log('Creating withdraw token a account'); - const userAccountA = await createAccount(connection, payer, mintA, owner.publicKey, Keypair.generate()); - console.log('Creating withdraw token b account'); - const userAccountB = await createAccount(connection, payer, mintB, owner.publicKey, Keypair.generate()); - console.log('Creating withdraw pool token account'); - const poolAccount = await getAccount(connection, tokenAccountPool); - const poolTokenAmount = poolAccount.amount; - await approve( - connection, - payer, - tokenAccountPool, - userTransferAuthority.publicKey, - owner, - adjustedPoolTokenA + adjustedPoolTokenB, - ); - - const confirmOptions = { - skipPreflight: true - } - - console.log('Withdrawing token A only'); - await tokenSwap.withdrawSingleTokenTypeExactAmountOut( - userAccountA, - tokenAccountPool, - tokenSwap.mintA, - TOKEN_PROGRAM_ID, - userTransferAuthority, - withdrawAmount, - adjustedPoolTokenA, - confirmOptions - ); - - let info; - info = await getAccount(connection, userAccountA); - assert(info.amount == withdrawAmount); - info = await getAccount(connection, tokenAccountA); - assert(info.amount == currentSwapTokenA - withdrawAmount); - currentSwapTokenA += withdrawAmount; - info = await getAccount(connection, tokenAccountPool); - assert(info.amount >= poolTokenAmount - adjustedPoolTokenA); - - console.log('Withdrawing token B only'); - await tokenSwap.withdrawSingleTokenTypeExactAmountOut( - userAccountB, - tokenAccountPool, - tokenSwap.mintB, - TOKEN_PROGRAM_ID, - userTransferAuthority, - withdrawAmount, - adjustedPoolTokenB, - confirmOptions - ); - - info = await getAccount(connection, userAccountB); - assert(info.amount == withdrawAmount); - info = await getAccount(connection, tokenAccountB); - assert(info.amount == currentSwapTokenB - withdrawAmount); - currentSwapTokenB += withdrawAmount; - info = await getAccount(connection, tokenAccountPool); - assert( - info.amount >= - poolTokenAmount - adjustedPoolTokenA - adjustedPoolTokenB, - ); -}