From 7ad7e5622cebadf30bd983459f1ef64c3659846c Mon Sep 17 00:00:00 2001 From: Wilhelm Thieme Date: Mon, 11 Nov 2024 12:05:27 -0500 Subject: [PATCH] Add whirlpools cli package --- legacy-sdk/cli/README.md | 73 ++ legacy-sdk/cli/package.json | 28 + legacy-sdk/cli/src/commands/close_position.ts | 92 +++ legacy-sdk/cli/src/commands/collect_fees.ts | 182 +++++ .../cli/src/commands/collect_rewards.ts | 171 ++++ .../cli/src/commands/decrease_liquidity.ts | 288 +++++++ .../cli/src/commands/delete_token_badge.ts | 84 ++ .../cli/src/commands/increase_liquidity.ts | 324 ++++++++ .../cli/src/commands/initialize_config.ts | 59 ++ .../commands/initialize_config_extension.ts | 57 ++ .../cli/src/commands/initialize_fee_tier.ts | 67 ++ .../cli/src/commands/initialize_reward.ts | 128 +++ .../cli/src/commands/initialize_tick_array.ts | 243 ++++++ .../src/commands/initialize_token_badge.ts | 86 ++ .../cli/src/commands/initialize_whirlpool.ts | 196 +++++ legacy-sdk/cli/src/commands/open_position.ts | 323 ++++++++ legacy-sdk/cli/src/commands/push_price.ts | 316 ++++++++ .../set_collect_protocol_fees_authority.ts | 106 +++ .../commands/set_default_protocol_fee_rate.ts | 26 + .../cli/src/commands/set_fee_authority.ts | 93 +++ .../src/commands/set_token_badge_authority.ts | 85 ++ .../commands/todo/initializeTickArrayRange | 379 +++++++++ .../commands/todo/initializeWhirlpoolReward | 38 + legacy-sdk/cli/src/index.ts | 21 + legacy-sdk/cli/src/utils/deposit_ratio.ts | 54 ++ legacy-sdk/cli/src/utils/prompt.ts | 68 ++ legacy-sdk/cli/src/utils/provider.ts | 18 + .../cli/src/utils/transaction_sender.ts | 197 +++++ legacy-sdk/cli/tsconfig.json | 8 + legacy-sdk/scripts/calcRentExempt.ts | 52 -- .../scripts/expLogTestCaseConversion.ts | 15 - legacy-sdk/scripts/genExpBitConstants.ts | 66 -- .../scripts/genPositiveExpBitConstants.ts | 13 - legacy-sdk/scripts/package.json | 19 - legacy-sdk/scripts/tsconfig.json | 12 - legacy-sdk/scripts/whirlpoolNft.json | 11 - .../src/impl/whirlpool-client-impl.ts | 5 +- .../whirlpool/src/impl/whirlpool-impl.ts | 37 +- .../composites/collect-all-txn.ts | 4 +- .../src/instructions/composites/swap-async.ts | 13 +- .../network/public/fetcher/fetcher-utils.ts | 120 +-- .../whirlpool/src/quotes/swap/swap-manager.ts | 2 +- .../whirlpool/src/utils/instructions-util.ts | 9 +- .../whirlpool/src/utils/math/swap-math.ts | 19 +- .../whirlpool/src/utils/math/token-math.ts | 30 +- .../whirlpool/src/utils/public/pool-utils.ts | 1 - .../close_bundled_position.test.ts | 26 +- .../tests/integration/close_position.test.ts | 21 +- ...ose_position_with_token_extensions.test.ts | 572 +++++++------ ...n_with_token_extensions_management.test.ts | 272 ++++--- .../integration/multi-ix/splash_pool.test.ts | 507 +++++++----- ...pen_position_with_token_extensions.test.ts | 466 ++++++----- .../whirlpool/tests/integration/swap.test.ts | 79 +- .../tests/integration/two_hop_swap.test.ts | 688 ++++++++++------ .../integration/v2/collect_fees_v2.test.ts | 8 +- .../tests/integration/v2/swap_v2.test.ts | 102 ++- .../token-extensions/interest-bearing.test.ts | 125 +-- .../v2/token-extensions/transfer-hook.test.ts | 154 ++-- .../integration/v2/two_hop_swap_v2.test.ts | 763 ++++++++++++------ .../position-impl-collectFees.test.ts | 50 +- .../position-impl-collectRewards.test.ts | 44 +- .../sdk/whirlpools/position-impl.test.ts | 34 +- .../whirlpools/swap/swap-edge-case.test.ts | 44 +- .../sdk/whirlpools/utils/fetcher-util.test.ts | 294 +++++-- .../whirlpools/whirlpool-client-impl.test.ts | 45 +- .../whirlpool-impl-closePosition.test.ts | 20 +- ...-collectFeesAndRewardsForPositions.test.ts | 68 +- .../sdk/whirlpools/whirlpool-impl.test.ts | 305 +++---- .../whirlpool/tests/utils/init-utils.ts | 38 +- .../whirlpool/tests/utils/test-builders.ts | 6 +- .../whirlpool/tests/utils/v2/init-utils-v2.ts | 2 +- .../whirlpool/tests/utils/v2/token-2022.ts | 4 +- yarn.lock | 184 +++-- 73 files changed, 6990 insertions(+), 2169 deletions(-) create mode 100644 legacy-sdk/cli/README.md create mode 100644 legacy-sdk/cli/package.json create mode 100644 legacy-sdk/cli/src/commands/close_position.ts create mode 100644 legacy-sdk/cli/src/commands/collect_fees.ts create mode 100644 legacy-sdk/cli/src/commands/collect_rewards.ts create mode 100644 legacy-sdk/cli/src/commands/decrease_liquidity.ts create mode 100644 legacy-sdk/cli/src/commands/delete_token_badge.ts create mode 100644 legacy-sdk/cli/src/commands/increase_liquidity.ts create mode 100644 legacy-sdk/cli/src/commands/initialize_config.ts create mode 100644 legacy-sdk/cli/src/commands/initialize_config_extension.ts create mode 100644 legacy-sdk/cli/src/commands/initialize_fee_tier.ts create mode 100644 legacy-sdk/cli/src/commands/initialize_reward.ts create mode 100644 legacy-sdk/cli/src/commands/initialize_tick_array.ts create mode 100644 legacy-sdk/cli/src/commands/initialize_token_badge.ts create mode 100644 legacy-sdk/cli/src/commands/initialize_whirlpool.ts create mode 100644 legacy-sdk/cli/src/commands/open_position.ts create mode 100644 legacy-sdk/cli/src/commands/push_price.ts create mode 100644 legacy-sdk/cli/src/commands/set_collect_protocol_fees_authority.ts create mode 100644 legacy-sdk/cli/src/commands/set_default_protocol_fee_rate.ts create mode 100644 legacy-sdk/cli/src/commands/set_fee_authority.ts create mode 100644 legacy-sdk/cli/src/commands/set_token_badge_authority.ts create mode 100644 legacy-sdk/cli/src/commands/todo/initializeTickArrayRange create mode 100644 legacy-sdk/cli/src/commands/todo/initializeWhirlpoolReward create mode 100644 legacy-sdk/cli/src/index.ts create mode 100644 legacy-sdk/cli/src/utils/deposit_ratio.ts create mode 100644 legacy-sdk/cli/src/utils/prompt.ts create mode 100644 legacy-sdk/cli/src/utils/provider.ts create mode 100644 legacy-sdk/cli/src/utils/transaction_sender.ts create mode 100644 legacy-sdk/cli/tsconfig.json delete mode 100644 legacy-sdk/scripts/calcRentExempt.ts delete mode 100644 legacy-sdk/scripts/expLogTestCaseConversion.ts delete mode 100644 legacy-sdk/scripts/genExpBitConstants.ts delete mode 100644 legacy-sdk/scripts/genPositiveExpBitConstants.ts delete mode 100644 legacy-sdk/scripts/package.json delete mode 100644 legacy-sdk/scripts/tsconfig.json delete mode 100644 legacy-sdk/scripts/whirlpoolNft.json diff --git a/legacy-sdk/cli/README.md b/legacy-sdk/cli/README.md new file mode 100644 index 000000000..f157eae87 --- /dev/null +++ b/legacy-sdk/cli/README.md @@ -0,0 +1,73 @@ +# Setting up your script environment +```bash +yarn +``` + +# Set your RPC and wallet +```bash +export ANCHOR_PROVIDER_URL= +export ANCHOR_WALLET= +``` + +Example: +```bash +export ANCHOR_PROVIDER_URL=http://localhost:8899 +export ANCHOR_WALLET=~/.config/solana/id.json +``` + +# Supported commands +Token-2022 tokens are acceptable 👍 + +## Config & FeeTier +### initialize +- `yarn start initializeConfig`: initialize new WhirlpoolsConfig account +- `yarn start initializeConfigExtension`: initialize new WhirlpoolsConfigExtension account +- `yarn start initializeFeeTier`: initialize new FeeTier account + +### update +- `yarn start setTokenBadgeAuthority`: set new TokenBadge authority on WhirlpoolsConfigExtension +- `yarn start setDefaultProtocolFeeRate`: set new default protocol fee rate on WhirlpoolsConfig +- `yarn start setFeeAuthority`: set new fee authority on WhirlpoolsConfig +- `yarn start setCollectProtocolFeesAuthority`: set new collect protocol fees authority on WhirlpoolsConfig +- TODO: set reward emissions super authority +- TODO: set config extension authority + +## Whirlpool & TickArray +- `yarn start initializeWhirlpool`: initialize new Whirlpool account +- `yarn start initializeTickArray`: initialize new TickArray account + +## TokenBadge +- `yarn start initializeTokenBadge`: initialize new TokenBadge account +- `yarn start deleteTokenBadge`: delete TokenBadge account + +## Reward +- `yarn start initializeReward`: initialize new reward for a whirlpool +- TODO: set reward emission + +## Position +- `yarn start openPosition`: open a new position +- `yarn start increaseLiquidity`: deposit to a position +- `yarn start decreaseLiquidity`: withdraw from a position +- `yarn start collectFees`: collect fees from a position +- `yarn start collectRewards`: collect rewards from a position +- `yarn start closePosition`: close an empty position + +## Swap +- `yarn start pushPrice`: adjust pool price (possible if pool liquidity is zero or very small) + +## WSOL and ATA creation +TODO: WSOL handling & create ATA if needed (workaround exists, please see the following) + +### workaround for WSOL +`whirlpool-mgmt-tools` works well with ATA, so using WSOL on ATA is workaround. + +- wrap 1 SOL: `spl-token wrap 1` (ATA for WSOL will be initialized with 1 SOL) +- unwrap: `spl-token unwrap` (ATA for WSOL will be closed) +- add 1 WSOL: `solana transfer 1` then `spl-token sync-native` (transfer & sync are needed) + +### workaround for ATA +We can easily initialize ATA with spl-token CLI. + +``` +spl-token create-account +``` diff --git a/legacy-sdk/cli/package.json b/legacy-sdk/cli/package.json new file mode 100644 index 000000000..ae22c4119 --- /dev/null +++ b/legacy-sdk/cli/package.json @@ -0,0 +1,28 @@ +{ + "name": "@orca-so/whirlpools-sdk-cli", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "build": "tsc --noEmit", + "start": "tsx src/index.ts" + }, + "dependencies": { + "@coral-xyz/anchor": "0.29.0", + "@orca-so/common-sdk": "0.6.3", + "@orca-so/orca-sdk": "0.1.1", + "@orca-so/whirlpools-sdk": "*", + "@solana/spl-token": "0.4.1", + "@solana/web3.js": "^1.90.0", + "@types/bn.js": "^5.1.0", + "bs58": "^5.0.0", + "decimal.js": "^10.4.3", + "js-convert-case": "^4.2.0", + "prompts": "^2.4.2" + }, + "devDependencies": { + "@types/prompts": "^2.4.9", + "tsx": "^4.19.0", + "typescript": "^5.4.5" + } +} diff --git a/legacy-sdk/cli/src/commands/close_position.ts b/legacy-sdk/cli/src/commands/close_position.ts new file mode 100644 index 000000000..bff0f816b --- /dev/null +++ b/legacy-sdk/cli/src/commands/close_position.ts @@ -0,0 +1,92 @@ +import { PublicKey } from "@solana/web3.js"; +import { WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptText } from "../utils/prompt"; + +console.info("close Position..."); + +// prompt +const positionPubkeyStr = await promptText("positionPubkey"); + +const positionPubkey = new PublicKey(positionPubkeyStr); +const position = await ctx.fetcher.getPosition(positionPubkey); +if (!position) { + throw new Error("position not found"); +} +const positionMint = await ctx.fetcher.getMintInfo(position.positionMint); +if (!positionMint) { + throw new Error("positionMint not found"); +} + +if (!position.liquidity.isZero()) { + throw new Error("position is not empty (liquidity is not zero)"); +} + +if (!position.feeOwedA.isZero() || !position.feeOwedB.isZero()) { + throw new Error("position has collectable fees"); +} + +if (!position.rewardInfos.every((r) => r.amountOwed.isZero())) { + throw new Error("position has collectable rewards"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); + +if (positionMint.tokenProgram.equals(TOKEN_PROGRAM_ID)) { + builder.addInstruction( + WhirlpoolIx.closePositionIx(ctx.program, { + position: positionPubkey, + positionAuthority: ctx.wallet.publicKey, + positionTokenAccount: getAssociatedTokenAddressSync( + position.positionMint, + ctx.wallet.publicKey, + ), + positionMint: position.positionMint, + receiver: ctx.wallet.publicKey, + }), + ); +} else { + builder.addInstruction( + WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { + position: positionPubkey, + positionAuthority: ctx.wallet.publicKey, + positionTokenAccount: getAssociatedTokenAddressSync( + position.positionMint, + ctx.wallet.publicKey, + undefined, + TOKEN_2022_PROGRAM_ID, + ), + positionMint: position.positionMint, + receiver: ctx.wallet.publicKey, + }), + ); +} + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +close Position... +prompt: positionPubkey: H4WEb57EYh5AhorHArjgRXVgSBJRMZi3DvsLb3J1XNj6 +estimatedComputeUnits: 120649 +prompt: priorityFeeInSOL: 0 +Priority fee: 0 SOL +process transaction... +transaction is still valid, 150 blocks left (at most) +sending... +confirming... +✅successfully landed +signature dQwedycTbM9UTYwQiiUE5Q7ydZRzL3zywaQ3xEo3RhHxDvfsY8wkAakSXQRdXswxdQCLLMwwDJVSNHYcTCDDcf3 + +*/ diff --git a/legacy-sdk/cli/src/commands/collect_fees.ts b/legacy-sdk/cli/src/commands/collect_fees.ts new file mode 100644 index 000000000..018724626 --- /dev/null +++ b/legacy-sdk/cli/src/commands/collect_fees.ts @@ -0,0 +1,182 @@ +import { PublicKey } from "@solana/web3.js"; +import { + ORCA_WHIRLPOOL_PROGRAM_ID, + PDAUtil, + WhirlpoolIx, + collectFeesQuote, + TickArrayUtil, +} from "@orca-so/whirlpools-sdk"; +import { DecimalUtil, TransactionBuilder } from "@orca-so/common-sdk"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import { TokenExtensionUtil } from "@orca-so/whirlpools-sdk/dist/utils/public/token-extension-util"; +import { ctx } from "../utils/provider"; +import { promptText } from "../utils/prompt"; + +console.info("collect Fees..."); + +// prompt +const positionPubkeyStr = await promptText("positionPubkey"); + +const positionPubkey = new PublicKey(positionPubkeyStr); +const position = await ctx.fetcher.getPosition(positionPubkey); +if (!position) { + throw new Error("position not found"); +} +const positionMint = await ctx.fetcher.getMintInfo(position.positionMint); +if (!positionMint) { + throw new Error("positionMint not found"); +} + +const whirlpoolPubkey = position.whirlpool; +const whirlpool = await ctx.fetcher.getPool(whirlpoolPubkey); +if (!whirlpool) { + throw new Error("whirlpool not found"); +} +const tickSpacing = whirlpool.tickSpacing; + +const tokenMintAPubkey = whirlpool.tokenMintA; +const tokenMintBPubkey = whirlpool.tokenMintB; +const mintA = await ctx.fetcher.getMintInfo(tokenMintAPubkey); +const mintB = await ctx.fetcher.getMintInfo(tokenMintBPubkey); +if (!mintA || !mintB) { + // extremely rare case (CloseMint extension on Token-2022 is used) + throw new Error("token mint not found"); +} +const decimalsA = mintA.decimals; +const decimalsB = mintB.decimals; + +const lowerTickArrayPubkey = PDAUtil.getTickArrayFromTickIndex( + position.tickLowerIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, +).publicKey; +const upperTickArrayPubkey = PDAUtil.getTickArrayFromTickIndex( + position.tickUpperIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, +).publicKey; + +const lowerTickArray = await ctx.fetcher.getTickArray(lowerTickArrayPubkey); +const upperTickArray = await ctx.fetcher.getTickArray(upperTickArrayPubkey); +if (!lowerTickArray || !upperTickArray) { + throw new Error("tick array not found"); +} + +const quote = collectFeesQuote({ + position, + tickLower: TickArrayUtil.getTickFromArray( + lowerTickArray, + position.tickLowerIndex, + tickSpacing, + ), + tickUpper: TickArrayUtil.getTickFromArray( + upperTickArray, + position.tickUpperIndex, + tickSpacing, + ), + whirlpool, + tokenExtensionCtx: await TokenExtensionUtil.buildTokenExtensionContext( + ctx.fetcher, + whirlpool, + ), +}); + +console.info( + "collectable feeA: ", + DecimalUtil.fromBN(quote.feeOwedA, decimalsA), +); +console.info( + "collectable feeB: ", + DecimalUtil.fromBN(quote.feeOwedB, decimalsB), +); + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); + +const tokenOwnerAccountA = getAssociatedTokenAddressSync( + tokenMintAPubkey, + ctx.wallet.publicKey, + undefined, + mintA.tokenProgram, +); +const tokenOwnerAccountB = getAssociatedTokenAddressSync( + tokenMintBPubkey, + ctx.wallet.publicKey, + undefined, + mintB.tokenProgram, +); + +if (position.liquidity.gtn(0)) { + builder.addInstruction( + WhirlpoolIx.updateFeesAndRewardsIx(ctx.program, { + position: positionPubkey, + tickArrayLower: lowerTickArrayPubkey, + tickArrayUpper: upperTickArrayPubkey, + whirlpool: whirlpoolPubkey, + }), + ); +} + +builder.addInstruction( + WhirlpoolIx.collectFeesV2Ix(ctx.program, { + position: positionPubkey, + positionAuthority: ctx.wallet.publicKey, + tokenMintA: tokenMintAPubkey, + tokenMintB: tokenMintBPubkey, + positionTokenAccount: getAssociatedTokenAddressSync( + position.positionMint, + ctx.wallet.publicKey, + undefined, + positionMint.tokenProgram, + ), + tokenOwnerAccountA, + tokenOwnerAccountB, + tokenProgramA: mintA.tokenProgram, + tokenProgramB: mintB.tokenProgram, + tokenVaultA: whirlpool.tokenVaultA, + tokenVaultB: whirlpool.tokenVaultB, + whirlpool: whirlpoolPubkey, + tokenTransferHookAccountsA: + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + ctx.provider.connection, + mintA, + tokenOwnerAccountA, + whirlpool.tokenVaultA, + ctx.wallet.publicKey, + ), + tokenTransferHookAccountsB: + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + ctx.provider.connection, + mintB, + tokenOwnerAccountB, + whirlpool.tokenVaultB, + ctx.wallet.publicKey, + ), + }), +); + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +collect Fees... +prompt: positionPubkey: H4WEb57EYh5AhorHArjgRXVgSBJRMZi3DvsLb3J1XNj6 +collectable feeA: 0 +collectable feeB: 0 +estimatedComputeUnits: 149469 +prompt: priorityFeeInSOL: 0 +Priority fee: 0 SOL +process transaction... +transaction is still valid, 150 blocks left (at most) +sending... +confirming... +✅successfully landed +signature 3VfNAQJ8nxTStU9fjkhg5sNRPpBrAMYx5Vyp92aDS5FsWpEqgLw5Ckzzw5hJ1rsNEh6VGLaf9TZWWcLCRWzvhNjX + +*/ diff --git a/legacy-sdk/cli/src/commands/collect_rewards.ts b/legacy-sdk/cli/src/commands/collect_rewards.ts new file mode 100644 index 000000000..e42906bea --- /dev/null +++ b/legacy-sdk/cli/src/commands/collect_rewards.ts @@ -0,0 +1,171 @@ +import { PublicKey } from "@solana/web3.js"; +import { + ORCA_WHIRLPOOL_PROGRAM_ID, + PDAUtil, + WhirlpoolIx, + TickArrayUtil, + PoolUtil, + collectRewardsQuote, +} from "@orca-so/whirlpools-sdk"; +import { DecimalUtil, TransactionBuilder } from "@orca-so/common-sdk"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import { TokenExtensionUtil } from "@orca-so/whirlpools-sdk/dist/utils/public/token-extension-util"; +import { ctx } from "../utils/provider"; +import { promptText } from "../utils/prompt"; + +console.info("collect Rewards..."); + +// prompt +const positionPubkeyStr = await promptText("positionPubkey"); + +const positionPubkey = new PublicKey(positionPubkeyStr); +const position = await ctx.fetcher.getPosition(positionPubkey); +if (!position) { + throw new Error("position not found"); +} +const positionMint = await ctx.fetcher.getMintInfo(position.positionMint); +if (!positionMint) { + throw new Error("positionMint not found"); +} + +const whirlpoolPubkey = position.whirlpool; +const whirlpool = await ctx.fetcher.getPool(whirlpoolPubkey); +if (!whirlpool) { + throw new Error("whirlpool not found"); +} +const tickSpacing = whirlpool.tickSpacing; + +const rewardMintPubkeys = whirlpool.rewardInfos + .filter((r) => PoolUtil.isRewardInitialized(r)) + .map((r) => r.mint); +const rewardMints = await Promise.all( + rewardMintPubkeys.map((m) => ctx.fetcher.getMintInfo(m)), +); +if (rewardMints.some((m) => !m)) { + // extremely rare case (CloseMint extension on Token-2022 is used) + throw new Error("token mint not found"); +} + +if (rewardMints.length === 0) { + throw new Error("no rewards"); +} + +const lowerTickArrayPubkey = PDAUtil.getTickArrayFromTickIndex( + position.tickLowerIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, +).publicKey; +const upperTickArrayPubkey = PDAUtil.getTickArrayFromTickIndex( + position.tickUpperIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, +).publicKey; + +const lowerTickArray = await ctx.fetcher.getTickArray(lowerTickArrayPubkey); +const upperTickArray = await ctx.fetcher.getTickArray(upperTickArrayPubkey); +if (!lowerTickArray || !upperTickArray) { + throw new Error("tick array not found"); +} + +const quote = collectRewardsQuote({ + position, + tickLower: TickArrayUtil.getTickFromArray( + lowerTickArray, + position.tickLowerIndex, + tickSpacing, + ), + tickUpper: TickArrayUtil.getTickFromArray( + upperTickArray, + position.tickUpperIndex, + tickSpacing, + ), + whirlpool, + tokenExtensionCtx: await TokenExtensionUtil.buildTokenExtensionContext( + ctx.fetcher, + whirlpool, + ), +}); + +for (let i = 0; i < rewardMints.length; i++) { + console.info( + `collectable reward[${i}](${rewardMintPubkeys[i].toBase58()}): `, + DecimalUtil.fromBN(quote.rewardOwed[i]!, rewardMints[i]!.decimals), + ); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); + +if (position.liquidity.gtn(0)) { + builder.addInstruction( + WhirlpoolIx.updateFeesAndRewardsIx(ctx.program, { + position: positionPubkey, + tickArrayLower: lowerTickArrayPubkey, + tickArrayUpper: upperTickArrayPubkey, + whirlpool: whirlpoolPubkey, + }), + ); +} + +for (let i = 0; i < rewardMints.length; i++) { + // TODO: create if needed... + const rewardOwnerAccount = getAssociatedTokenAddressSync( + rewardMintPubkeys[i], + ctx.wallet.publicKey, + undefined, + rewardMints[i]?.tokenProgram, + ); + + builder.addInstruction( + WhirlpoolIx.collectRewardV2Ix(ctx.program, { + position: positionPubkey, + positionAuthority: ctx.wallet.publicKey, + rewardIndex: i, + rewardMint: rewardMintPubkeys[i], + rewardVault: whirlpool.rewardInfos[i].vault, + rewardTokenProgram: rewardMints[i]!.tokenProgram, + rewardOwnerAccount, + positionTokenAccount: getAssociatedTokenAddressSync( + position.positionMint, + ctx.wallet.publicKey, + undefined, + positionMint.tokenProgram, + ), + whirlpool: whirlpoolPubkey, + rewardTransferHookAccounts: + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + ctx.provider.connection, + rewardMints[i]!, + rewardOwnerAccount, + whirlpool.tokenVaultB, + ctx.wallet.publicKey, + ), + }), + ); +} + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +collect Rewards... +prompt: positionPubkey: H4WEb57EYh5AhorHArjgRXVgSBJRMZi3DvsLb3J1XNj6 +collectable reward[0](Afn8YB1p4NsoZeS5XJBZ18LTfEy5NFPwN46wapZcBQr6): 0.004849 +collectable reward[1](Jd4M8bfJG3sAkd82RsGWyEXoaBXQP7njFzBwEaCTuDa): 0.000048513 +estimatedComputeUnits: 154746 +prompt: priorityFeeInSOL: 0 +Priority fee: 0 SOL +process transaction... +transaction is still valid, 150 blocks left (at most) +sending... +confirming... +✅successfully landed +signature 4uRtXJHNQNhZC17Cryatk8ASbDABNqgskPcBouoRqUjA8P3YhbP1Z9Z25JAcJLP1wdxu9TsLwHiR7G2R3Z7oZss6 + +*/ diff --git a/legacy-sdk/cli/src/commands/decrease_liquidity.ts b/legacy-sdk/cli/src/commands/decrease_liquidity.ts new file mode 100644 index 000000000..ce009f1cb --- /dev/null +++ b/legacy-sdk/cli/src/commands/decrease_liquidity.ts @@ -0,0 +1,288 @@ +import { PublicKey } from "@solana/web3.js"; +import type { DecreaseLiquidityQuote } from "@orca-so/whirlpools-sdk"; +import { + ORCA_WHIRLPOOL_PROGRAM_ID, + PDAUtil, + WhirlpoolIx, + PoolUtil, + PriceMath, + IGNORE_CACHE, + decreaseLiquidityQuoteByLiquidityWithParams, +} from "@orca-so/whirlpools-sdk"; +import { + DecimalUtil, + Percentage, + TransactionBuilder, +} from "@orca-so/common-sdk"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import Decimal from "decimal.js"; +import { calcDepositRatio } from "../utils/deposit_ratio"; +import BN from "bn.js"; +import { TokenExtensionUtil } from "@orca-so/whirlpools-sdk/dist/utils/public/token-extension-util"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptNumber, promptText } from "../utils/prompt"; + +console.info("decrease Liquidity..."); + +// prompt +const positionPubkeyStr = await promptText("positionPubkey"); + +const positionPubkey = new PublicKey(positionPubkeyStr); +const position = await ctx.fetcher.getPosition(positionPubkey); +if (!position) { + throw new Error("position not found"); +} +const positionMint = await ctx.fetcher.getMintInfo(position.positionMint); +if (!positionMint) { + throw new Error("positionMint not found"); +} + +const whirlpoolPubkey = position.whirlpool; +const whirlpool = await ctx.fetcher.getPool(whirlpoolPubkey); +if (!whirlpool) { + throw new Error("whirlpool not found"); +} +const tickSpacing = whirlpool.tickSpacing; + +const tokenMintAPubkey = whirlpool.tokenMintA; +const tokenMintBPubkey = whirlpool.tokenMintB; +const mintA = await ctx.fetcher.getMintInfo(tokenMintAPubkey); +const mintB = await ctx.fetcher.getMintInfo(tokenMintBPubkey); +if (!mintA || !mintB) { + // extremely rare case (CloseMint extension on Token-2022 is used) + throw new Error("token mint not found"); +} +const decimalsA = mintA.decimals; +const decimalsB = mintB.decimals; +const currentPrice = PriceMath.sqrtPriceX64ToPrice( + whirlpool.sqrtPrice, + decimalsA, + decimalsB, +); + +console.info( + "tokenMintA", + tokenMintAPubkey.toBase58(), + `(${mintA.tokenProgram})`, +); +console.info( + "tokenMintB", + tokenMintBPubkey.toBase58(), + `(${mintB.tokenProgram})`, +); + +const currentBalance = PoolUtil.getTokenAmountsFromLiquidity( + position.liquidity, + whirlpool.sqrtPrice, + PriceMath.tickIndexToSqrtPriceX64(position.tickLowerIndex), + PriceMath.tickIndexToSqrtPriceX64(position.tickUpperIndex), + false, +); +console.info( + `current liquidity: ${position.liquidity} (${DecimalUtil.fromBN(currentBalance.tokenA, decimalsA).toSD(6)} A, ${DecimalUtil.fromBN(currentBalance.tokenB, decimalsB).toSD(6)} B)`, +); +console.info( + "lowerPrice(Index):", + PriceMath.tickIndexToPrice( + position.tickLowerIndex, + decimalsA, + decimalsB, + ).toSD(6), + `(${position.tickLowerIndex})`, +); +console.info( + "upperPrice(Index):", + PriceMath.tickIndexToPrice( + position.tickUpperIndex, + decimalsA, + decimalsB, + ).toSD(6), + `(${position.tickUpperIndex})`, +); + +const lowerSqrtPrice = PriceMath.tickIndexToSqrtPriceX64( + position.tickLowerIndex, +); +const upperSqrtPrice = PriceMath.tickIndexToSqrtPriceX64( + position.tickUpperIndex, +); +const depositRatio = calcDepositRatio( + whirlpool.sqrtPrice, + lowerSqrtPrice, + upperSqrtPrice, + decimalsA, + decimalsB, +); +console.info(`current price: ${currentPrice.toSD(6)} B/A`); +console.info( + `deposit ratio A:B ${depositRatio[0].toFixed(2)}% : ${depositRatio[1].toFixed(2)}%`, +); + +let decreaseLiquidityQuote: DecreaseLiquidityQuote; +while (true) { + console.info(); + const decreaseLiquidityAmountOrPercentage = await promptText( + "Please enter liquidity amount to decrease or enter percentage to decrease with % (e.g. 10%)", + ); + + let liquidityAmount: BN; + if (decreaseLiquidityAmountOrPercentage.trim().endsWith("%")) { + const percentage = new Decimal( + decreaseLiquidityAmountOrPercentage.trim().slice(0, -1), + ); + if (percentage.gt(100) || percentage.lessThanOrEqualTo(0)) { + console.info("invalid percentage"); + continue; + } + const liquidity = new Decimal(position.liquidity.toString()); + liquidityAmount = new BN( + liquidity.mul(percentage).div(100).floor().toString(), + ); + } else { + liquidityAmount = new BN(decreaseLiquidityAmountOrPercentage); + if (liquidityAmount.gt(position.liquidity)) { + console.info("too large liquidity amount"); + continue; + } + } + + const decimalSlippagePercentNum = await promptNumber( + "decimalSlippagePercent", + ); + const decimalSlippagePercent = new Decimal(decimalSlippagePercentNum); + const slippage = Percentage.fromDecimal(decimalSlippagePercent); + + const quote = await decreaseLiquidityQuoteByLiquidityWithParams({ + liquidity: liquidityAmount, + sqrtPrice: whirlpool.sqrtPrice, + tickCurrentIndex: whirlpool.tickCurrentIndex, + tickLowerIndex: position.tickLowerIndex, + tickUpperIndex: position.tickUpperIndex, + tokenExtensionCtx: await TokenExtensionUtil.buildTokenExtensionContext( + ctx.fetcher, + whirlpool, + IGNORE_CACHE, + ), + slippageTolerance: slippage, + }); + + console.info(`liquidity DELTA: ${quote.liquidityAmount.toString()}`); + console.info( + `estimated tokenA: ${DecimalUtil.fromBN(quote.tokenEstA, decimalsA).toSD(6)} at least ${DecimalUtil.fromBN(quote.tokenMinA, decimalsA).toSD(6)}`, + ); + console.info( + `estimated tokenB: ${DecimalUtil.fromBN(quote.tokenEstB, decimalsB).toSD(6)} at least ${DecimalUtil.fromBN(quote.tokenMinB, decimalsB).toSD(6)}`, + ); + + const ok = await promptConfirm("If the above is OK, enter YES"); + if (ok) { + decreaseLiquidityQuote = quote; + break; + } +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); + +const tokenOwnerAccountA = getAssociatedTokenAddressSync( + tokenMintAPubkey, + ctx.wallet.publicKey, + undefined, + mintA.tokenProgram, +); +const tokenOwnerAccountB = getAssociatedTokenAddressSync( + tokenMintBPubkey, + ctx.wallet.publicKey, + undefined, + mintB.tokenProgram, +); + +builder.addInstruction( + WhirlpoolIx.decreaseLiquidityV2Ix(ctx.program, { + liquidityAmount: decreaseLiquidityQuote.liquidityAmount, + tokenMinA: decreaseLiquidityQuote.tokenMinA, + tokenMinB: decreaseLiquidityQuote.tokenMinB, + position: positionPubkey, + positionAuthority: ctx.wallet.publicKey, + tokenMintA: tokenMintAPubkey, + tokenMintB: tokenMintBPubkey, + positionTokenAccount: getAssociatedTokenAddressSync( + position.positionMint, + ctx.wallet.publicKey, + undefined, + positionMint.tokenProgram, + ), + tickArrayLower: PDAUtil.getTickArrayFromTickIndex( + position.tickLowerIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, + ).publicKey, + tickArrayUpper: PDAUtil.getTickArrayFromTickIndex( + position.tickUpperIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, + ).publicKey, + tokenOwnerAccountA, + tokenOwnerAccountB, + tokenProgramA: mintA.tokenProgram, + tokenProgramB: mintB.tokenProgram, + tokenVaultA: whirlpool.tokenVaultA, + tokenVaultB: whirlpool.tokenVaultB, + whirlpool: whirlpoolPubkey, + tokenTransferHookAccountsA: + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + ctx.provider.connection, + mintA, + tokenOwnerAccountA, + whirlpool.tokenVaultA, + ctx.wallet.publicKey, + ), + tokenTransferHookAccountsB: + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + ctx.provider.connection, + mintB, + tokenOwnerAccountB, + whirlpool.tokenVaultB, + ctx.wallet.publicKey, + ), + }), +); + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +decrease Liquidity... +prompt: positionPubkey: H4WEb57EYh5AhorHArjgRXVgSBJRMZi3DvsLb3J1XNj6 +tokenMintA Jd4M8bfJG3sAkd82RsGWyEXoaBXQP7njFzBwEaCTuDa (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) +tokenMintB BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) +current liquidity: 31538582 (9.96839 A, 0.099783 B) +lowerPrice(Index): 0.0000000000000000544947 (-443584) +upperPrice(Index): 18350300000000000000000 (443584) +current price: 0.0100099 B/A +deposit ratio A:B 50.00% : 50.00% +Please enter liquidity amount to decrease or enter percentage to decrease with % (e.g. 10%) +prompt: decreaseLiquidityAmountOrPercentage: 50% +prompt: decimalSlippagePercent: 10 +liquidity DELTA: 15769291 +estimated tokenA: 4.98419 at least 4.53108 +estimated tokenB: 0.049891 at least 0.045355 +if the above is OK, enter OK +prompt: OK: OK +estimatedComputeUnits: 153091 +prompt: priorityFeeInSOL: 0 +Priority fee: 0 SOL +process transaction... +transaction is still valid, 150 blocks left (at most) +sending... +confirming... +✅successfully landed +signature 2XPsDXqcX936gD46MM4MsyigaFcP7u2vxFCErYTKUMUXGVHphMFdx9P6ckoVnNeVDS1TxK1C5qn4LKg4ivz9c6um + +*/ diff --git a/legacy-sdk/cli/src/commands/delete_token_badge.ts b/legacy-sdk/cli/src/commands/delete_token_badge.ts new file mode 100644 index 000000000..84e11c04b --- /dev/null +++ b/legacy-sdk/cli/src/commands/delete_token_badge.ts @@ -0,0 +1,84 @@ +import { PublicKey } from "@solana/web3.js"; +import { PDAUtil, WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; +console.info("delete TokenBadge..."); + +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); +const tokenMintStr = await promptText("tokenMint"); + +const whirlpoolsConfigPubkey = new PublicKey(whirlpoolsConfigPubkeyStr); +const tokenMint = new PublicKey(tokenMintStr); + +const pda = PDAUtil.getTokenBadge( + ctx.program.programId, + whirlpoolsConfigPubkey, + tokenMint, +); +const configExtensionPda = PDAUtil.getConfigExtension( + ctx.program.programId, + whirlpoolsConfigPubkey, +); +const configExtension = await ctx.fetcher.getConfigExtension( + configExtensionPda.publicKey, +); + +if (!configExtension) { + throw new Error("configExtension not found"); +} + +if (!configExtension.tokenBadgeAuthority.equals(ctx.wallet.publicKey)) { + throw new Error( + `the current wallet must be the token badge authority(${configExtension.tokenBadgeAuthority.toBase58()})`, + ); +} + +console.info( + "setting...", + "\n\twhirlpoolsConfig", + whirlpoolsConfigPubkey.toBase58(), + "\n\ttokenMint", + tokenMint.toBase58(), + "\n\ttokenBadge", + pda.publicKey.toBase58(), +); +const ok = await promptConfirm("If the above is OK, enter YES"); +if (!ok) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.deleteTokenBadgeIx(ctx.program, { + whirlpoolsConfigExtension: configExtensionPda.publicKey, + whirlpoolsConfig: whirlpoolsConfigPubkey, + tokenMint, + tokenBadge: pda.publicKey, + tokenBadgeAuthority: configExtension.tokenBadgeAuthority, + receiver: ctx.wallet.publicKey, + }), +); + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +delete TokenBadge... +prompt: whirlpoolsConfigPubkey: JChtLEVR9E6B5jiHTZS1Nd9WgMULMHv2UcVryYACAFYQ +prompt: tokenMint: FfprBPB2ULqp4tyBfzrzwxNvpGYoh8hidzSmiA5oDtmu +setting... + whirlpoolsConfig JChtLEVR9E6B5jiHTZS1Nd9WgMULMHv2UcVryYACAFYQ + tokenMint FfprBPB2ULqp4tyBfzrzwxNvpGYoh8hidzSmiA5oDtmu + tokenBadge FZViZVK1ANAH9Ca3SfshZRpUdSfy1qpX3KGbDBCfCJNh + +if the above is OK, enter YES +prompt: yesno: YES +tx: 1k7UNUdrVqbSbDG4XhWuLaKpxXZpGw9akz4q7iLF6ismWhtnSnJUK8san5voNCBYMFWCUyxUgYwWb3iTHBZe8Tf + +*/ diff --git a/legacy-sdk/cli/src/commands/increase_liquidity.ts b/legacy-sdk/cli/src/commands/increase_liquidity.ts new file mode 100644 index 000000000..24297b762 --- /dev/null +++ b/legacy-sdk/cli/src/commands/increase_liquidity.ts @@ -0,0 +1,324 @@ +import { PublicKey } from "@solana/web3.js"; +import type { IncreaseLiquidityQuote } from "@orca-so/whirlpools-sdk"; +import { + ORCA_WHIRLPOOL_PROGRAM_ID, + PDAUtil, + WhirlpoolIx, + PoolUtil, + PriceMath, + increaseLiquidityQuoteByInputTokenWithParams, + IGNORE_CACHE, +} from "@orca-so/whirlpools-sdk"; +import { + DecimalUtil, + Percentage, + TransactionBuilder, +} from "@orca-so/common-sdk"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import Decimal from "decimal.js"; +import { calcDepositRatio } from "../utils/deposit_ratio"; +import BN from "bn.js"; +import { TokenExtensionUtil } from "@orca-so/whirlpools-sdk/dist/utils/public/token-extension-util"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; + +console.info("increase Liquidity..."); + +// prompt +const positionPubkeyStr = await promptText("positionPubkey"); + +const positionPubkey = new PublicKey(positionPubkeyStr); +const position = await ctx.fetcher.getPosition(positionPubkey); +if (!position) { + throw new Error("position not found"); +} +const positionMint = await ctx.fetcher.getMintInfo(position.positionMint); +if (!positionMint) { + throw new Error("positionMint not found"); +} + +const whirlpoolPubkey = position.whirlpool; +const whirlpool = await ctx.fetcher.getPool(whirlpoolPubkey); +if (!whirlpool) { + throw new Error("whirlpool not found"); +} +const tickSpacing = whirlpool.tickSpacing; + +const tokenMintAPubkey = whirlpool.tokenMintA; +const tokenMintBPubkey = whirlpool.tokenMintB; +const mintA = await ctx.fetcher.getMintInfo(tokenMintAPubkey); +const mintB = await ctx.fetcher.getMintInfo(tokenMintBPubkey); +if (!mintA || !mintB) { + // extremely rare case (CloseMint extension on Token-2022 is used) + throw new Error("token mint not found"); +} +const decimalsA = mintA.decimals; +const decimalsB = mintB.decimals; +const currentPrice = PriceMath.sqrtPriceX64ToPrice( + whirlpool.sqrtPrice, + decimalsA, + decimalsB, +); + +console.info( + "tokenMintA", + tokenMintAPubkey.toBase58(), + `(${mintA.tokenProgram})`, +); +console.info( + "tokenMintB", + tokenMintBPubkey.toBase58(), + `(${mintB.tokenProgram})`, +); + +const currentBalance = PoolUtil.getTokenAmountsFromLiquidity( + position.liquidity, + whirlpool.sqrtPrice, + PriceMath.tickIndexToSqrtPriceX64(position.tickLowerIndex), + PriceMath.tickIndexToSqrtPriceX64(position.tickUpperIndex), + false, +); +console.info( + `current liquidity: ${position.liquidity} (${DecimalUtil.fromBN(currentBalance.tokenA, decimalsA).toSD(6)} A, ${DecimalUtil.fromBN(currentBalance.tokenB, decimalsB).toSD(6)} B)`, +); +console.info( + "lowerPrice(Index):", + PriceMath.tickIndexToPrice( + position.tickLowerIndex, + decimalsA, + decimalsB, + ).toSD(6), + `(${position.tickLowerIndex})`, +); +console.info( + "upperPrice(Index):", + PriceMath.tickIndexToPrice( + position.tickUpperIndex, + decimalsA, + decimalsB, + ).toSD(6), + `(${position.tickUpperIndex})`, +); + +const lowerSqrtPrice = PriceMath.tickIndexToSqrtPriceX64( + position.tickLowerIndex, +); +const upperSqrtPrice = PriceMath.tickIndexToSqrtPriceX64( + position.tickUpperIndex, +); +const depositRatio = calcDepositRatio( + whirlpool.sqrtPrice, + lowerSqrtPrice, + upperSqrtPrice, + decimalsA, + decimalsB, +); +console.info(`current price: ${currentPrice.toSD(6)} B/A`); +console.info( + `deposit ratio A:B ${depositRatio[0].toFixed(2)}% : ${depositRatio[1].toFixed(2)}%`, +); + +const balanceA = await ctx.fetcher.getTokenInfo( + getAssociatedTokenAddressSync( + tokenMintAPubkey, + ctx.wallet.publicKey, + undefined, + mintA.tokenProgram, + ), +); +const balanceB = await ctx.fetcher.getTokenInfo( + getAssociatedTokenAddressSync( + tokenMintBPubkey, + ctx.wallet.publicKey, + undefined, + mintB.tokenProgram, + ), +); + +let increaseLiquidityQuote: IncreaseLiquidityQuote; +while (true) { + let depositByA: boolean; + if (whirlpool.sqrtPrice.lte(lowerSqrtPrice)) { + depositByA = true; + } else if (whirlpool.sqrtPrice.gte(upperSqrtPrice)) { + depositByA = false; + } else { + console.info( + "current price is in the range, please specify the token to deposit (A or B)", + ); + while (true) { + const tokenAorB = await promptText("AorB"); + if (tokenAorB === "A" || tokenAorB === "B") { + depositByA = tokenAorB === "A"; + break; + } + } + } + + console.info( + `balance A: ${DecimalUtil.fromBN(new BN(balanceA!.amount.toString()), decimalsA).toSD(6)}`, + ); + console.info( + `balance B: ${DecimalUtil.fromBN(new BN(balanceB!.amount.toString()), decimalsB).toSD(6)}`, + ); + + console.info( + `Please enter the decimal amount of token ${depositByA ? "A" : "B"} to deposit`, + ); + const decimalAmountStr = await promptText("decimalAmount"); + const decimalAmount = new Decimal(decimalAmountStr); + const amount = DecimalUtil.toBN( + decimalAmount, + depositByA ? decimalsA : decimalsB, + ); + + const decimalSlippagePercentStr = await promptText("decimalSlippagePercent"); + const decimalSlippagePercent = new Decimal(decimalSlippagePercentStr); + const slippage = Percentage.fromDecimal(decimalSlippagePercent); + + const quote = await increaseLiquidityQuoteByInputTokenWithParams({ + inputTokenAmount: amount, + inputTokenMint: depositByA ? tokenMintAPubkey : tokenMintBPubkey, + sqrtPrice: whirlpool.sqrtPrice, + tickCurrentIndex: whirlpool.tickCurrentIndex, + tickLowerIndex: position.tickLowerIndex, + tickUpperIndex: position.tickUpperIndex, + tokenExtensionCtx: await TokenExtensionUtil.buildTokenExtensionContext( + ctx.fetcher, + whirlpool, + IGNORE_CACHE, + ), + tokenMintA: tokenMintAPubkey, + tokenMintB: tokenMintBPubkey, + slippageTolerance: slippage, + }); + + console.info(`estimated liquidity: ${quote.liquidityAmount.toString()}`); + console.info( + `estimated tokenA: ${DecimalUtil.fromBN(quote.tokenEstA, decimalsA).toSD(6)} at most ${DecimalUtil.fromBN(quote.tokenMaxA, decimalsA).toSD(6)}`, + ); + console.info( + `estimated tokenB: ${DecimalUtil.fromBN(quote.tokenEstB, decimalsB).toSD(6)} at most ${DecimalUtil.fromBN(quote.tokenMaxB, decimalsB).toSD(6)}`, + ); + + if ( + quote.tokenMaxA.gt(new BN(balanceA!.amount.toString())) || + quote.tokenMaxB.gt(new BN(balanceB!.amount.toString())) + ) { + throw new Error("insufficient balance"); + } + + const ok = await promptConfirm("if the above is OK, enter YES"); + if (ok) { + increaseLiquidityQuote = quote; + break; + } +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); + +const tokenOwnerAccountA = getAssociatedTokenAddressSync( + tokenMintAPubkey, + ctx.wallet.publicKey, + undefined, + mintA.tokenProgram, +); +const tokenOwnerAccountB = getAssociatedTokenAddressSync( + tokenMintBPubkey, + ctx.wallet.publicKey, + undefined, + mintB.tokenProgram, +); + +builder.addInstruction( + WhirlpoolIx.increaseLiquidityV2Ix(ctx.program, { + liquidityAmount: increaseLiquidityQuote.liquidityAmount, + tokenMaxA: increaseLiquidityQuote.tokenMaxA, + tokenMaxB: increaseLiquidityQuote.tokenMaxB, + position: positionPubkey, + positionAuthority: ctx.wallet.publicKey, + tokenMintA: tokenMintAPubkey, + tokenMintB: tokenMintBPubkey, + positionTokenAccount: getAssociatedTokenAddressSync( + position.positionMint, + ctx.wallet.publicKey, + undefined, + positionMint.tokenProgram, + ), + tickArrayLower: PDAUtil.getTickArrayFromTickIndex( + position.tickLowerIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, + ).publicKey, + tickArrayUpper: PDAUtil.getTickArrayFromTickIndex( + position.tickUpperIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, + ).publicKey, + tokenOwnerAccountA, + tokenOwnerAccountB, + tokenProgramA: mintA.tokenProgram, + tokenProgramB: mintB.tokenProgram, + tokenVaultA: whirlpool.tokenVaultA, + tokenVaultB: whirlpool.tokenVaultB, + whirlpool: whirlpoolPubkey, + tokenTransferHookAccountsA: + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + ctx.provider.connection, + mintA, + tokenOwnerAccountA, + whirlpool.tokenVaultA, + ctx.wallet.publicKey, + ), + tokenTransferHookAccountsB: + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + ctx.provider.connection, + mintB, + tokenOwnerAccountB, + whirlpool.tokenVaultB, + ctx.wallet.publicKey, + ), + }), +); + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +increase Liquidity... +prompt: positionPubkey: H4WEb57EYh5AhorHArjgRXVgSBJRMZi3DvsLb3J1XNj6 +tokenMintA Jd4M8bfJG3sAkd82RsGWyEXoaBXQP7njFzBwEaCTuDa (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) +tokenMintB BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) +current liquidity: 0 (0 A, 0 B) +lowerPrice(Index): 0.0000000000000000544947 (-443584) +upperPrice(Index): 18350300000000000000000 (443584) +deposit ratio A:B 50.00% : 50.00% +current price is in the range, please specify the token to deposit (A or B) +prompt: AorB: A +balance A: 7708.07 +balance B: 10189.3 +Please enter the decimal amount of token A to deposit +prompt: decimalAmount: 10 +prompt: decimalSlippagePercent: 1 +estimated liquidity: 31638582 +estimated tokenA: 9.99999 at most 10.0999 +estimated tokenB: 0.1001 at most 0.101101 +prompt: OK: OK +estimatedComputeUnits: 152435 +prompt: priorityFeeInSOL: 0 +Priority fee: 0 SOL +process transaction... +transaction is still valid, 150 blocks left (at most) +sending... +confirming... +✅successfully landed +signature UdEZa3GWncberjduAxCLiiQH5MTMdLuzCycN6jceXo9bUefKPGuaqnvYJc1EiAeh4VDgvfUCEKB8L5UHnJr6ZXA + +*/ diff --git a/legacy-sdk/cli/src/commands/initialize_config.ts b/legacy-sdk/cli/src/commands/initialize_config.ts new file mode 100644 index 000000000..7b9ef50ca --- /dev/null +++ b/legacy-sdk/cli/src/commands/initialize_config.ts @@ -0,0 +1,59 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import { WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptText } from "../utils/prompt"; + +console.info("initialize WhirlpoolsConfig..."); + +// prompt +const feeAuthorityPubkeyStr = await promptText("feeAuthorityPubkey"); +const collectProtocolFeesAuthorityPubkeyStr = await promptText( + "collectProtocolFeesAuthorityPubkey", +); +const rewardEmissionsSuperAuthorityPubkeyStr = await promptText( + "rewardEmissionsSuperAuthorityPubkey", +); +const defaultProtocolFeeRatePer10000Str = await promptText( + "defaultProtocolFeeRatePer10000", +); + +const configKeypair = Keypair.generate(); + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.initializeConfigIx(ctx.program, { + whirlpoolsConfigKeypair: configKeypair, + funder: ctx.wallet.publicKey, + feeAuthority: new PublicKey(feeAuthorityPubkeyStr), + collectProtocolFeesAuthority: new PublicKey( + collectProtocolFeesAuthorityPubkeyStr, + ), + rewardEmissionsSuperAuthority: new PublicKey( + rewardEmissionsSuperAuthorityPubkeyStr, + ), + defaultProtocolFeeRate: Number.parseInt(defaultProtocolFeeRatePer10000Str), + }), +); + +const landed = await sendTransaction(builder); +if (landed) { + console.info("whirlpoolsConfig address:", configKeypair.publicKey.toBase58()); +} + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +create WhirlpoolsConfig... +prompt: feeAuthorityPubkey: r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +prompt: collectProtocolFeesAuthorityPubkey: r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +prompt: rewardEmissionsSuperAuthorityPubkey: r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +prompt: defaultProtocolFeeRatePer10000: 300 +tx: 5k733gttt65s2vAuABVhVcyGMkFDKRU3MQLhmxZ1crxCaxxXn2PsucntLN6rxqz3VeAv1jPTxfZoxUbkChbDngzT +whirlpoolsConfig address: 8raEdn1tNEft7MnbMQJ1ktBqTKmHLZu7NJ7teoBkEPKm + +*/ diff --git a/legacy-sdk/cli/src/commands/initialize_config_extension.ts b/legacy-sdk/cli/src/commands/initialize_config_extension.ts new file mode 100644 index 000000000..011aad120 --- /dev/null +++ b/legacy-sdk/cli/src/commands/initialize_config_extension.ts @@ -0,0 +1,57 @@ +import { PublicKey } from "@solana/web3.js"; +import { PDAUtil, WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptText } from "../utils/prompt"; + +console.info("initialize WhirlpoolsConfigExtension..."); + +// prompt +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); + +const whirlpoolsConfigPubkey = new PublicKey(whirlpoolsConfigPubkeyStr); + +const pda = PDAUtil.getConfigExtension( + ctx.program.programId, + whirlpoolsConfigPubkey, +); +const whirlpoolsConfig = await ctx.fetcher.getConfig(whirlpoolsConfigPubkey); + +if (!whirlpoolsConfig) { + throw new Error("whirlpoolsConfig not found"); +} + +if (!whirlpoolsConfig.feeAuthority.equals(ctx.wallet.publicKey)) { + throw new Error( + `the current wallet must be the fee authority(${whirlpoolsConfig.feeAuthority.toBase58()})`, + ); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.initializeConfigExtensionIx(ctx.program, { + whirlpoolsConfig: whirlpoolsConfigPubkey, + whirlpoolsConfigExtensionPda: pda, + feeAuthority: whirlpoolsConfig.feeAuthority, + funder: ctx.wallet.publicKey, + }), +); + +const landed = await sendTransaction(builder); +if (landed) { + console.info("whirlpoolsConfigExtension address:", pda.publicKey.toBase58()); +} + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +initialize WhirlpoolsConfigExtension... +prompt: whirlpoolsConfigPubkey: JChtLEVR9E6B5jiHTZS1Nd9WgMULMHv2UcVryYACAFYQ +tx: 67TqdLX2BisDS6dAm3ofNzoFzoiC8Xu2ZH7Z3j6PSSNm2r8s3RVbBzZA64trn4D7EdZy3Rxgk4aVjKwDonDh8k3j +whirlpoolsConfigExtension address: BbTBWGoiXTbekvLbK1bKzkZEPpTBCY3bXhyf5pCoX4V3 + +*/ diff --git a/legacy-sdk/cli/src/commands/initialize_fee_tier.ts b/legacy-sdk/cli/src/commands/initialize_fee_tier.ts new file mode 100644 index 000000000..6afabf070 --- /dev/null +++ b/legacy-sdk/cli/src/commands/initialize_fee_tier.ts @@ -0,0 +1,67 @@ +import { PublicKey } from "@solana/web3.js"; +import { PDAUtil, WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptText } from "../utils/prompt"; + +console.info("initialize FeeTier..."); + +// prompt +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); +const tickSpacingStr = await promptText("tickSpacing"); +const defaultFeeRatePer1000000Str = await promptText( + "defaultFeeRatePer1000000", +); + +const whirlpoolsConfigPubkey = new PublicKey(whirlpoolsConfigPubkeyStr); +const tickSpacing = Number.parseInt(tickSpacingStr); + +const pda = PDAUtil.getFeeTier( + ctx.program.programId, + whirlpoolsConfigPubkey, + tickSpacing, +); +const whirlpoolsConfig = await ctx.fetcher.getConfig(whirlpoolsConfigPubkey); + +if (!whirlpoolsConfig) { + throw new Error("whirlpoolsConfig not found"); +} + +if (!whirlpoolsConfig.feeAuthority.equals(ctx.wallet.publicKey)) { + throw new Error( + `the current wallet must be the fee authority(${whirlpoolsConfig.feeAuthority.toBase58()})`, + ); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.initializeFeeTierIx(ctx.program, { + feeTierPda: pda, + funder: ctx.wallet.publicKey, + whirlpoolsConfig: whirlpoolsConfigPubkey, + feeAuthority: whirlpoolsConfig.feeAuthority, + tickSpacing, + defaultFeeRate: Number.parseInt(defaultFeeRatePer1000000Str), + }), +); + +const landed = await sendTransaction(builder); +if (landed) { + console.info("feeTier address:", pda.publicKey.toBase58()); +} + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +create FeeTier... +prompt: whirlpoolsConfigPubkey: 8raEdn1tNEft7MnbMQJ1ktBqTKmHLZu7NJ7teoBkEPKm +prompt: tickSpacing: 64 +prompt: defaultFeeRatePer1000000: 3000 +tx: gomSUyS88MbjVFTfTw2JPgQumVGttDYgm2Si7kqR5JYaqCgLA1fnSycRhjdAxXdfUWbpK1FZJQxKHgfNJrXgn2h +feeTier address: BYUiw9LdPsn5n8qHQhL7SNphubKtLXKwQ4tsSioP6nTj + +*/ diff --git a/legacy-sdk/cli/src/commands/initialize_reward.ts b/legacy-sdk/cli/src/commands/initialize_reward.ts new file mode 100644 index 000000000..3e8b136fb --- /dev/null +++ b/legacy-sdk/cli/src/commands/initialize_reward.ts @@ -0,0 +1,128 @@ +import { PublicKey, Keypair } from "@solana/web3.js"; +import { PDAUtil, WhirlpoolIx, PoolUtil } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; + +console.info("initialize Reward..."); + +// prompt +const whirlpoolPubkeyStr = await promptText("whirlpoolPubkey"); +const rewardTokenMintStr = await promptText("rewardTokenMint"); + +const whirlpoolPubkey = new PublicKey(whirlpoolPubkeyStr); +const rewardTokenMintPubkey = new PublicKey(rewardTokenMintStr); + +const whirlpool = await ctx.fetcher.getPool(whirlpoolPubkey); +if (!whirlpool) { + throw new Error("whirlpool not found"); +} +const rewardToken = await ctx.fetcher.getMintInfo(rewardTokenMintPubkey); +if (!rewardToken) { + throw new Error("reward token not found"); +} +const rewardTokenProgram = rewardToken.tokenProgram.equals( + TOKEN_2022_PROGRAM_ID, +) + ? "Token-2022" + : "Token"; + +const alreadyInitialized = whirlpool.rewardInfos.some((r) => + r.mint.equals(rewardTokenMintPubkey), +); +if (alreadyInitialized) { + throw new Error("reward for the token already initialized"); +} +const allInitialized = whirlpool.rewardInfos.every((r) => + PoolUtil.isRewardInitialized(r), +); +if (allInitialized) { + throw new Error( + "all rewards already initialized, no more reward can be initialized", + ); +} + +const rewardIndex = whirlpool.rewardInfos.findIndex( + (r) => !PoolUtil.isRewardInitialized(r), +); +const rewardAuthority = whirlpool.rewardInfos[rewardIndex].authority; +if (!rewardAuthority.equals(ctx.wallet.publicKey)) { + throw new Error( + `the current wallet must be the reward authority(${rewardAuthority.toBase58()})`, + ); +} + +const rewardTokenBadgePubkey = PDAUtil.getTokenBadge( + ctx.program.programId, + whirlpool.whirlpoolsConfig, + rewardTokenMintPubkey, +).publicKey; +const rewardTokenBadge = await ctx.fetcher.getTokenBadge( + rewardTokenBadgePubkey, +); +const rewardTokenBadgeInialized = !!rewardTokenBadge; +const rewardVaultKeypair = Keypair.generate(); + +console.info( + "setting...", + "\n\twhirlpool", + whirlpoolPubkey.toBase58(), + "\n\trewardIndex", + rewardIndex, + "\n\trewardToken", + rewardTokenMintPubkey.toBase58(), + `(${rewardTokenProgram})`, + rewardTokenBadgeInialized ? "with badge" : "without badge", + "\n\trewardVault(gen)", + rewardVaultKeypair.publicKey.toBase58(), +); +const yesno = await promptConfirm("if the above is OK, enter YES"); +if (!yesno) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.initializeRewardV2Ix(ctx.program, { + funder: ctx.wallet.publicKey, + rewardAuthority: ctx.wallet.publicKey, + rewardIndex, + rewardMint: rewardTokenMintPubkey, + rewardTokenBadge: rewardTokenBadgePubkey, + rewardTokenProgram: rewardToken.tokenProgram, + rewardVaultKeypair, + whirlpool: whirlpoolPubkey, + }), +); + +const landed = await sendTransaction(builder); +if (landed) { + console.info( + "initialized reward vault address:", + rewardVaultKeypair.publicKey.toBase58(), + ); +} + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +initialize Reward... +prompt: whirlpoolPubkey: 9dXKLjL2137ojWsQZALxV9mzQAb3ovwzHn6DbiV1NHZf +prompt: rewardTokenMint: FfprBPB2ULqp4tyBfzrzwxNvpGYoh8hidzSmiA5oDtmu +setting... + whirlpool 9dXKLjL2137ojWsQZALxV9mzQAb3ovwzHn6DbiV1NHZf + rewardIndex 1 + rewardToken FfprBPB2ULqp4tyBfzrzwxNvpGYoh8hidzSmiA5oDtmu (Token) with badge + rewardVault(gen) DKaGdKFMLpY3Pj4crPW2tJNVuoQLA1tarLaTDCn2ZWox + +if the above is OK, enter YES +prompt: yesno: YES +tx: 47bP5aPKGBMift8xJYK3oCnDog45UNYAcr4yJsjYKsatkb7RhNFyBshaAECM3CVpvXZNC2JkLD7rcoYSaKviz2ZD +initialized reward vault address: DKaGdKFMLpY3Pj4crPW2tJNVuoQLA1tarLaTDCn2ZWox + +*/ diff --git a/legacy-sdk/cli/src/commands/initialize_tick_array.ts b/legacy-sdk/cli/src/commands/initialize_tick_array.ts new file mode 100644 index 000000000..3512ca56a --- /dev/null +++ b/legacy-sdk/cli/src/commands/initialize_tick_array.ts @@ -0,0 +1,243 @@ +import { PublicKey } from "@solana/web3.js"; +import { + PDAUtil, + WhirlpoolIx, + PriceMath, + TickUtil, + TICK_ARRAY_SIZE, + IGNORE_CACHE, +} from "@orca-so/whirlpools-sdk"; +import type { PDA } from "@orca-so/common-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import type Decimal from "decimal.js"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptText } from "../utils/prompt"; + +console.info("initialize TickArray..."); + +// prompt +const whirlpoolPubkeyStr = await promptText("whirlpoolPubkey"); + +const whirlpoolPubkey = new PublicKey(whirlpoolPubkeyStr); +const whirlpool = await ctx.fetcher.getPool(whirlpoolPubkey); +if (!whirlpool) { + throw new Error("whirlpool not found"); +} + +const mintA = await ctx.fetcher.getMintInfo(whirlpool.tokenMintA); +const mintB = await ctx.fetcher.getMintInfo(whirlpool.tokenMintB); +const tickSpacing = whirlpool.tickSpacing; + +if (!mintA || !mintB) { + throw new Error("mintA or mintB not found"); +} + +type TickArrayInfo = { + pda: PDA; + startTickIndex: number; + startPrice: Decimal; + endPrice: Decimal; + isCurrent: boolean; + isFullRange: boolean; + isInitialized?: boolean; +}; + +const tickArrayInfos: TickArrayInfo[] = []; +const [minTickIndex, maxTickIndex] = + TickUtil.getFullRangeTickIndex(tickSpacing); +tickArrayInfos.push({ + pda: PDAUtil.getTickArrayFromTickIndex( + minTickIndex, + tickSpacing, + whirlpoolPubkey, + ctx.program.programId, + ), + startTickIndex: TickUtil.getStartTickIndex(minTickIndex, tickSpacing), + startPrice: PriceMath.tickIndexToPrice( + minTickIndex, + mintA.decimals, + mintB.decimals, + ), + endPrice: PriceMath.tickIndexToPrice( + Math.ceil(minTickIndex / (tickSpacing * TICK_ARRAY_SIZE)) * + (tickSpacing * TICK_ARRAY_SIZE), + mintA.decimals, + mintB.decimals, + ), + isCurrent: false, + isFullRange: true, +}); + +for (let offset = -6; offset <= +6; offset++) { + const startTickIndex = TickUtil.getStartTickIndex( + whirlpool.tickCurrentIndex, + tickSpacing, + offset, + ); + const pda = PDAUtil.getTickArray( + ctx.program.programId, + whirlpoolPubkey, + startTickIndex, + ); + const endTickIndex = startTickIndex + tickSpacing * TICK_ARRAY_SIZE; + const startPrice = PriceMath.tickIndexToPrice( + startTickIndex, + mintA.decimals, + mintB.decimals, + ); + const endPrice = PriceMath.tickIndexToPrice( + endTickIndex, + mintA.decimals, + mintB.decimals, + ); + + tickArrayInfos.push({ + pda, + startTickIndex, + startPrice, + endPrice, + isCurrent: offset == 0, + isFullRange: false, + }); +} + +tickArrayInfos.push({ + pda: PDAUtil.getTickArrayFromTickIndex( + maxTickIndex, + tickSpacing, + whirlpoolPubkey, + ctx.program.programId, + ), + startTickIndex: TickUtil.getStartTickIndex(maxTickIndex, tickSpacing), + startPrice: PriceMath.tickIndexToPrice( + Math.floor(maxTickIndex / (tickSpacing * TICK_ARRAY_SIZE)) * + (tickSpacing * TICK_ARRAY_SIZE), + mintA.decimals, + mintB.decimals, + ), + endPrice: PriceMath.tickIndexToPrice( + maxTickIndex, + mintA.decimals, + mintB.decimals, + ), + isCurrent: false, + isFullRange: true, +}); + +const checkInitialized = await ctx.fetcher.getTickArrays( + tickArrayInfos.map((info) => info.pda.publicKey), + IGNORE_CACHE, +); +checkInitialized.forEach((ta, i) => (tickArrayInfos[i].isInitialized = !!ta)); + +console.info("neighring tickarrays & fullrange tickarrays..."); +tickArrayInfos.forEach((ta) => + console.info( + ta.isCurrent ? ">>" : " ", + ta.pda.publicKey.toBase58().padEnd(45, " "), + ta.isInitialized ? " initialized" : "NOT INITIALIZED", + "start", + ta.startTickIndex.toString().padStart(10, " "), + "covered range", + ta.startPrice.toSignificantDigits(6), + "-", + ta.endPrice.toSignificantDigits(6), + ta.isFullRange ? "(FULL)" : "", + ), +); + +const tickArrayPubkeyStr = await promptText("tickArrayPubkey"); +const tickArrayPubkey = new PublicKey(tickArrayPubkeyStr); +const which = tickArrayInfos.filter((ta) => + ta.pda.publicKey.equals(tickArrayPubkey), +)[0]; + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.initTickArrayIx(ctx.program, { + funder: ctx.wallet.publicKey, + whirlpool: whirlpoolPubkey, + startTick: which.startTickIndex, + tickArrayPda: which.pda, + }), +); + +const landed = await sendTransaction(builder); +if (landed) { + console.info("initialized tickArray address:", tickArrayPubkey.toBase58()); +} + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +create TickArray... +prompt: whirlpoolPubkey: CJBunHdcRxtYSWGxkw8KarDpoz78KtNeMni2yU51TbPq +neighring tickarrays... + DUeNdqcFMo573Wg58GmRvYYhw3m9p5cZmzeg7kTxxLuy NOT INITIALIZED start -73216 covered range 0.661345 - 1.161477 + dufQnkLbrHTWZmbmuZGwxQgBCebKLVi2peTrdgmsq3S NOT INITIALIZED start -67584 covered range 1.161477 - 2.039827 + 9Fj1szNFbzc7hwJxuGkHmjBreAGR7Yvqnt6FCwuyTd3w NOT INITIALIZED start -61952 covered range 2.039827 - 3.582413 + 7tQ6FvFkm2PwbbyZ3tBnHyE5bqq8dw8ucnfLGBjHPbtD NOT INITIALIZED start -56320 covered range 3.582413 - 6.291557 + 6f2k4NkMBR6jhy52QTqrMca7Hb7a7ArWdP1bWgtuxYij NOT INITIALIZED start -50688 covered range 6.291557 - 11.049448 + DpNHXExUnksYhmD1tQLr25W4BJSz3Ykk4a8DN7YtYgBG NOT INITIALIZED start -45056 covered range 11.049448 - 19.405419 +>> 48W97WVPhfbLWHBP4Z5828GAWzgvmbr9YngkNbGmR7zr NOT INITIALIZED start -39424 covered range 19.405419 - 34.080460 + ck7UA3Hb68mC33hftDY5aGpyNzrTHaTu4ZShBV5yzqY NOT INITIALIZED start -33792 covered range 34.080460 - 59.853270 + BTHpoHPNh8TQq4HSoKAvxKBxee1BffyFAbxEZcxr4BYU NOT INITIALIZED start -28160 covered range 59.853270 - 105.116358 + 3CMuyPQatYQQmyXsrUMQTDAghzcecpw1qXyiR1uczFe6 NOT INITIALIZED start -22528 covered range 105.116358 - 184.608940 + By4Avt7jgymhiK5EaTzQnrDMMdzDWguEuuPLwJ1jazcS NOT INITIALIZED start -16896 covered range 184.608940 - 324.216530 + 9qKqNjLf61YfyhnDBK5Nf35e1PCxkQqRnCiApqs9uJSo NOT INITIALIZED start -11264 covered range 324.216530 - 569.400149 + EzHNpv1X8KDwugXi1ALnkzV9wpJdsShXY5DdNcgCXoc4 NOT INITIALIZED start -5632 covered range 569.400149 - 999.999999 +prompt: tickArrayPubkey: 48W97WVPhfbLWHBP4Z5828GAWzgvmbr9YngkNbGmR7zr +tx: 5cxpJC4MDxiHxh1hSjKPtVxKjLR1CzNA3As3scBZu7vLYEmc6PPgUACG5tnnh2QYSFhM2h1nCkt6Ao5PRhq5G6b1 +initialized tickArray address: 48W97WVPhfbLWHBP4Z5828GAWzgvmbr9YngkNbGmR7zr + + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +create TickArray... +prompt: whirlpoolPubkey: CJBunHdcRxtYSWGxkw8KarDpoz78KtNeMni2yU51TbPq +neighring tickarrays... + DUeNdqcFMo573Wg58GmRvYYhw3m9p5cZmzeg7kTxxLuy NOT INITIALIZED start -73216 covered range 0.661345 - 1.161477 + dufQnkLbrHTWZmbmuZGwxQgBCebKLVi2peTrdgmsq3S NOT INITIALIZED start -67584 covered range 1.161477 - 2.039827 + 9Fj1szNFbzc7hwJxuGkHmjBreAGR7Yvqnt6FCwuyTd3w NOT INITIALIZED start -61952 covered range 2.039827 - 3.582413 + 7tQ6FvFkm2PwbbyZ3tBnHyE5bqq8dw8ucnfLGBjHPbtD NOT INITIALIZED start -56320 covered range 3.582413 - 6.291557 + 6f2k4NkMBR6jhy52QTqrMca7Hb7a7ArWdP1bWgtuxYij NOT INITIALIZED start -50688 covered range 6.291557 - 11.049448 + DpNHXExUnksYhmD1tQLr25W4BJSz3Ykk4a8DN7YtYgBG NOT INITIALIZED start -45056 covered range 11.049448 - 19.405419 +>> 48W97WVPhfbLWHBP4Z5828GAWzgvmbr9YngkNbGmR7zr initialized start -39424 covered range 19.405419 - 34.080460 + ck7UA3Hb68mC33hftDY5aGpyNzrTHaTu4ZShBV5yzqY NOT INITIALIZED start -33792 covered range 34.080460 - 59.853270 + BTHpoHPNh8TQq4HSoKAvxKBxee1BffyFAbxEZcxr4BYU NOT INITIALIZED start -28160 covered range 59.853270 - 105.116358 + 3CMuyPQatYQQmyXsrUMQTDAghzcecpw1qXyiR1uczFe6 NOT INITIALIZED start -22528 covered range 105.116358 - 184.608940 + By4Avt7jgymhiK5EaTzQnrDMMdzDWguEuuPLwJ1jazcS NOT INITIALIZED start -16896 covered range 184.608940 - 324.216530 + 9qKqNjLf61YfyhnDBK5Nf35e1PCxkQqRnCiApqs9uJSo NOT INITIALIZED start -11264 covered range 324.216530 - 569.400149 + EzHNpv1X8KDwugXi1ALnkzV9wpJdsShXY5DdNcgCXoc4 NOT INITIALIZED start -5632 covered range 569.400149 - 999.999999 +prompt: tickArrayPubkey: ck7UA3Hb68mC33hftDY5aGpyNzrTHaTu4ZShBV5yzqY +tx: 34hg8C72sXYYdHdy47u3r57AKJNCyDySCzbijjcfw6kVwrSDjdFnMY743tAiMxnyp4pUByZYgxHu6a1GQR4JRjgN +initialized tickArray address: ck7UA3Hb68mC33hftDY5aGpyNzrTHaTu4ZShBV5yzqY + + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +create TickArray... +prompt: whirlpoolPubkey: CJBunHdcRxtYSWGxkw8KarDpoz78KtNeMni2yU51TbPq +neighring tickarrays... + DUeNdqcFMo573Wg58GmRvYYhw3m9p5cZmzeg7kTxxLuy NOT INITIALIZED start -73216 covered range 0.661345 - 1.161477 + dufQnkLbrHTWZmbmuZGwxQgBCebKLVi2peTrdgmsq3S NOT INITIALIZED start -67584 covered range 1.161477 - 2.039827 + 9Fj1szNFbzc7hwJxuGkHmjBreAGR7Yvqnt6FCwuyTd3w NOT INITIALIZED start -61952 covered range 2.039827 - 3.582413 + 7tQ6FvFkm2PwbbyZ3tBnHyE5bqq8dw8ucnfLGBjHPbtD NOT INITIALIZED start -56320 covered range 3.582413 - 6.291557 + 6f2k4NkMBR6jhy52QTqrMca7Hb7a7ArWdP1bWgtuxYij NOT INITIALIZED start -50688 covered range 6.291557 - 11.049448 + DpNHXExUnksYhmD1tQLr25W4BJSz3Ykk4a8DN7YtYgBG NOT INITIALIZED start -45056 covered range 11.049448 - 19.405419 +>> 48W97WVPhfbLWHBP4Z5828GAWzgvmbr9YngkNbGmR7zr initialized start -39424 covered range 19.405419 - 34.080460 + ck7UA3Hb68mC33hftDY5aGpyNzrTHaTu4ZShBV5yzqY initialized start -33792 covered range 34.080460 - 59.853270 + BTHpoHPNh8TQq4HSoKAvxKBxee1BffyFAbxEZcxr4BYU NOT INITIALIZED start -28160 covered range 59.853270 - 105.116358 + 3CMuyPQatYQQmyXsrUMQTDAghzcecpw1qXyiR1uczFe6 NOT INITIALIZED start -22528 covered range 105.116358 - 184.608940 + By4Avt7jgymhiK5EaTzQnrDMMdzDWguEuuPLwJ1jazcS NOT INITIALIZED start -16896 covered range 184.608940 - 324.216530 + 9qKqNjLf61YfyhnDBK5Nf35e1PCxkQqRnCiApqs9uJSo NOT INITIALIZED start -11264 covered range 324.216530 - 569.400149 + EzHNpv1X8KDwugXi1ALnkzV9wpJdsShXY5DdNcgCXoc4 NOT INITIALIZED start -5632 covered range 569.400149 - 999.999999 +prompt: tickArrayPubkey: DpNHXExUnksYhmD1tQLr25W4BJSz3Ykk4a8DN7YtYgBG +tx: 4xLJmdjV9UvUxotH3iE4oqXXZj2MAthP27iFN9iGSRT8CYtzKj1vK3y6sa3DtyxUqZ3JPLziKTpKvWXHQPBggStv +initialized tickArray address: DpNHXExUnksYhmD1tQLr25W4BJSz3Ykk4a8DN7YtYgBG + +*/ diff --git a/legacy-sdk/cli/src/commands/initialize_token_badge.ts b/legacy-sdk/cli/src/commands/initialize_token_badge.ts new file mode 100644 index 000000000..8d2cd0772 --- /dev/null +++ b/legacy-sdk/cli/src/commands/initialize_token_badge.ts @@ -0,0 +1,86 @@ +import { PublicKey } from "@solana/web3.js"; +import { PDAUtil, WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; + +console.info("initialize TokenBadge..."); + +// prompt +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); +const whirlpoolsConfigPubkey = new PublicKey(whirlpoolsConfigPubkeyStr); +const tokenMintStr = await promptText("tokenMint"); +const tokenMint = new PublicKey(tokenMintStr); + +const pda = PDAUtil.getTokenBadge( + ctx.program.programId, + whirlpoolsConfigPubkey, + tokenMint, +); +const configExtensionPda = PDAUtil.getConfigExtension( + ctx.program.programId, + whirlpoolsConfigPubkey, +); +const configExtension = await ctx.fetcher.getConfigExtension( + configExtensionPda.publicKey, +); + +if (!configExtension) { + throw new Error("configExtension not found"); +} + +if (!configExtension.tokenBadgeAuthority.equals(ctx.wallet.publicKey)) { + throw new Error( + `the current wallet must be the token badge authority(${configExtension.tokenBadgeAuthority.toBase58()})`, + ); +} + +console.info( + "setting...", + "\n\twhirlpoolsConfig", + whirlpoolsConfigPubkey.toBase58(), + "\n\ttokenMint", + tokenMint.toBase58(), +); +const yesno = await promptConfirm("if the above is OK, enter YES"); +if (!yesno) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.initializeTokenBadgeIx(ctx.program, { + whirlpoolsConfigExtension: configExtensionPda.publicKey, + whirlpoolsConfig: whirlpoolsConfigPubkey, + tokenMint, + tokenBadgePda: pda, + tokenBadgeAuthority: configExtension.tokenBadgeAuthority, + funder: ctx.wallet.publicKey, + }), +); + +const landed = await sendTransaction(builder); +if (landed) { + console.info("tokenBadge address:", pda.publicKey.toBase58()); +} + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +initialize TokenBadge... +prompt: whirlpoolsConfigPubkey: JChtLEVR9E6B5jiHTZS1Nd9WgMULMHv2UcVryYACAFYQ +prompt: tokenMint: FfprBPB2ULqp4tyBfzrzwxNvpGYoh8hidzSmiA5oDtmu +setting... + whirlpoolsConfig JChtLEVR9E6B5jiHTZS1Nd9WgMULMHv2UcVryYACAFYQ + tokenMint FfprBPB2ULqp4tyBfzrzwxNvpGYoh8hidzSmiA5oDtmu + +if the above is OK, enter YES +prompt: yesno: YES +tx: 5sQvVXTWHMdn9YVsWSqNCT2rCArMLz3Wazu67LETs2Hpfs4uHuWvBoKsz2RhaBwpc2DcE233DYQ4rs9PyzW88hj2 +tokenBadge address: FZViZVK1ANAH9Ca3SfshZRpUdSfy1qpX3KGbDBCfCJNh + +*/ diff --git a/legacy-sdk/cli/src/commands/initialize_whirlpool.ts b/legacy-sdk/cli/src/commands/initialize_whirlpool.ts new file mode 100644 index 000000000..6d63b9b66 --- /dev/null +++ b/legacy-sdk/cli/src/commands/initialize_whirlpool.ts @@ -0,0 +1,196 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import { + PDAUtil, + WhirlpoolIx, + PoolUtil, + PriceMath, +} from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptText, promptConfirm } from "../utils/prompt"; + +console.info("initialize Whirlpool..."); + +// prompt +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); +const tokenMint0PubkeyStr = await promptText("tokenMint0Pubkey"); +const tokenMint1PubkeyStr = await promptText("tokenMint1Pubkey"); +const tickSpacingStr = await promptText("tickSpacing"); + +const whirlpoolsConfigPubkey = new PublicKey(whirlpoolsConfigPubkeyStr); +const tokenMint0Pubkey = new PublicKey(tokenMint0PubkeyStr); +const tokenMint1Pubkey = new PublicKey(tokenMint1PubkeyStr); +const tickSpacing = Number.parseInt(tickSpacingStr); + +const [tokenMintAAddress, tokenMintBAddress] = PoolUtil.orderMints( + tokenMint0Pubkey, + tokenMint1Pubkey, +); +if (tokenMintAAddress.toString() !== tokenMint0Pubkey.toBase58()) { + console.info("token order is inverted due to order restriction"); +} + +const tokenMintAPubkey = new PublicKey(tokenMintAAddress); +const tokenMintBPubkey = new PublicKey(tokenMintBAddress); + +const feeTierPubkey = PDAUtil.getFeeTier( + ctx.program.programId, + whirlpoolsConfigPubkey, + tickSpacing, +).publicKey; + +const pda = PDAUtil.getWhirlpool( + ctx.program.programId, + whirlpoolsConfigPubkey, + tokenMintAPubkey, + tokenMintBPubkey, + tickSpacing, +); +const tokenVaultAKeypair = Keypair.generate(); +const tokenVaultBKeypair = Keypair.generate(); + +const mintA = await ctx.fetcher.getMintInfo(tokenMintAPubkey); +const mintB = await ctx.fetcher.getMintInfo(tokenMintBPubkey); + +if (!mintA || !mintB) { + throw new Error("mint not found"); +} + +const tokenProgramA = mintA.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) + ? "Token-2022" + : "Token"; +const tokenProgramB = mintB.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) + ? "Token-2022" + : "Token"; +const tokenBadgeAPubkey = PDAUtil.getTokenBadge( + ctx.program.programId, + whirlpoolsConfigPubkey, + tokenMintAPubkey, +).publicKey; +const tokenBadgeBPubkey = PDAUtil.getTokenBadge( + ctx.program.programId, + whirlpoolsConfigPubkey, + tokenMintBPubkey, +).publicKey; +const tokenBadge = await ctx.fetcher.getTokenBadges([ + tokenBadgeAPubkey, + tokenBadgeBPubkey, +]); +const tokenBadgeAInitialized = !!tokenBadge.get(tokenBadgeAPubkey.toBase58()); +const tokenBadgeBInitialized = !!tokenBadge.get(tokenBadgeBPubkey.toBase58()); + +let initTickIndex, initPrice; +while (true) { + initTickIndex = Number.parseInt(await promptText("initTickIndex")); + initPrice = PriceMath.tickIndexToPrice( + initTickIndex, + mintA.decimals, + mintB.decimals, + ); + + const ok = await promptConfirm( + `is InitPrice ${initPrice.toFixed(6)} OK ? (if it is OK, enter OK)`, + ); + if (ok) break; +} + +const initSqrtPrice = PriceMath.tickIndexToSqrtPriceX64(initTickIndex); + +console.info( + "setting...", + "\n\twhirlpoolsConfig", + whirlpoolsConfigPubkey.toBase58(), + "\n\ttokenMintA", + tokenMintAPubkey.toBase58(), + `(${tokenProgramA})`, + tokenBadgeAInitialized ? "with badge" : "without badge", + "\n\ttokenMintB", + tokenMintBPubkey.toBase58(), + `(${tokenProgramB})`, + tokenBadgeBInitialized ? "with badge" : "without badge", + "\n\ttickSpacing", + tickSpacing, + "\n\tinitPrice", + initPrice.toFixed(mintB.decimals), + "B/A", + "\n\ttokenVaultA(gen)", + tokenVaultAKeypair.publicKey.toBase58(), + "\n\ttokenVaultB(gen)", + tokenVaultBKeypair.publicKey.toBase58(), +); +const yesno = await promptConfirm("if the above is OK, enter YES"); +if (!yesno) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.initializePoolV2Ix(ctx.program, { + whirlpoolPda: pda, + funder: ctx.wallet.publicKey, + whirlpoolsConfig: whirlpoolsConfigPubkey, + tokenMintA: tokenMintAPubkey, + tokenMintB: tokenMintBPubkey, + tokenProgramA: mintA.tokenProgram, + tokenProgramB: mintB.tokenProgram, + tokenBadgeA: tokenBadgeAPubkey, + tokenBadgeB: tokenBadgeBPubkey, + tickSpacing, + feeTierKey: feeTierPubkey, + tokenVaultAKeypair, + tokenVaultBKeypair, + initSqrtPrice, + }), +); + +const landed = await sendTransaction(builder); +if (landed) { + console.info("whirlpool address:", pda.publicKey.toBase58()); +} + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +create Whirlpool... +prompt: whirlpoolsConfigPubkey: 8raEdn1tNEft7MnbMQJ1ktBqTKmHLZu7NJ7teoBkEPKm +prompt: tokenMintAPubkey: So11111111111111111111111111111111111111112 +prompt: tokenMintBPubkey: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v +prompt: feeTierPubkey: BYUiw9LdPsn5n8qHQhL7SNphubKtLXKwQ4tsSioP6nTj +prompt: initTickIndex: 0 +is InitPrice 999.999999 OK ? (if it is OK, enter OK) +prompt: OK: +prompt: initTickIndex: -1000 +is InitPrice 904.841941 OK ? (if it is OK, enter OK) +prompt: OK: +prompt: initTickIndex: -10000 +is InitPrice 367.897834 OK ? (if it is OK, enter OK) +prompt: OK: +prompt: initTickIndex: -50000 +is InitPrice 6.739631 OK ? (if it is OK, enter OK) +prompt: OK: +prompt: initTickIndex: -40000 +is InitPrice 18.319302 OK ? (if it is OK, enter OK) +prompt: OK: +prompt: initTickIndex: -38000 +is InitPrice 22.375022 OK ? (if it is OK, enter OK) +prompt: OK: OK +setting... + whirlpoolsConfig 8raEdn1tNEft7MnbMQJ1ktBqTKmHLZu7NJ7teoBkEPKm + tokenMintA So11111111111111111111111111111111111111112 + tokenMintB EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v + tickSpacing 64 + initPrice 22.375022 B/A + tokenVaultA(gen) G5JMrgXxUdjjGXPVMezddTZAr5x7L9N4Nix6ZBS1FAwB + tokenVaultB(gen) 3wwdjzY7mAsoG5yYN3Ebo58p1HdihCCSeo8Qbwx8Yg5r + +if the above is OK, enter YES +prompt: yesno: YES +tx: X7pyW22o6fi5x1YmjDEacabvbtPYrqLyXaJpv88JJ6xLBi9eXra9QhuqeYuRmLGh72NsmQ11Kf8YCe3rPzqcc9r +whirlpool address: CJBunHdcRxtYSWGxkw8KarDpoz78KtNeMni2yU51TbPq + +*/ diff --git a/legacy-sdk/cli/src/commands/open_position.ts b/legacy-sdk/cli/src/commands/open_position.ts new file mode 100644 index 000000000..231981154 --- /dev/null +++ b/legacy-sdk/cli/src/commands/open_position.ts @@ -0,0 +1,323 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import { + ORCA_WHIRLPOOL_PROGRAM_ID, + PDAUtil, + WhirlpoolIx, + PriceMath, + TickUtil, +} from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import Decimal from "decimal.js"; +import { calcDepositRatio } from "../utils/deposit_ratio"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; + +console.info("open Position..."); + +// prompt +const whirlpoolPubkeyStr = await promptText("whirlpoolPubkey"); + +const whirlpoolPubkey = new PublicKey(whirlpoolPubkeyStr); +const whirlpool = await ctx.fetcher.getPool(whirlpoolPubkey); +if (!whirlpool) { + throw new Error("whirlpool not found"); +} +const tickSpacing = whirlpool.tickSpacing; + +const tokenMintAPubkey = whirlpool.tokenMintA; +const tokenMintBPubkey = whirlpool.tokenMintB; +const mintA = await ctx.fetcher.getMintInfo(tokenMintAPubkey); +const mintB = await ctx.fetcher.getMintInfo(tokenMintBPubkey); +if (!mintA || !mintB) { + // extremely rare case (CloseMint extension on Token-2022 is used) + throw new Error("token mint not found"); +} +const decimalsA = mintA.decimals; +const decimalsB = mintB.decimals; +const currentPrice = PriceMath.sqrtPriceX64ToPrice( + whirlpool.sqrtPrice, + decimalsA, + decimalsB, +); + +console.info( + "tokenMintA", + tokenMintAPubkey.toBase58(), + `(${mintA.tokenProgram})`, +); +console.info( + "tokenMintB", + tokenMintBPubkey.toBase58(), + `(${mintB.tokenProgram})`, +); + +let lowerTickIndex: number; +let upperTickIndex: number; + +console.info(`if you want to create FULL RANGE position, enter YES`); +const fullrangeYesno = await promptConfirm("YES"); +if (fullrangeYesno) { + // RULL RANGE + const fullrange = TickUtil.getFullRangeTickIndex(tickSpacing); + lowerTickIndex = fullrange[0]; + upperTickIndex = fullrange[1]; + console.info("using full range"); +} else { + // CONCENTRATED + while (true) { + console.info(`current price: ${currentPrice.toSD(6)} B/A`); + + const lowerPriceStr = await promptText("lowerPrice"); + const upperPriceStr = await promptText("upperPrice"); + const lowerPrice = new Decimal(lowerPriceStr); + const upperPrice = new Decimal(upperPriceStr); + + const initializableLowerTickIndex = PriceMath.priceToInitializableTickIndex( + lowerPrice, + decimalsA, + decimalsB, + tickSpacing, + ); + const initializableUpperTickIndex = PriceMath.priceToInitializableTickIndex( + upperPrice, + decimalsA, + decimalsB, + tickSpacing, + ); + const initializableLowerPrice = PriceMath.tickIndexToPrice( + initializableLowerTickIndex, + decimalsA, + decimalsB, + ); + const initializableUpperPrice = PriceMath.tickIndexToPrice( + initializableUpperTickIndex, + decimalsA, + decimalsB, + ); + + const [ratioA, ratioB] = calcDepositRatio( + whirlpool.sqrtPrice, + PriceMath.tickIndexToSqrtPriceX64(initializableLowerTickIndex), + PriceMath.tickIndexToSqrtPriceX64(initializableUpperTickIndex), + decimalsA, + decimalsB, + ); + console.info( + `deposit ratio A:B ${ratioA.toFixed(2)}% : ${ratioB.toFixed(2)}%`, + ); + console.info( + `is range [${initializableLowerPrice.toSD(6)}, ${initializableUpperPrice.toSD(6)}] OK ? (if it is OK, enter OK)`, + ); + const ok = await promptConfirm("OK"); + if (ok) { + lowerTickIndex = initializableLowerTickIndex; + upperTickIndex = initializableUpperTickIndex; + break; + } + } +} + +const lowerTickArrayPda = PDAUtil.getTickArrayFromTickIndex( + lowerTickIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, +); +const upperTickArrayPda = PDAUtil.getTickArrayFromTickIndex( + upperTickIndex, + tickSpacing, + whirlpoolPubkey, + ORCA_WHIRLPOOL_PROGRAM_ID, +); +const lowerTickArray = await ctx.fetcher.getTickArray( + lowerTickArrayPda.publicKey, +); +const upperTickArray = await ctx.fetcher.getTickArray( + upperTickArrayPda.publicKey, +); +const initLowerTickArray = !lowerTickArray; +const initUpperTickArray = !upperTickArray; + +console.info(`if you want to create position with Metadata, enter YES`); +const withMetadataYesno = await promptConfirm("YES"); + +console.info( + `if you want to create position WITHOUT TokenExtensions, enter YES`, +); +const withoutTokenExtensions = await promptConfirm("YES"); + +console.info( + "setting...", + "\n\twhirlpool", + whirlpoolPubkey.toBase58(), + "\n\ttokenMintA", + tokenMintAPubkey.toBase58(), + "\n\ttokenMintB", + tokenMintBPubkey.toBase58(), + "\n\ttickSpacing", + tickSpacing, + "\n\tcurrentPrice", + currentPrice.toSD(6), + "B/A", + "\n\tlowerPrice(Index)", + PriceMath.tickIndexToPrice(lowerTickIndex, decimalsA, decimalsB).toSD(6), + `(${lowerTickIndex})`, + "\n\tupperPrice(Index)", + PriceMath.tickIndexToPrice(upperTickIndex, decimalsA, decimalsB).toSD(6), + `(${upperTickIndex})`, + "\n\tlowerTickArray", + lowerTickArrayPda.publicKey.toBase58(), + initLowerTickArray ? "(TO BE INITIALIZED)" : "(initialized)", + "\n\tupperTickArray", + upperTickArrayPda.publicKey.toBase58(), + initUpperTickArray ? "(TO BE INITIALIZED)" : "(initialized)", + "\n\twithMetadata", + withMetadataYesno ? "WITH metadata" : "WITHOUT metadata", + "\n\twithTokenExtensions", + !withoutTokenExtensions ? "WITH TokenExtensions" : "WITHOUT TokenExtensions", +); +const yesno = await promptConfirm("if the above is OK, enter YES"); +if (!yesno) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); + +if (initLowerTickArray) { + builder.addInstruction( + WhirlpoolIx.initTickArrayIx(ctx.program, { + whirlpool: whirlpoolPubkey, + funder: ctx.wallet.publicKey, + startTick: TickUtil.getStartTickIndex(lowerTickIndex, tickSpacing), + tickArrayPda: lowerTickArrayPda, + }), + ); +} + +if (initUpperTickArray) { + builder.addInstruction( + WhirlpoolIx.initTickArrayIx(ctx.program, { + whirlpool: whirlpoolPubkey, + funder: ctx.wallet.publicKey, + startTick: TickUtil.getStartTickIndex(upperTickIndex, tickSpacing), + tickArrayPda: upperTickArrayPda, + }), + ); +} + +const positionMintKeypair = Keypair.generate(); +const positionPda = PDAUtil.getPosition( + ORCA_WHIRLPOOL_PROGRAM_ID, + positionMintKeypair.publicKey, +); +if (!withoutTokenExtensions) { + // TokenExtensions based Position NFT + builder.addInstruction( + WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, { + funder: ctx.wallet.publicKey, + whirlpool: whirlpoolPubkey, + tickLowerIndex: lowerTickIndex, + tickUpperIndex: upperTickIndex, + withTokenMetadataExtension: withMetadataYesno, + owner: ctx.wallet.publicKey, + positionMint: positionMintKeypair.publicKey, + positionPda, + positionTokenAccount: getAssociatedTokenAddressSync( + positionMintKeypair.publicKey, + ctx.wallet.publicKey, + undefined, + TOKEN_2022_PROGRAM_ID, + ), + }), + ); +} else { + // TokenProgram based Position NFT + + const metadataPda = PDAUtil.getPositionMetadata( + positionMintKeypair.publicKey, + ); + const params = { + funder: ctx.wallet.publicKey, + whirlpool: whirlpoolPubkey, + tickLowerIndex: lowerTickIndex, + tickUpperIndex: upperTickIndex, + owner: ctx.wallet.publicKey, + positionMintAddress: positionMintKeypair.publicKey, + positionPda, + positionTokenAccount: getAssociatedTokenAddressSync( + positionMintKeypair.publicKey, + ctx.wallet.publicKey, + ), + metadataPda, + }; + + if (withMetadataYesno) { + builder.addInstruction( + WhirlpoolIx.openPositionWithMetadataIx(ctx.program, params), + ); + } else { + builder.addInstruction(WhirlpoolIx.openPositionIx(ctx.program, params)); + } +} +builder.addSigner(positionMintKeypair); + +const landed = await sendTransaction(builder); +if (landed) { + console.info( + "position mint address:", + positionMintKeypair.publicKey.toBase58(), + ); + console.info("position address:", positionPda.publicKey.toBase58()); + console.info( + "📝position liquidity is empty, please use yarn run increaseLiquidity to deposit", + ); +} + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +open Position... +prompt: whirlpoolPubkey: EgxU92G34jw6QDG9RuTX9StFg1PmHuDqkRKAE5kVEiZ4 +tokenMintA Jd4M8bfJG3sAkd82RsGWyEXoaBXQP7njFzBwEaCTuDa (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) +tokenMintB BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) +if you want to create FULL RANGE position, enter YES +prompt: yesno: YES +using full range +if you want to create position with Metadata, enter YES +prompt: yesno: no +setting... + whirlpool EgxU92G34jw6QDG9RuTX9StFg1PmHuDqkRKAE5kVEiZ4 + tokenMintA Jd4M8bfJG3sAkd82RsGWyEXoaBXQP7njFzBwEaCTuDa + tokenMintB BRjpCHtyQLNCo8gqRUr8jtdAj5AjPYQaoqbvcZiHok1k + tickSpacing 64 + currentPrice 0.0100099 B/A + lowerPrice(Index) 0.0000000000000000544947 (-443584) + upperPrice(Index) 18350300000000000000000 (443584) + lowerTickArray AihMywzP74pU2riq1ihFW2YSVcc1itT3yiP7minvkxDs (initialized) + upperTickArray F4h3qr6uBgdLDJyTms4YiebiaiuCEvC5C9LJE8scA1LV (initialized) + withMetadata WITHOUT metadata + +if the above is OK, enter YES +prompt: yesno: YES +estimatedComputeUnits: 163606 +prompt: priorityFeeInSOL: 0 +Priority fee: 0 SOL +process transaction... +transaction is still valid, 150 blocks left (at most) +sending... +confirming... +✅successfully landed +signature CAY5wBXVhbHRVjJYgi8c1KS5XczLDwZB1S7sf5fwkCakXo8SQBtasvjBvtjwpdZUoQnwJoPmhG2ZrPGX3PRQ8ax +position mint address: 8nBbX74FraqPuoL8AwXxPiPaULg8CP8hUJ41hJGAx4nb +position address: H4WEb57EYh5AhorHArjgRXVgSBJRMZi3DvsLb3J1XNj6 +📝position liquidity is empty, please use yarn run increaseLiquidity to deposit + +*/ diff --git a/legacy-sdk/cli/src/commands/push_price.ts b/legacy-sdk/cli/src/commands/push_price.ts new file mode 100644 index 000000000..d89d3108a --- /dev/null +++ b/legacy-sdk/cli/src/commands/push_price.ts @@ -0,0 +1,316 @@ +import { PublicKey } from "@solana/web3.js"; +import type { SwapV2Params } from "@orca-so/whirlpools-sdk"; +import { + ORCA_WHIRLPOOL_PROGRAM_ID, + PDAUtil, + WhirlpoolIx, + PriceMath, + TICK_ARRAY_SIZE, + MAX_TICK_INDEX, + MIN_TICK_INDEX, + SwapUtils, + IGNORE_CACHE, + swapQuoteWithParams, + TokenExtensionUtil, + PREFER_CACHE, +} from "@orca-so/whirlpools-sdk"; +import { + DecimalUtil, + Percentage, + TransactionBuilder, + U64_MAX, +} from "@orca-so/common-sdk"; +import BN from "bn.js"; +import { getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { sendTransaction } from "../utils/transaction_sender"; +import Decimal from "decimal.js"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; + +const SIGNIFICANT_DIGITS = 9; + +console.info("try to push pool price..."); + +// prompt +const whirlpoolPubkeyStr = await promptText("whirlpoolPubkey"); + +const whirlpoolPubkey = new PublicKey(whirlpoolPubkeyStr); +const whirlpool = await ctx.fetcher.getPool(whirlpoolPubkey); +if (!whirlpool) { + throw new Error("whirlpool not found"); +} +const tickSpacing = whirlpool.tickSpacing; + +const tokenMintAPubkey = whirlpool.tokenMintA; +const tokenMintBPubkey = whirlpool.tokenMintB; +const mintA = await ctx.fetcher.getMintInfo(tokenMintAPubkey); +const mintB = await ctx.fetcher.getMintInfo(tokenMintBPubkey); +if (!mintA || !mintB) { + // extremely rare case (CloseMint extension on Token-2022 is used) + throw new Error("token mint not found"); +} +const decimalsA = mintA.decimals; +const decimalsB = mintB.decimals; +const currentPrice = PriceMath.sqrtPriceX64ToPrice( + whirlpool.sqrtPrice, + decimalsA, + decimalsB, +); + +console.info( + "tokenMintA", + tokenMintAPubkey.toBase58(), + `(${mintA.tokenProgram})`, +); +console.info( + "tokenMintB", + tokenMintBPubkey.toBase58(), + `(${mintB.tokenProgram})`, +); + +const currentTickIndex = whirlpool.tickCurrentIndex; + +// yeah, there is obviously edge-case, but it is okay because this is just a dev tool +const ticksInArray = tickSpacing * TICK_ARRAY_SIZE; +const targetTickIndexMax = Math.min( + Math.ceil(currentTickIndex / ticksInArray) * ticksInArray + + 2 * ticksInArray - + 1, + MAX_TICK_INDEX, +); +const targetTickIndexMin = Math.max( + Math.floor(currentTickIndex / ticksInArray) * ticksInArray - 2 * ticksInArray, + MIN_TICK_INDEX, +); + +const targetPriceMax = PriceMath.tickIndexToPrice( + targetTickIndexMax, + decimalsA, + decimalsB, +); +const targetPriceMin = PriceMath.tickIndexToPrice( + targetTickIndexMin, + decimalsA, + decimalsB, +); +const targetSqrtPriceMax = PriceMath.priceToSqrtPriceX64( + targetPriceMax, + decimalsA, + decimalsB, +); +const targetSqrtPriceMin = PriceMath.priceToSqrtPriceX64( + targetPriceMin, + decimalsA, + decimalsB, +); + +let targetSqrtPrice: BN; +while (true) { + console.info(`current price: ${currentPrice.toSD(SIGNIFICANT_DIGITS)} B/A`); + console.info( + `available target price range: ${targetPriceMin.toSD(SIGNIFICANT_DIGITS)} B/A ~ ${targetPriceMax.toSD(SIGNIFICANT_DIGITS)} B/A`, + ); + + const targetPriceStr = await promptText("targetPrice"); + const targetPrice = new Decimal(targetPriceStr); + targetSqrtPrice = PriceMath.priceToSqrtPriceX64( + targetPrice, + decimalsA, + decimalsB, + ); + + if ( + targetSqrtPrice.lt(targetSqrtPriceMin) || + targetSqrtPrice.gt(targetSqrtPriceMax) + ) { + console.info("invalid target price"); + continue; + } + + console.info(`target price: ${targetPrice.toSD(SIGNIFICANT_DIGITS)} B/A`); + console.info(`is target price OK ? (if it is OK, enter OK)`); + const ok = await promptConfirm("OK"); + if (ok) { + break; + } +} + +// get swap quote +const aToB = targetSqrtPrice.lt(whirlpool.sqrtPrice); +const inputToken = aToB ? mintA : mintB; +const outputToken = aToB ? mintB : mintA; + +const tickArrays = await SwapUtils.getTickArrays( + whirlpool.tickCurrentIndex, + tickSpacing, + aToB, + ORCA_WHIRLPOOL_PROGRAM_ID, + whirlpoolPubkey, + ctx.fetcher, + IGNORE_CACHE, +); + +const tokenExtensionCtx = + await TokenExtensionUtil.buildTokenExtensionContextForPool( + ctx.fetcher, + tokenMintAPubkey, + tokenMintBPubkey, + PREFER_CACHE, + ); +const quote = swapQuoteWithParams( + { + aToB, + amountSpecifiedIsInput: true, + otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold(true), + tickArrays, + whirlpoolData: whirlpool, + tokenExtensionCtx, + + // use too much input to estimate required input amount + tokenAmount: U64_MAX, + sqrtPriceLimit: targetSqrtPrice, + }, + Percentage.fromFraction(1, 1000), +); // 0.1% slippage + +console.info("aToB", quote.aToB); +console.info( + "estimatedAmountIn", + DecimalUtil.fromBN(quote.estimatedAmountIn, inputToken.decimals).toString(), + aToB ? "A" : "B", +); +console.info( + "estimatedAmountOut", + DecimalUtil.fromBN(quote.estimatedAmountOut, outputToken.decimals).toString(), + aToB ? "B" : "A", +); + +// ok prompt +console.info(`OK ? (if it is OK, enter OK)`); +const ok = await promptConfirm("OK"); +if (!ok) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); + +const tokenOwnerAccountA = getAssociatedTokenAddressSync( + tokenMintAPubkey, + ctx.wallet.publicKey, + undefined, + mintA.tokenProgram, +); +const tokenOwnerAccountB = getAssociatedTokenAddressSync( + tokenMintBPubkey, + ctx.wallet.publicKey, + undefined, + mintB.tokenProgram, +); +const swapV2Params: SwapV2Params = { + amount: quote.amount, + amountSpecifiedIsInput: quote.amountSpecifiedIsInput, + aToB: quote.aToB, + sqrtPriceLimit: targetSqrtPrice, + otherAmountThreshold: quote.otherAmountThreshold, + tokenAuthority: ctx.wallet.publicKey, + tokenMintA: tokenMintAPubkey, + tokenMintB: tokenMintBPubkey, + tokenOwnerAccountA, + tokenOwnerAccountB, + tokenVaultA: whirlpool.tokenVaultA, + tokenVaultB: whirlpool.tokenVaultB, + whirlpool: whirlpoolPubkey, + tokenProgramA: mintA.tokenProgram, + tokenProgramB: mintB.tokenProgram, + oracle: PDAUtil.getOracle(ctx.program.programId, whirlpoolPubkey).publicKey, + tickArray0: quote.tickArray0, + tickArray1: quote.tickArray1, + tickArray2: quote.tickArray2, + ...TokenExtensionUtil.getExtraAccountMetasForTransferHookForPool( + ctx.connection, + tokenExtensionCtx, + // hmm, why I didn't make Utility class more convenient ... ? + aToB ? tokenOwnerAccountA : whirlpool.tokenVaultA, + aToB ? whirlpool.tokenVaultA : tokenOwnerAccountA, + aToB ? ctx.wallet.publicKey : whirlpoolPubkey, + aToB ? whirlpool.tokenVaultB : tokenOwnerAccountB, + aToB ? tokenOwnerAccountB : whirlpool.tokenVaultB, + aToB ? whirlpoolPubkey : ctx.wallet.publicKey, + ), +}; + +if (quote.estimatedAmountIn.isZero() && quote.estimatedAmountOut.isZero()) { + // push empty pool price + builder.addInstruction( + WhirlpoolIx.swapV2Ix(ctx.program, { + ...swapV2Params, + // partial fill (intentional) + amount: new BN(1), + amountSpecifiedIsInput: false, + sqrtPriceLimit: targetSqrtPrice, + otherAmountThreshold: new BN(1), + }), + ); +} else { + builder.addInstruction(WhirlpoolIx.swapV2Ix(ctx.program, swapV2Params)); +} + +const landed = await sendTransaction(builder); +if (landed) { + const postWhirlpool = await ctx.fetcher.getPool( + whirlpoolPubkey, + IGNORE_CACHE, + ); + if (!postWhirlpool) { + throw new Error("whirlpool not found"); + } + const updatedPrice = PriceMath.sqrtPriceX64ToPrice( + postWhirlpool.sqrtPrice, + decimalsA, + decimalsB, + ); + // if arb bot executed opposite trade, the price will be reverted + console.info( + "updated current price", + updatedPrice.toSD(SIGNIFICANT_DIGITS), + "B/A", + ); +} + +/* + +SAMPLE EXECUTION LOG + +$ yarn run pushPrice +yarn run v1.22.22 +$ npx ts-node src/push_price.ts +connection endpoint https://orca.mainnet.eclipse.rpcpool.com/xxxxxxxxxxxxxx +wallet EvQdhqCLKc6Sh6mxvzF7pMrCjhnMzsJCvXEmYfu18Boj +try to push pool price... +prompt: whirlpoolPubkey: 44w4HrojzxKwxEb3bmjRNcJ4irFhUGBUjrCYecYhPvqq +tokenMintA So11111111111111111111111111111111111111112 (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) +tokenMintB AKEWE7Bgh87GPp171b4cJPSSZfmZwQ3KaqYqXoKLNAEE (TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb) +current price: 1054.7755 B/A +available target price range: 868.669141 B/A ~ 1235.02281 B/A +prompt: targetPrice: 1080 +target price: 1080 B/A +is target price OK ? (if it is OK, enter OK) +prompt: OK: OK +aToB false +estimatedAmountIn 0.333295 B +estimatedAmountOut 0.00031196 A +OK ? (if it is OK, enter OK) +prompt: OK: OK +estimatedComputeUnits: 176958 +prompt: priorityFeeInSOL: 0.0000001 +Priority fee: 1e-7 SOL +process transaction... +transaction is still valid, 151 blocks left (at most) +sending... +confirming... +✅successfully landed +signature 23KANyU2dQCowps4HEstxHsqZMJS8GYJV7hb6NZhrVPQfmk1aRHFHMVMdxY1sVcEsbTe37ozqd513orzH7fZHnJP +updated current price 1079.99999 B/A +✨ Done in 49.55s. + +*/ diff --git a/legacy-sdk/cli/src/commands/set_collect_protocol_fees_authority.ts b/legacy-sdk/cli/src/commands/set_collect_protocol_fees_authority.ts new file mode 100644 index 000000000..6f48aa0e3 --- /dev/null +++ b/legacy-sdk/cli/src/commands/set_collect_protocol_fees_authority.ts @@ -0,0 +1,106 @@ +import { PublicKey } from "@solana/web3.js"; +import { WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; + +console.info("set CollectProtocolFeesAuthority..."); + +// prompt +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); +const newCollectProtocolFeesAuthorityPubkeyStr = await promptText( + "newCollectProtocolFeesAuthorityPubkey", +); +const newCollectProtocolFeesAuthorityPubkeyAgainStr = await promptText( + "newCollectProtocolFeesAuthorityPubkeyAgain", +); + +const whirlpoolsConfigPubkey = new PublicKey(whirlpoolsConfigPubkeyStr); +const newCollectProtocolFeesAuthorityPubkey = new PublicKey( + newCollectProtocolFeesAuthorityPubkeyStr, +); +const newCollectProtocolFeesAuthorityPubkeyAgain = new PublicKey( + newCollectProtocolFeesAuthorityPubkeyAgainStr, +); + +if ( + !newCollectProtocolFeesAuthorityPubkey.equals( + newCollectProtocolFeesAuthorityPubkeyAgain, + ) +) { + throw new Error( + "newCollectProtocolFeesAuthorityPubkey and newCollectProtocolFeesAuthorityPubkeyAgain must be the same", + ); +} + +const whirlpoolsConfig = await ctx.fetcher.getConfig(whirlpoolsConfigPubkey); +if (!whirlpoolsConfig) { + throw new Error("whirlpoolsConfig not found"); +} + +if ( + !whirlpoolsConfig.collectProtocolFeesAuthority.equals(ctx.wallet.publicKey) +) { + throw new Error( + `the current wallet must be the collect protocol fees authority(${whirlpoolsConfig.collectProtocolFeesAuthority.toBase58()})`, + ); +} + +console.info( + "setting...", + "\n\tcollectProtocolFeesAuthority", + whirlpoolsConfig.collectProtocolFeesAuthority.toBase58(), + "\n\tnewCollectProtocolFeesAuthority", + newCollectProtocolFeesAuthorityPubkey.toBase58(), +); +console.info("\nif the above is OK, enter YES"); +console.info( + "\n>>>>> WARNING: authority transfer is highly sensitive operation, please double check new authority address <<<<<\n", +); +const yesno = await promptConfirm("yesno"); +if (!yesno) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.setCollectProtocolFeesAuthorityIx(ctx.program, { + whirlpoolsConfig: whirlpoolsConfigPubkey, + collectProtocolFeesAuthority: whirlpoolsConfig.collectProtocolFeesAuthority, + newCollectProtocolFeesAuthority: newCollectProtocolFeesAuthorityPubkey, + }), +); + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet 2v112XbwQXFrdqX438HUrfZF91qCZb7QRP4bwUiN7JF5 +set CollectProtocolFeesAuthority... +prompt: whirlpoolsConfigPubkey: FcrweFY1G9HJAHG5inkGB6pKg1HZ6x9UC2WioAfWrGkR +prompt: newCollectProtocolFeesAuthorityPubkey: 3otH3AHWqkqgSVfKFkrxyDqd2vK6LcaqigHrFEmWcGuo +prompt: newCollectProtocolFeesAuthorityPubkeyAgain: 3otH3AHWqkqgSVfKFkrxyDqd2vK6LcaqigHrFEmWcGuo +setting... + collectProtocolFeesAuthority 2v112XbwQXFrdqX438HUrfZF91qCZb7QRP4bwUiN7JF5 + newCollectProtocolFeesAuthority 3otH3AHWqkqgSVfKFkrxyDqd2vK6LcaqigHrFEmWcGuo + +if the above is OK, enter YES + +>>>>> WARNING: authority transfer is highly sensitive operation, please double check new authority address <<<<< + +prompt: yesno: YES +estimatedComputeUnits: 102679 +prompt: priorityFeeInSOL: 0.000005 +Priority fee: 0.000005 SOL +process transaction... +transaction is still valid, 151 blocks left (at most) +sending... +confirming... +✅successfully landed +signature WNmYAhcYSoiJgJRveeKijsA77w1i68eD7iYjZzmEtwAf7VnzLqZYcFZk1acCzY5Qt3rMXNjnS6Xvd8mFNtyWmas + +*/ diff --git a/legacy-sdk/cli/src/commands/set_default_protocol_fee_rate.ts b/legacy-sdk/cli/src/commands/set_default_protocol_fee_rate.ts new file mode 100644 index 000000000..c5a7eb354 --- /dev/null +++ b/legacy-sdk/cli/src/commands/set_default_protocol_fee_rate.ts @@ -0,0 +1,26 @@ +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { PublicKey } from "@solana/web3.js"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptText } from "../utils/prompt"; + +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); +const feeAuthorityPubkeyStr = await promptText("feeAuthorityPubkey"); +const defaultProtocolFeeRatePer10000Str = await promptText( + "defaultProtocolFeeRatePer10000", +); + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.setDefaultProtocolFeeRateIx(ctx.program, { + whirlpoolsConfig: new PublicKey(whirlpoolsConfigPubkeyStr), + feeAuthority: new PublicKey(feeAuthorityPubkeyStr), + defaultProtocolFeeRate: Number.parseInt(defaultProtocolFeeRatePer10000Str), + }), +); + +const landed = await sendTransaction(builder); +if (landed) { + console.info("tx landed"); +} diff --git a/legacy-sdk/cli/src/commands/set_fee_authority.ts b/legacy-sdk/cli/src/commands/set_fee_authority.ts new file mode 100644 index 000000000..9fd74786d --- /dev/null +++ b/legacy-sdk/cli/src/commands/set_fee_authority.ts @@ -0,0 +1,93 @@ +import { PublicKey } from "@solana/web3.js"; +import { WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; + +console.info("set FeeAuthority..."); + +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); +const newFeeAuthorityPubkeyStr = await promptText("newFeeAuthorityPubkey"); +const newFeeAuthorityPubkeyAgainStr = await promptText( + "newFeeAuthorityPubkeyAgain", +); + +const whirlpoolsConfigPubkey = new PublicKey(whirlpoolsConfigPubkeyStr); +const newFeeAuthorityPubkey = new PublicKey(newFeeAuthorityPubkeyStr); +const newFeeAuthorityPubkeyAgain = new PublicKey(newFeeAuthorityPubkeyAgainStr); + +if (!newFeeAuthorityPubkey.equals(newFeeAuthorityPubkeyAgain)) { + throw new Error( + "newFeeAuthorityPubkey and newFeeAuthorityPubkeyAgain must be the same", + ); +} + +const whirlpoolsConfig = await ctx.fetcher.getConfig(whirlpoolsConfigPubkey); +if (!whirlpoolsConfig) { + throw new Error("whirlpoolsConfig not found"); +} + +if (!whirlpoolsConfig.feeAuthority.equals(ctx.wallet.publicKey)) { + throw new Error( + `the current wallet must be the fee authority(${whirlpoolsConfig.feeAuthority.toBase58()})`, + ); +} + +console.info( + "setting...", + "\n\tfeeAuthority", + whirlpoolsConfig.feeAuthority.toBase58(), + "\n\tnewFeeAuthority", + newFeeAuthorityPubkey.toBase58(), +); +console.info("\nif the above is OK, enter YES"); +console.info( + "\n>>>>> WARNING: authority transfer is highly sensitive operation, please double check new authority address <<<<<\n", +); +const yesno = await promptConfirm("yesno"); +if (!yesno) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.setFeeAuthorityIx(ctx.program, { + whirlpoolsConfig: whirlpoolsConfigPubkey, + feeAuthority: whirlpoolsConfig.feeAuthority, + newFeeAuthority: newFeeAuthorityPubkey, + }), +); + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet 2v112XbwQXFrdqX438HUrfZF91qCZb7QRP4bwUiN7JF5 +set FeeAuthority... +prompt: whirlpoolsConfigPubkey: FcrweFY1G9HJAHG5inkGB6pKg1HZ6x9UC2WioAfWrGkR +prompt: newFeeAuthorityPubkey: 3otH3AHWqkqgSVfKFkrxyDqd2vK6LcaqigHrFEmWcGuo +prompt: newFeeAuthorityPubkeyAgain: 3otH3AHWqkqgSVfKFkrxyDqd2vK6LcaqigHrFEmWcGuo +setting... + feeAuthority 2v112XbwQXFrdqX438HUrfZF91qCZb7QRP4bwUiN7JF5 + newFeeAuthority 3otH3AHWqkqgSVfKFkrxyDqd2vK6LcaqigHrFEmWcGuo + +if the above is OK, enter YES + +>>>>> WARNING: authority transfer is highly sensitive operation, please double check new authority address <<<<< + +prompt: yesno: YES +estimatedComputeUnits: 102611 +prompt: priorityFeeInSOL: 0.000005 +Priority fee: 0.000005 SOL +process transaction... +transaction is still valid, 151 blocks left (at most) +sending... +confirming... +✅successfully landed +signature 5Z75rUDcMkXS5sUz45rKNLVMbniBtEzLdU3LC1mHmQymSUBYEzZTHAmmeE6gxzRTHtmmp9AWVWvM9MPYYNsGWTyq + +*/ diff --git a/legacy-sdk/cli/src/commands/set_token_badge_authority.ts b/legacy-sdk/cli/src/commands/set_token_badge_authority.ts new file mode 100644 index 000000000..f11e63f02 --- /dev/null +++ b/legacy-sdk/cli/src/commands/set_token_badge_authority.ts @@ -0,0 +1,85 @@ +import { PublicKey } from "@solana/web3.js"; +import { PDAUtil, WhirlpoolIx } from "@orca-so/whirlpools-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import { sendTransaction } from "../utils/transaction_sender"; +import { ctx } from "../utils/provider"; +import { promptConfirm, promptText } from "../utils/prompt"; + +console.info("set TokenBadgeAuthority..."); + +const whirlpoolsConfigPubkeyStr = await promptText("whirlpoolsConfigPubkey"); +const newTokenBadgeAuthorityPubkeyStr = await promptText( + "newTokenBadgeAuthorityPubkey", +); + +const whirlpoolsConfigPubkey = new PublicKey(whirlpoolsConfigPubkeyStr); +const newTokenBadgeAuthorityPubkey = new PublicKey( + newTokenBadgeAuthorityPubkeyStr, +); + +const pda = PDAUtil.getConfigExtension( + ctx.program.programId, + whirlpoolsConfigPubkey, +); +const whirlpoolsConfigExtension = await ctx.fetcher.getConfigExtension( + pda.publicKey, +); + +if (!whirlpoolsConfigExtension) { + throw new Error("whirlpoolsConfigExtension not found"); +} + +if ( + !whirlpoolsConfigExtension.configExtensionAuthority.equals( + ctx.wallet.publicKey, + ) +) { + throw new Error( + `the current wallet must be the config extension authority(${whirlpoolsConfigExtension.configExtensionAuthority.toBase58()})`, + ); +} + +console.info( + "setting...", + "\n\ttokenBadgeAuthority", + whirlpoolsConfigExtension.tokenBadgeAuthority.toBase58(), + "\n\tnewTokenBadgeAuthority", + newTokenBadgeAuthorityPubkey.toBase58(), +); +console.info("\nif the above is OK, enter YES"); +const yesno = await promptConfirm("yesno"); +if (!yesno) { + throw new Error("stopped"); +} + +const builder = new TransactionBuilder(ctx.connection, ctx.wallet); +builder.addInstruction( + WhirlpoolIx.setTokenBadgeAuthorityIx(ctx.program, { + whirlpoolsConfig: whirlpoolsConfigPubkey, + whirlpoolsConfigExtension: pda.publicKey, + configExtensionAuthority: + whirlpoolsConfigExtension.configExtensionAuthority, + newTokenBadgeAuthority: newTokenBadgeAuthorityPubkey, + }), +); + +await sendTransaction(builder); + +/* + +SAMPLE EXECUTION LOG + +connection endpoint http://localhost:8899 +wallet r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 +set TokenBadgeAuthority... +prompt: whirlpoolsConfigPubkey: JChtLEVR9E6B5jiHTZS1Nd9WgMULMHv2UcVryYACAFYQ +prompt: newTokenBadgeAuthorityPubkey: 2v112XbwQXFrdqX438HUrfZF91qCZb7QRP4bwUiN7JF5 +setting... + tokenBadgeAuthority r21Gamwd9DtyjHeGywsneoQYR39C1VDwrw7tWxHAwh6 + newTokenBadgeAuthority 2v112XbwQXFrdqX438HUrfZF91qCZb7QRP4bwUiN7JF5 + +if the above is OK, enter YES +prompt: yesno: YES +tx: 2g8gsZFYyNcp4oQU6s9ZM5ZcyH4sye3KbNVTdTyt9KZzPrwP2tqK3Hxrc8LEXiTHGUSiyw228QWsYBdJMdPNqib5 + +*/ diff --git a/legacy-sdk/cli/src/commands/todo/initializeTickArrayRange b/legacy-sdk/cli/src/commands/todo/initializeTickArrayRange new file mode 100644 index 000000000..5996a0930 --- /dev/null +++ b/legacy-sdk/cli/src/commands/todo/initializeTickArrayRange @@ -0,0 +1,379 @@ +import type { PDA} from "@orca-so/common-sdk"; +import { TransactionBuilder } from "@orca-so/common-sdk"; +import type { + AccountFetcher, + WhirlpoolData} from "@orca-so/whirlpools-sdk"; +import { + MAX_TICK_INDEX, + MIN_TICK_INDEX, + ORCA_WHIRLPOOL_PROGRAM_ID, + PriceMath, + TickArrayUtil, + TickUtil, + TICK_ARRAY_SIZE, + WhirlpoolContext, + WhirlpoolIx, +} from "@orca-so/whirlpools-sdk"; +import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet"; +import type { MintInfo } from "@solana/spl-token"; +import type { Connection, PublicKey } from "@solana/web3.js"; +import Decimal from "decimal.js"; +import prompts from "prompts"; +import { getConnectionFromEnv } from "../connection"; +import { getOwnerKeypair } from "../loadAccounts"; +import { promptForNetwork, promptForWhirlpool } from "./whirlpool-script-utils"; +const SOL_9_DEC = new Decimal(10).pow(9); +const TICK_ARRAYS_TO_FETCH_PER_TX = 5; + +async function run() { + const owner = await getOwnerKeypair(); + const network = await promptForNetwork(); + const connection = await getConnectionFromEnv(network); + console.log(`Connected to RPC - ${connection.rpcEndpoint}`); + + const ctx = WhirlpoolContext.from( + connection, + new NodeWallet(owner), + ORCA_WHIRLPOOL_PROGRAM_ID, + ); + const fetcher = ctx.fetcher; + + // Derive Whirlpool address + const { key: whirlpoolAddr, tokenKeyInverted } = await promptForWhirlpool(); + if (tokenKeyInverted) { + console.log(`NOTE: tokenMintA & B order had been inverted.`); + } + console.log(`Fetching Whirlpool at address - ${whirlpoolAddr.toBase58()}`); + console.log(`...`); + + const acct = await fetchWhirlpoolAccounts(fetcher, whirlpoolAddr); + + const { lowerTick, upperTick } = await promptForTickRange(acct); + + console.log( + `Script will initialize arrays range [${lowerTick} -> ${upperTick}] (start-indices). Current tick is at ${ + acct.data.tickCurrentIndex + } / price - $${PriceMath.sqrtPriceX64ToPrice( + acct.data.sqrtPrice, + acct.tokenA.decimals, + acct.tokenB.decimals, + ).toDecimalPlaces(5)}`, + ); + console.log(`Fetching tick-array data...`); + const tickArrayInfo = await getTickArrays( + fetcher, + connection, + whirlpoolAddr, + acct, + lowerTick, + upperTick, + ); + + console.log(``); + console.log( + `There are ${tickArrayInfo.numTickArraysInRange} arrays in range, with ${tickArrayInfo.numTickArraysToInit} arrays that needs to be initialized.`, + ); + + if (tickArrayInfo.numTickArraysToInit > 0) { + await checkWalletBalance( + connection, + owner.publicKey, + tickArrayInfo.costToInit, + ); + await executeInitialization(ctx, whirlpoolAddr, tickArrayInfo.pdasToInit); + } + + console.log("Complete."); +} + +async function executeInitialization( + ctx: WhirlpoolContext, + addr: PublicKey, + arraysToInit: { startIndex: number; pda: PDA }[], +) { + const response = await prompts([ + { + type: "select", + name: "execute", + message: "Execute?", + choices: [ + { title: "yes", value: "yes" }, + { title: "no", value: "no" }, + ], + }, + ]); + + if (response.execute === "no") { + return; + } + + let start = 0; + let end = TICK_ARRAYS_TO_FETCH_PER_TX; + + do { + const chunk = arraysToInit.slice(start, end); + + const startIndicies = chunk.map((val) => val.startIndex); + console.log( + `Executing initializations for array w/ start indices [${startIndicies}]...`, + ); + const txBuilder = new TransactionBuilder(ctx.connection, ctx.wallet); + for (const tickArray of chunk) { + txBuilder.addInstruction( + WhirlpoolIx.initTickArrayIx(ctx.program, { + startTick: tickArray.startIndex, + tickArrayPda: tickArray.pda, + whirlpool: addr, + funder: ctx.wallet.publicKey, + }), + ); + } + + const txId = await txBuilder.buildAndExecute(); + console.log(`Tx executed at ${txId}`); + + start = end; + end = end + TICK_ARRAYS_TO_FETCH_PER_TX; + } while (start < arraysToInit.length); +} + +async function checkWalletBalance( + connection: Connection, + ownerKey: PublicKey, + costToInit: Decimal, +) { + const walletBalance = new Decimal(await connection.getBalance(ownerKey)).div( + SOL_9_DEC, + ); + console.log( + `Wallet balance (${ownerKey.toBase58()}) - ${walletBalance} SOL. Est. cost - ${costToInit} SOL`, + ); + if (walletBalance.lessThan(costToInit)) { + throw new Error("Wallet has insufficent SOL to complete this operation."); + } +} + +async function getTickArrays( + fetcher: AccountFetcher, + connection: Connection, + whirlpool: PublicKey, + acct: WhirlpoolAccounts, + lowerTick: number, + upperTick: number, +) { + const lowerStartTick = TickUtil.getStartTickIndex( + lowerTick, + acct.data.tickSpacing, + ); + const upperStartTick = TickUtil.getStartTickIndex( + upperTick, + acct.data.tickSpacing, + ); + const numTickArraysInRange = + Math.ceil( + (upperStartTick - lowerStartTick) / + acct.data.tickSpacing / + TICK_ARRAY_SIZE, + ) + 1; + const arrayStartIndicies = [...Array(numTickArraysInRange).keys()].map( + (index) => lowerStartTick + index * acct.data.tickSpacing * TICK_ARRAY_SIZE, + ); + + const initArrayKeys = await TickArrayUtil.getUninitializedArraysPDAs( + arrayStartIndicies, + ORCA_WHIRLPOOL_PROGRAM_ID, + whirlpool, + acct.data.tickSpacing, + fetcher, + true, + ); + + // TickArray = Tick.LEN(113) * Array_SIZE (88) + 36 + 8 = 9988 + const rentExemptPerAcct = + await connection.getMinimumBalanceForRentExemption(9988); + + const costToInit = new Decimal(initArrayKeys.length * rentExemptPerAcct).div( + SOL_9_DEC, + ); + + return { + numTickArraysInRange, + numTickArraysToInit: initArrayKeys.length, + pdasToInit: initArrayKeys, + costToInit, + }; +} + +type WhirlpoolAccounts = { + tokenMintA: PublicKey; + tokenMintB: PublicKey; + tokenA: MintInfo; + tokenB: MintInfo; + data: WhirlpoolData; +}; +async function fetchWhirlpoolAccounts( + fetcher: AccountFetcher, + whirlpoolAddr: PublicKey, +): Promise { + const pool = await fetcher.getPool(whirlpoolAddr, true); + if (!pool) { + throw new Error( + `Unable to fetch Whirlpool at addr - ${whirlpoolAddr.toBase58()}`, + ); + } + const { tokenMintA, tokenMintB } = pool; + const [tokenA, tokenB] = await fetcher.listMintInfos( + [tokenMintA, tokenMintB], + true, + ); + + if (!tokenA) { + throw new Error(`Unable to fetch token - ${tokenMintA.toBase58()}`); + } + + if (!tokenB) { + throw new Error(`Unable to fetch token - ${tokenMintB.toBase58()}`); + } + + return { + tokenMintA, + tokenMintB, + tokenA, + tokenB, + data: pool, + }; +} + +type PriceRangeResponse = { + lowerTick: number; + upperTick: number; +}; +async function promptForTickRange( + acct: WhirlpoolAccounts, +): Promise { + const provideTypeResponse = await prompts([ + { + type: "select", + name: "provideType", + message: "How would you like to provide the price range?", + choices: [ + { title: "Full Range", value: "fullRange" }, + { title: "By Price", value: "price" }, + { title: "By tick", value: "tick" }, + { title: "Current Price", value: "currentPrice" }, + ], + }, + ]); + + let lowerTick = 0, + upperTick = 0; + switch (provideTypeResponse.provideType) { + case "fullRange": { + lowerTick = MIN_TICK_INDEX; + upperTick = MAX_TICK_INDEX; + break; + } + case "price": { + const priceResponse = await prompts([ + { + type: "number", + name: "lowerPrice", + message: `Lower Price for ${acct.tokenMintB.toBase58()}/${acct.tokenMintB.toBase58()}`, + }, + { + type: "number", + name: "upperPrice", + message: `Upper Price for ${acct.tokenMintB.toBase58()}/${acct.tokenMintB.toBase58()}`, + }, + ]); + lowerTick = PriceMath.priceToTickIndex( + new Decimal(priceResponse.lowerPrice), + acct.tokenA.decimals, + acct.tokenB.decimals, + ); + upperTick = PriceMath.priceToTickIndex( + new Decimal(priceResponse.upperPrice), + acct.tokenA.decimals, + acct.tokenB.decimals, + ); + break; + } + case "tick": { + const tickResponse = await prompts([ + { + type: "text", + name: "lowerTick", + message: `Lower Tick for ${acct.tokenMintB.toBase58()}/${acct.tokenMintB.toBase58()}`, + }, + { + type: "text", + name: "upperTick", + message: `Upper Tick for ${acct.tokenMintB.toBase58()}/${acct.tokenMintB.toBase58()}`, + }, + ]); + + lowerTick = new Decimal(tickResponse.lowerTick) + .toDecimalPlaces(0) + .toNumber(); + upperTick = new Decimal(tickResponse.upperTick) + .toDecimalPlaces(0) + .toNumber(); + break; + } + case "currentPrice": { + const currPriceResponse = await prompts([ + { + type: "number", + name: "expandBy", + message: `Current price is ${PriceMath.sqrtPriceX64ToPrice( + acct.data.sqrtPrice, + acct.tokenA.decimals, + acct.tokenB.decimals, + ).toDecimalPlaces(9)} / tick - ${ + acct.data.tickCurrentIndex + }. How many tick arrays on each direction would you like to initialize?`, + }, + ]); + const currTick = TickUtil.getInitializableTickIndex( + acct.data.tickCurrentIndex, + acct.data.tickSpacing, + ); + const expandByTick = + currPriceResponse.expandBy * acct.data.tickSpacing * TICK_ARRAY_SIZE; + lowerTick = currTick - expandByTick; + upperTick = currTick + expandByTick; + break; + } + } + + if (lowerTick < MIN_TICK_INDEX || lowerTick > MAX_TICK_INDEX) { + throw new Error( + `Lower tick - ${lowerTick} is lower than MIN allowed [(${MIN_TICK_INDEX}, ${MAX_TICK_INDEX}]`, + ); + } + + if (upperTick < MIN_TICK_INDEX || upperTick > MAX_TICK_INDEX) { + throw new Error( + `Upper tick - ${lowerTick} is not within bounds [${MIN_TICK_INDEX}, ${MAX_TICK_INDEX}]`, + ); + } + + if (lowerTick >= upperTick) { + throw new Error( + `Upper tick ${upperTick} must be higher than lower tick - ${lowerTick}`, + ); + } + + return { + lowerTick: TickUtil.getInitializableTickIndex( + lowerTick, + acct.data.tickSpacing, + ), + upperTick: TickUtil.getInitializableTickIndex( + upperTick, + acct.data.tickSpacing, + ), + }; +} + +run(); diff --git a/legacy-sdk/cli/src/commands/todo/initializeWhirlpoolReward b/legacy-sdk/cli/src/commands/todo/initializeWhirlpoolReward new file mode 100644 index 000000000..023416659 --- /dev/null +++ b/legacy-sdk/cli/src/commands/todo/initializeWhirlpoolReward @@ -0,0 +1,38 @@ +import { Provider } from "@project-serum/anchor"; +import { OrcaNetwork, OrcaWhirlpoolClient } from "@orca-so/whirlpool-sdk"; + +const SOLANA_NETWORK_URL = "https://api.devnet.solana.com"; + +async function run() { + // @ts-expect-error this script doesn't work with latest anchor version + const provider = Provider.local(SOLANA_NETWORK_URL); + + const rewardAuthority = "81dVYq6RgX6Jt1TEDWpLkYUMWesNq3GMSYLKaKsopUqi"; + const poolAddress = "75dykYVKVj15kHEYiK4p9XEy8XpkrnfWMR8q3pbiC9Uo"; + const rewardMint = "orcarKHSqC5CDDsGbho8GKvwExejWHxTqGzXgcewB9L"; + + const client = new OrcaWhirlpoolClient({ + network: OrcaNetwork.DEVNET, + }); + + const { tx, rewardVault } = client.admin.getInitRewardTx({ + provider, + rewardAuthority, + poolAddress, + rewardMint, + rewardIndex: 0, + }); + + const txId = await tx.buildAndExecute(); + + console.log("txId", txId); + console.log("rewardVault", rewardVault.toBase58()); +} + +run() + .then(() => { + console.log("Success"); + }) + .catch((e) => { + console.error(e); + }); diff --git a/legacy-sdk/cli/src/index.ts b/legacy-sdk/cli/src/index.ts new file mode 100644 index 000000000..9258e83d4 --- /dev/null +++ b/legacy-sdk/cli/src/index.ts @@ -0,0 +1,21 @@ +import { readdirSync } from "fs"; +import { promptChoice } from "./utils/prompt"; +import { toSnakeCase } from "js-convert-case"; + +const commands = readdirSync("./src/commands") + .filter((file) => file.endsWith(".ts")) + .map((file) => file.replace(".ts", "")) + .map((file) => ({ + title: file, + value: () => import(`./commands/${file}.ts`), + })); + +const arg = toSnakeCase(process.argv[2]); + +const maybeCommand = commands.find((c) => c.title === arg); +if (maybeCommand) { + await maybeCommand.value(); +} else { + const command = await promptChoice("command", commands); + await command(); +} diff --git a/legacy-sdk/cli/src/utils/deposit_ratio.ts b/legacy-sdk/cli/src/utils/deposit_ratio.ts new file mode 100644 index 000000000..ab73ea5a8 --- /dev/null +++ b/legacy-sdk/cli/src/utils/deposit_ratio.ts @@ -0,0 +1,54 @@ +import { PriceMath } from "@orca-so/whirlpools-sdk"; +import BN from "bn.js"; + +export function calcDepositRatio( + currSqrtPriceX64: BN, + lowerSqrtPriceX64: BN, + upperSqrtPriceX64: BN, + decimalsA: number, + decimalsB: number, +): [number, number] { + const clampedSqrtPriceX64 = BN.min( + BN.max(currSqrtPriceX64, lowerSqrtPriceX64), + upperSqrtPriceX64, + ); + + const clampedSqrtPrice = PriceMath.sqrtPriceX64ToPrice( + clampedSqrtPriceX64, + decimalsA, + decimalsB, + ).sqrt(); + const lowerSqrtPrice = PriceMath.sqrtPriceX64ToPrice( + lowerSqrtPriceX64, + decimalsA, + decimalsB, + ).sqrt(); + const upperSqrtPrice = PriceMath.sqrtPriceX64ToPrice( + upperSqrtPriceX64, + decimalsA, + decimalsB, + ).sqrt(); + + const currPrice = PriceMath.sqrtPriceX64ToPrice( + currSqrtPriceX64, + decimalsA, + decimalsB, + ); + + // calc ratio (L: liquidity) + // depositA = L/currSqrtPrice - L/upperSqrtPrice + // depositB = L*currSqrtPrice - L*lowerSqrtPrice + const depositA = upperSqrtPrice + .sub(clampedSqrtPrice) + .div(clampedSqrtPrice.mul(upperSqrtPrice)); + const depositB = clampedSqrtPrice.sub(lowerSqrtPrice); + + const depositAValueInB = depositA.mul(currPrice); + const depositBValueInB = depositB; + const totalValueInB = depositAValueInB.add(depositBValueInB); + + const ratioA = depositAValueInB.div(totalValueInB).mul(100); + const ratioB = depositBValueInB.div(totalValueInB).mul(100); + + return [ratioA.toNumber(), ratioB.toNumber()]; +} diff --git a/legacy-sdk/cli/src/utils/prompt.ts b/legacy-sdk/cli/src/utils/prompt.ts new file mode 100644 index 000000000..755f5ce79 --- /dev/null +++ b/legacy-sdk/cli/src/utils/prompt.ts @@ -0,0 +1,68 @@ +import prompt from "prompts"; + +export async function promptText( + message: string, + initial?: string, +): Promise { + const response = (await prompt({ + type: "text", + name: "text", + message, + initial, + })) as { text: string }; + if (Object.keys(response).length === 0) { + throw new Error("Prompt cancelled"); + } + const result = response.text.trim(); + if (result.length === 0) { + throw new Error("Prompt empty"); + } + return result; +} + +export async function promptNumber( + message: string, + initial?: number, +): Promise { + const response = (await prompt({ + type: "number", + name: "number", + float: true, + message, + initial, + })) as { number: number }; + if (Object.keys(response).length === 0) { + throw new Error("Prompt cancelled"); + } + return response.number; +} + +export interface Choice { + readonly title: string; + readonly description?: string; + readonly value: T; +} + +export async function promptChoice( + message: string, + choices: Choice[], +): Promise { + const response = (await prompt({ + type: "select", + name: "choice", + message, + choices, + })) as { choice: T }; + if (Object.keys(response).length === 0) { + throw new Error("Prompt cancelled"); + } + return response.choice; +} + +export async function promptConfirm(message: string): Promise { + const choices = [ + { title: "Yes", value: true }, + { title: "No", value: false }, + ]; + return promptChoice(message, choices); +} diff --git a/legacy-sdk/cli/src/utils/provider.ts b/legacy-sdk/cli/src/utils/provider.ts new file mode 100644 index 000000000..0f6f06f0e --- /dev/null +++ b/legacy-sdk/cli/src/utils/provider.ts @@ -0,0 +1,18 @@ +import { AnchorProvider } from "@coral-xyz/anchor"; +import { + ORCA_WHIRLPOOL_PROGRAM_ID, + WhirlpoolContext, +} from "@orca-so/whirlpools-sdk"; + +// export ANCHOR_PROVIDER_URL=http://localhost:8899 +// export ANCHOR_WALLET=~/.config/solana/id.json + +export const provider = AnchorProvider.env(); +console.info("connection endpoint", provider.connection.rpcEndpoint); +console.info("wallet", provider.wallet.publicKey.toBase58()); + +export const ctx = WhirlpoolContext.from( + provider.connection, + provider.wallet, + ORCA_WHIRLPOOL_PROGRAM_ID, +); diff --git a/legacy-sdk/cli/src/utils/transaction_sender.ts b/legacy-sdk/cli/src/utils/transaction_sender.ts new file mode 100644 index 000000000..a154cb965 --- /dev/null +++ b/legacy-sdk/cli/src/utils/transaction_sender.ts @@ -0,0 +1,197 @@ +import type { Keypair, VersionedTransaction } from "@solana/web3.js"; +import { ComputeBudgetProgram, LAMPORTS_PER_SOL } from "@solana/web3.js"; +import { + DecimalUtil, + TransactionBuilder, + estimateComputeBudgetLimit, +} from "@orca-so/common-sdk"; +import Decimal from "decimal.js"; +import base58 from "bs58"; +import { promptConfirm, promptNumber } from "./prompt"; + +export async function sendTransaction( + builder: TransactionBuilder, +): Promise { + const instructions = builder.compressIx(true); + // HACK: to clone TransactionBuilder + const signers = builder["signers"] as Keypair[]; + + const estimatedComputeUnits = await estimateComputeBudgetLimit( + builder.connection, + [instructions], + undefined, + builder.wallet.publicKey, + 0.1, // + 10% + ); + console.info("estimatedComputeUnits:", estimatedComputeUnits); + + let landed = false; + let success = false; + while (true) { + let priorityFeeInLamports = 0; + + while (true) { + const priorityFeeInSOL = await promptNumber("priorityFeeInSOL"); + priorityFeeInLamports = DecimalUtil.toBN( + new Decimal(priorityFeeInSOL), + 9, + ).toNumber(); + if (priorityFeeInLamports > LAMPORTS_PER_SOL) { + console.info("> 1 SOL is obviously too much for priority fee"); + continue; + } + if (priorityFeeInLamports > 5_000_000) { + console.info( + `Is it okay to use ${priorityFeeInLamports / LAMPORTS_PER_SOL} SOL for priority fee ? (if it is OK, enter OK)`, + ); + const ok = await promptConfirm("OK"); + if (!ok) continue; + } + + console.info( + "Priority fee:", + priorityFeeInLamports / LAMPORTS_PER_SOL, + "SOL", + ); + break; + } + + const builderWithPriorityFee = new TransactionBuilder( + builder.connection, + builder.wallet, + builder.opts, + ); + if (priorityFeeInLamports > 0) { + const setComputeUnitPriceIx = ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: Math.floor( + (priorityFeeInLamports * 1_000_000) / estimatedComputeUnits, + ), + }); + const setComputeUnitLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ + units: estimatedComputeUnits, + }); + + builderWithPriorityFee.addInstruction({ + instructions: [setComputeUnitLimitIx, setComputeUnitPriceIx], + cleanupInstructions: [], + signers: [], + }); + } + builderWithPriorityFee.addInstruction(instructions); + signers.forEach((s) => builderWithPriorityFee.addSigner(s)); + + let withDifferentPriorityFee = false; + while (true) { + console.info("process transaction..."); + const result = await send(builderWithPriorityFee); + landed = result.landed; + success = result.success; + if (landed) break; + + console.info("\ntransaction have not landed. retry ?, enter OK"); + const ok = await promptConfirm("OK"); + if (!ok) break; + + console.info("\nchange priority fee setting?, enter YES"); + const yesno = await promptConfirm("YES"); + if (yesno) { + withDifferentPriorityFee = true; + break; + } + } + + if (landed) break; + if (!withDifferentPriorityFee) break; + } + + return landed && success; +} + +async function send( + builder: TransactionBuilder, +): Promise<{ landed: boolean; success: boolean }> { + const connection = builder.connection; + const wallet = builder.wallet; + + // manual build + const built = await builder.build({ maxSupportedTransactionVersion: 0 }); + + const blockhash = await connection.getLatestBlockhashAndContext("confirmed"); + const blockHeight = await connection.getBlockHeight({ + commitment: "confirmed", + minContextSlot: await blockhash.context.slot, + }); + + // why 151: https://solana.com/docs/core/transactions/confirmation#how-does-transaction-expiration-work + const transactionTTL = blockHeight + 151; + + const notSigned = built.transaction as VersionedTransaction; + notSigned.message.recentBlockhash = blockhash.value.blockhash; + + if (built.signers.length > 0) notSigned.sign(built.signers); + const signed = await wallet.signTransaction(notSigned); + const signature = base58.encode(signed.signatures[0]); + + // manual send and confirm + const waitToConfirm = () => + new Promise((resolve) => setTimeout(resolve, 5000)); + const waitToRetry = () => new Promise((resolve) => setTimeout(resolve, 3000)); + + const numTry = 100; // break by expiration + let landed = false; + let success = false; + for (let i = 0; i < numTry; i++) { + // check transaction TTL + const blockHeight = await connection.getBlockHeight("confirmed"); + if (blockHeight > transactionTTL) { + // check signature status (to avoid false negative) + const sigStatus = await connection.getSignatureStatus(signature); + if (sigStatus.value?.confirmationStatus === "confirmed") { + success = sigStatus.value.err === null; + console.info( + success ? "✅successfully landed" : `🚨landed BUT TRANSACTION FAILED`, + ); + landed = true; + break; + } + + console.info("transaction have been expired"); + break; + } + console.info( + "transaction is still valid,", + transactionTTL - blockHeight, + "blocks left (at most)", + ); + + // send without retry on RPC server + console.info("sending..."); + await connection.sendRawTransaction(signed.serialize(), { + skipPreflight: true, + maxRetries: 0, + }); + + console.info("confirming..."); + await waitToConfirm(); + + // check signature status + const sigStatus = await connection.getSignatureStatus(signature); + if (sigStatus.value?.confirmationStatus === "confirmed") { + success = sigStatus.value.err === null; + console.info( + success ? "✅successfully landed" : `🚨landed BUT TRANSACTION FAILED`, + ); + landed = true; + break; + } + + // todo: need to increase wait time, but TTL is not long... + await waitToRetry(); + } + + if (landed) { + console.info("signature", signature); + } + + return { landed, success }; +} diff --git a/legacy-sdk/cli/tsconfig.json b/legacy-sdk/cli/tsconfig.json new file mode 100644 index 000000000..58767cd77 --- /dev/null +++ b/legacy-sdk/cli/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "outDir": "./dist" + }, + "include": ["./src/**/*.ts"] +} diff --git a/legacy-sdk/scripts/calcRentExempt.ts b/legacy-sdk/scripts/calcRentExempt.ts deleted file mode 100644 index 968df0b4d..000000000 --- a/legacy-sdk/scripts/calcRentExempt.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { readFile } from "mz/fs"; -import Decimal from "decimal.js"; -import { Keypair } from "@solana/web3.js"; - -const toBuffer = (arr: Buffer | Uint8Array | Array): Buffer => { - if (arr instanceof Buffer) { - return arr; - } else if (arr instanceof Uint8Array) { - return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength); - } else { - return Buffer.from(arr); - } -}; - -async function getKeyPair(keyPath: string): Promise { - const buffer = await readFile(keyPath); - let data = JSON.parse(buffer.toString()); - return Keypair.fromSecretKey(toBuffer(data)); -} - -async function run() { - // https://api.mainnet-beta.solana.com - const wallet = new anchor.Wallet( - await getKeyPair("/Users/ottocheung/dev/solana/pub.json"), - ); - const connection = new anchor.web3.Connection( - "https://api.mainnet-beta.solana.com", - ); - const provider = new anchor.AnchorProvider( - connection, - wallet, - anchor.AnchorProvider.defaultOptions(), - ); - - const sizeInBytes = [ - 1024, 5000, 10000, 50000, 1000000, 3000000, 5000000, 10000000, - ]; - const solPrice = 160; - sizeInBytes.forEach(async (size) => { - const result = - await provider.connection.getMinimumBalanceForRentExemption(size); - const sol = new Decimal(result).mul(0.000000001); - console.info( - `size - ${size} lamports - ${result} SOL- ${sol} price - ${sol.mul( - solPrice, - )}`, - ); - }); -} - -run(); diff --git a/legacy-sdk/scripts/expLogTestCaseConversion.ts b/legacy-sdk/scripts/expLogTestCaseConversion.ts deleted file mode 100644 index db0bebbbe..000000000 --- a/legacy-sdk/scripts/expLogTestCaseConversion.ts +++ /dev/null @@ -1,15 +0,0 @@ -import Decimal from "decimal.js"; -Decimal.set({ precision: 40, rounding: 4 }); - -const x64 = new Decimal(2).pow(64); - -const number = new Decimal(1).mul(x64); -console.info(`number - ${number}`); - -const exp = new Decimal(1.0001).sqrt().pow(1).mul(x64); -console.info(`exp - ${exp.toFixed(0, 1)}`); - -const log = new Decimal(18445821805675392311) - .div(x64) - .log(new Decimal(1.0001).sqrt()); -console.info(`log - ${log.toString()}`); diff --git a/legacy-sdk/scripts/genExpBitConstants.ts b/legacy-sdk/scripts/genExpBitConstants.ts deleted file mode 100644 index 29ea5212c..000000000 --- a/legacy-sdk/scripts/genExpBitConstants.ts +++ /dev/null @@ -1,66 +0,0 @@ -import Decimal from "decimal.js"; -Decimal.set({ precision: 40, rounding: 4 }); - -/** - * This script is to generate the magic numbers & unit tests needed for the exponent function - * in Whirlpools. - * - * Explanation on what magic numbers are: - * https://orcaso.notion.site/External-Tick-Math-Documentation-0dd30688f6a74478a42ffe18968262a9 - */ - -const x64 = new Decimal(2).pow(64); -const b = new Decimal("1.0001"); -// Qm.n = Q32.64 -const n = 64; - -console.info( - `Printing bit constants for whirlpool exponent of base ${b.toDecimalPlaces( - 4, - )}`, -); -console.info(``); -console.info(`1.0001 x64 const - ${b.mul(x64).toFixed(0, 1)}`); -console.info(``); - -console.info(`With a maximum tick of +/-443636, we'll need 19 bit constants:`); - -for (let j = 0; j <= 18; j++) { - const power = new Decimal(2).pow(j - 1); - const sqrtBPower = b.pow(power); - const iSqrtBPower = new Decimal(1).div(sqrtBPower).mul(x64); - console.info(`${iSqrtBPower.toFixed(0, 1)}`); -} - -const genUnitTestCases = (cases: number[]) => { - console.info(`tick | positive index result | negative index result`); - for (const tick of cases) { - const jsResult = new Decimal(b) - .pow(tick) - .sqrt() - .mul(new Decimal(2).pow(n)) - .toFixed(0, 1); - const njsResult = new Decimal(b) - .pow(-tick) - .sqrt() - .mul(new Decimal(2).pow(n)) - .toFixed(0, 1); - - console.info(tick + " - " + jsResult + " , " + njsResult); - } -}; - -let bitGroup = [ - 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, - 32768, 65536, 131072, 262144, 524288, -]; -let randGroup = [2493, 23750, 395, 129, 39502, 395730, 245847, 120821].sort( - (n1, n2) => n1 - n2, -); - -console.info(" "); -console.info("Printing unit test cases for binary fraction bit cases:"); -genUnitTestCases(bitGroup); -console.info(" "); -console.info("Printing unit test cases for random values:"); -genUnitTestCases(randGroup); diff --git a/legacy-sdk/scripts/genPositiveExpBitConstants.ts b/legacy-sdk/scripts/genPositiveExpBitConstants.ts deleted file mode 100644 index 61c88233b..000000000 --- a/legacy-sdk/scripts/genPositiveExpBitConstants.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Decimal from "decimal.js"; -Decimal.set({ toExpPos: 100, toExpNeg: -100, precision: 100 }); - -const b = new Decimal(1.0001); - -for (let j = 0; j < 19; j++) { - // Calculate target price - const index = Decimal.pow(2, j); - console.info("index", index); - const rawPrice = b.pow(index.div(2)); - const targetPrice = rawPrice.mul(Decimal.pow(2, 96)).floor(); - console.info("targetPrice", targetPrice); -} diff --git a/legacy-sdk/scripts/package.json b/legacy-sdk/scripts/package.json deleted file mode 100644 index ab8bbb875..000000000 --- a/legacy-sdk/scripts/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "@orca-so/whirlpools-scripts", - "private": true, - "version": "1.0.0", - "dependencies": { - "@project-serum/anchor": "~0.26.0", - "@solana/spl-token": "^0.4.8", - "@solana/web3.js": "^1.95.2" - }, - "devDependencies": { - "@types/mz": "^2.7.3", - "mz": "^2.7.0", - "typescript": "^5.6.3" - }, - "scripts": { - "build": "tsc", - "clean": "rimraf dist" - } -} diff --git a/legacy-sdk/scripts/tsconfig.json b/legacy-sdk/scripts/tsconfig.json deleted file mode 100644 index e5d433fdf..000000000 --- a/legacy-sdk/scripts/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist" - }, - "include": ["./src/**/*.ts", "./src/artifacts/whirlpool.json"], - "references": [ - { - "path": "../whirlpool" - } - ] -} diff --git a/legacy-sdk/scripts/whirlpoolNft.json b/legacy-sdk/scripts/whirlpoolNft.json deleted file mode 100644 index b1c953851..000000000 --- a/legacy-sdk/scripts/whirlpoolNft.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "Orca Whirlpool Position", - "symbol": "OWP", - "description": "Orca Whirlpool Position", - "image": "data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 581.4 581.4' width='120px' height='120px'%3e%3cdefs%3e%3cstyle%3e.cls-1%7bfill:%23ffd15c;%7d%3c/style%3e%3c/defs%3e%3cg id='Layer_3' data-name='Layer 3'%3e%3cpath class='cls-1' d='M523.54,249.16a233.55,233.55,0,0,0-6.7-41.53,222.48,222.48,0,0,0-35.59-75.46,219.47,219.47,0,0,0-27.72-31.06A222.1,222.1,0,0,0,420.62,76,236.75,236.75,0,0,0,384,57.44a260.8,260.8,0,0,0-38.87-12.08,304.15,304.15,0,0,0-80-6.79,4,4,0,0,0,0,8c27.6,1.25,53.31,5.45,76.45,12.49a246,246,0,0,1,35.68,13.78,225.16,225.16,0,0,1,32.68,19.26,209.66,209.66,0,0,1,28.49,24.51,197.65,197.65,0,0,1,40.51,62A199.88,199.88,0,0,1,489.64,214a205.08,205.08,0,0,1,4.22,36.61,218.73,218.73,0,0,1-2.25,36.81A194,194,0,0,1,468.46,356a178.27,178.27,0,0,1-20.76,29.22,167.18,167.18,0,0,1-26.15,24.12,177.22,177.22,0,0,1-64.3,29.73,208.44,208.44,0,0,1-35.61,5.42c-6.45.43-12.39.61-18.26.53-1,0-1.89,0-2.79-.05l-1.47,0-1.34-.07c-.95,0-1.9-.09-2.92-.17-2.93-.2-5.91-.57-8.39-.9a125,125,0,0,1-61.23-26.55,122.38,122.38,0,0,1-38.44-53.58,106.39,106.39,0,0,1-6.14-31.94c-.13-3-.06-5.92,0-8.19s.26-4.91.6-8.2a106.41,106.41,0,0,1,3-16,96.59,96.59,0,0,1,13-28.91A92.54,92.54,0,0,1,219,247.72a102.29,102.29,0,0,1,28.35-14.82,120.85,120.85,0,0,1,32.37-6.14c2.39-.15,5.12-.22,8.35-.22a43.55,43.55,0,0,1,6.33.5,27.2,27.2,0,0,1,9.92,3.39,18.74,18.74,0,0,1,6.23,6,19.74,19.74,0,0,1,2.91,8.37,20.74,20.74,0,0,1-.92,9,19.24,19.24,0,0,1-1.81,3.88,17.87,17.87,0,0,1-2.62,3.24,18.58,18.58,0,0,1-7.5,4.38,27.88,27.88,0,0,1-5.14,1.14l-.72.08c-.24,0-.48.06-.71.07l-1.57.11-4.53.1a91.91,91.91,0,0,0-13.33,1.32A79.66,79.66,0,0,0,261,271.79a68.1,68.1,0,0,0-25.36,16.26,64.19,64.19,0,0,0-15.72,26.82,66.54,66.54,0,0,0,10.52,58.55,68.6,68.6,0,0,0,22.74,19.51,78.85,78.85,0,0,0,26.66,8.55,94.71,94.71,0,0,0,13,.94h1.6l1.39,0c1,0,1.93,0,2.84-.07,1.87-.05,3.79-.14,5.69-.26a167.14,167.14,0,0,0,45-9A148.35,148.35,0,0,0,390.39,371,140,140,0,0,0,441.5,293.6,155.89,155.89,0,0,0,446,247.79a162,162,0,0,0-8.29-44.93,152.92,152.92,0,0,0-21-40.87,155.5,155.5,0,0,0-32-32.74A167,167,0,0,0,301.71,96.4a185.25,185.25,0,0,0-22.24-1.11c-7.17.09-14.4.49-21.51,1.2a239.16,239.16,0,0,0-42.29,8.1,225.88,225.88,0,0,0-76.51,38.84,211.35,211.35,0,0,0-55.49,65.3,219.75,219.75,0,0,0-25.05,81.15A247.29,247.29,0,0,0,57.84,332a234.14,234.14,0,0,0,6.66,41.54A221.33,221.33,0,0,0,100.17,449,217.9,217.9,0,0,0,128,480,220.46,220.46,0,0,0,160.85,505a244.85,244.85,0,0,0,75.4,30.85,295.93,295.93,0,0,0,66,7.27q7,0,14.07-.31a4,4,0,0,0,0-8c-27.43-1.23-53.14-5.48-76.39-12.62-6.29-1.94-12.4-4.05-18.14-6.27-5.94-2.32-11.84-4.9-17.54-7.66A223.72,223.72,0,0,1,171.58,489a200.79,200.79,0,0,1-51.82-53.66,194.09,194.09,0,0,1-28.09-68.13,206.75,206.75,0,0,1-4.18-36.61,218.06,218.06,0,0,1,2.28-36.77,185.5,185.5,0,0,1,23.84-68.1,178.87,178.87,0,0,1,48-52.74,189,189,0,0,1,64.53-30.34,198.5,198.5,0,0,1,35.38-5.81c5.93-.44,12-.62,17.91-.53a141.29,141.29,0,0,1,17.22,1.33c23.12,3.11,44.43,12,61.64,25.75a111.63,111.63,0,0,1,37,53.48,116.89,116.89,0,0,1,5.49,32.61A110.59,110.59,0,0,1,397.11,282,96.37,96.37,0,0,1,384,310.82a93.81,93.81,0,0,1-21.79,22.79,103.63,103.63,0,0,1-28.39,14.88,120,120,0,0,1-32.31,6.19c-1.4.08-2.8.13-4.23.16-.61,0-1.21,0-1.82,0h-2.27a48.07,48.07,0,0,1-6.6-.5A31.48,31.48,0,0,1,275.78,351a20.8,20.8,0,0,1-7-6,19,19,0,0,1-3.41-7.94,18.61,18.61,0,0,1,.46-8.53,16.47,16.47,0,0,1,4.08-6.92,20.27,20.27,0,0,1,7.66-4.76,31.63,31.63,0,0,1,5.38-1.43,45.37,45.37,0,0,1,6.31-.6l5.28-.11h.18l3.31-.23c.85-.06,1.7-.16,2.55-.26l.95-.11a76.87,76.87,0,0,0,14-3.15A65.75,65.75,0,0,0,351.41,283,67.38,67.38,0,0,0,358,269.07,68.91,68.91,0,0,0,361,239.13a67.56,67.56,0,0,0-10.24-28.54,66,66,0,0,0-22-21.15A74.29,74.29,0,0,0,301.6,180a92,92,0,0,0-13.36-1c-4.5,0-8,.13-11.37.36a167.85,167.85,0,0,0-45,8.94,147.55,147.55,0,0,0-41.19,22,139.57,139.57,0,0,0-51,77.56,152.89,152.89,0,0,0-3.93,22.9c-.39,4.39-.61,8-.67,11.48-.09,4.46,0,8.13.16,11.54a151.39,151.39,0,0,0,9.4,45.25,159.53,159.53,0,0,0,21.92,40.09,169,169,0,0,0,70.93,54.75,166.11,166.11,0,0,0,43.7,11.25c4.51.48,8,.75,11.18.88,1.44.08,2.87.11,4.25.14l1.37,0h1.71c1.32,0,2.67,0,3.87,0,6.76-.09,13.92-.49,21.29-1.18a248.58,248.58,0,0,0,42.22-7.6,220.77,220.77,0,0,0,40.43-15.29,208.26,208.26,0,0,0,36.61-23.25,205.59,205.59,0,0,0,54.56-66.34,225.76,225.76,0,0,0,24.06-81.22A248.76,248.76,0,0,0,523.54,249.16Z'/%3e%3c/g%3e%3c/svg%3e", - "external_url": "https://www.orca.so/whirlpools/", - "collection": { - "name": "Orca Whirlpool Position", - "family": "Orca Whirlpool Position" - } -} \ No newline at end of file diff --git a/legacy-sdk/whirlpool/src/impl/whirlpool-client-impl.ts b/legacy-sdk/whirlpool/src/impl/whirlpool-client-impl.ts index 9971192e7..bf7c54ba8 100644 --- a/legacy-sdk/whirlpool/src/impl/whirlpool-client-impl.ts +++ b/legacy-sdk/whirlpool/src/impl/whirlpool-client-impl.ts @@ -160,7 +160,10 @@ export class WhirlpoolClientImpl implements WhirlpoolClient { ); } - const positionMint = await this.ctx.fetcher.getMintInfo(account.positionMint, opts); + const positionMint = await this.ctx.fetcher.getMintInfo( + account.positionMint, + opts, + ); if (!positionMint) { throw new Error( `Unable to fetch Mint for Position at address at ${positionAddress}`, diff --git a/legacy-sdk/whirlpool/src/impl/whirlpool-impl.ts b/legacy-sdk/whirlpool/src/impl/whirlpool-impl.ts index ec544d9d5..e4cda825c 100644 --- a/legacy-sdk/whirlpool/src/impl/whirlpool-impl.ts +++ b/legacy-sdk/whirlpool/src/impl/whirlpool-impl.ts @@ -8,7 +8,11 @@ import { ZERO, resolveOrCreateATAs, } from "@orca-so/common-sdk"; -import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; import type { PublicKey } from "@solana/web3.js"; import { Keypair } from "@solana/web3.js"; import invariant from "tiny-invariant"; @@ -315,7 +319,8 @@ export class WhirlpoolImpl implements Whirlpool { "tickUpper is out of bounds.", ); invariant( - tokenProgramId.equals(TOKEN_PROGRAM_ID) || tokenProgramId.equals(TOKEN_2022_PROGRAM_ID), + tokenProgramId.equals(TOKEN_PROGRAM_ID) || + tokenProgramId.equals(TOKEN_2022_PROGRAM_ID), "tokenProgramId must be either TOKEN_PROGRAM_ID or TOKEN_2022_PROGRAM_ID", ); @@ -380,15 +385,18 @@ export class WhirlpoolImpl implements Whirlpool { }; const positionIx = tokenProgramId.equals(TOKEN_2022_PROGRAM_ID) ? openPositionWithTokenExtensionsIx(this.ctx.program, { - ...params, - positionMint: positionMintPubkey, - withTokenMetadataExtension: withMetadata, - }) - : (withMetadata ? openPositionWithMetadataIx : openPositionIx)(this.ctx.program, { - ...params, - positionMintAddress: positionMintPubkey, - metadataPda, - }); + ...params, + positionMint: positionMintPubkey, + withTokenMetadataExtension: withMetadata, + }) + : (withMetadata ? openPositionWithMetadataIx : openPositionIx)( + this.ctx.program, + { + ...params, + positionMintAddress: positionMintPubkey, + metadataPda, + }, + ); txBuilder.addInstruction(positionIx); if (positionMint === undefined) { @@ -811,7 +819,12 @@ export class WhirlpoolImpl implements Whirlpool { }; if (positionMint.tokenProgram.equals(TOKEN_2022_PROGRAM_ID)) { - return [closePositionWithTokenExtensionsIx(this.ctx.program, closePositionParams)] + return [ + closePositionWithTokenExtensionsIx( + this.ctx.program, + closePositionParams, + ), + ]; } else { return [closePositionIx(this.ctx.program, closePositionParams)]; } diff --git a/legacy-sdk/whirlpool/src/instructions/composites/collect-all-txn.ts b/legacy-sdk/whirlpool/src/instructions/composites/collect-all-txn.ts index 61f263817..154632210 100644 --- a/legacy-sdk/whirlpool/src/instructions/composites/collect-all-txn.ts +++ b/legacy-sdk/whirlpool/src/instructions/composites/collect-all-txn.ts @@ -167,7 +167,9 @@ export async function collectAllForPositionsTxns( ); } - const positionMintInfo = positionMintInfos.get(position.positionMint.toBase58()); + const positionMintInfo = positionMintInfos.get( + position.positionMint.toBase58(), + ); if (!positionMintInfo) { throw new Error( `Unable to process positionMint ${position.positionMint.toBase58()} - missing mint info`, diff --git a/legacy-sdk/whirlpool/src/instructions/composites/swap-async.ts b/legacy-sdk/whirlpool/src/instructions/composites/swap-async.ts index 963e1cae8..d895f3415 100644 --- a/legacy-sdk/whirlpool/src/instructions/composites/swap-async.ts +++ b/legacy-sdk/whirlpool/src/instructions/composites/swap-async.ts @@ -33,7 +33,8 @@ export async function swapAsync( _opts: WhirlpoolAccountFetchOptions, ): Promise { const { wallet, whirlpool, swapInput } = params; - const { aToB, amount, otherAmountThreshold, amountSpecifiedIsInput } = swapInput; + const { aToB, amount, otherAmountThreshold, amountSpecifiedIsInput } = + swapInput; const txBuilder = new TransactionBuilder( ctx.connection, ctx.wallet, @@ -60,8 +61,14 @@ export async function swapAsync( ctx.connection, wallet, [ - { tokenMint: data.tokenMintA, wrappedSolAmountIn: aToB ? maxInputAmount : ZERO }, - { tokenMint: data.tokenMintB, wrappedSolAmountIn: !aToB ? maxInputAmount : ZERO }, + { + tokenMint: data.tokenMintA, + wrappedSolAmountIn: aToB ? maxInputAmount : ZERO, + }, + { + tokenMint: data.tokenMintB, + wrappedSolAmountIn: !aToB ? maxInputAmount : ZERO, + }, ], () => ctx.fetcher.getAccountRentExempt(), undefined, // use default diff --git a/legacy-sdk/whirlpool/src/network/public/fetcher/fetcher-utils.ts b/legacy-sdk/whirlpool/src/network/public/fetcher/fetcher-utils.ts index 3c9b40c9b..b2c953ee6 100644 --- a/legacy-sdk/whirlpool/src/network/public/fetcher/fetcher-utils.ts +++ b/legacy-sdk/whirlpool/src/network/public/fetcher/fetcher-utils.ts @@ -2,14 +2,22 @@ import type { Address } from "@orca-so/common-sdk"; import { AddressUtil } from "@orca-so/common-sdk"; import type { Connection, PublicKey } from "@solana/web3.js"; import invariant from "tiny-invariant"; -import type { PositionBundleData, PositionData, WhirlpoolData } from "../../../types/public"; +import type { + PositionBundleData, + PositionData, + WhirlpoolData, +} from "../../../types/public"; import { AccountName, WHIRLPOOL_CODER, getAccountSize, } from "../../../types/public"; import { ParsableWhirlpool } from "../parsing"; -import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID, unpackAccount } from "@solana/spl-token"; +import { + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, + unpackAccount, +} from "@solana/spl-token"; import { PDAUtil, PositionBundleUtil } from "../../../utils/public"; import { IGNORE_CACHE } from "../../.."; import type { WhirlpoolContext } from "../../.."; @@ -71,13 +79,13 @@ export async function getAllWhirlpoolAccountsForConfig({ export type PositionMap = { positions: ReadonlyMap; positionsWithTokenExtensions: ReadonlyMap; - positionBundles: BundledPositionMap[] + positionBundles: BundledPositionMap[]; }; export type BundledPositionMap = { - positionBundleAddress: Address, - positionBundleData: PositionBundleData, - bundledPositions: ReadonlyMap + positionBundleAddress: Address; + positionBundleData: PositionBundleData; + bundledPositions: ReadonlyMap; }; /** @@ -106,26 +114,15 @@ export async function getAllPositionAccountsByOwner({ }): Promise { const positions = !includesPositions ? new Map() - : await findPositions( - ctx, - owner, - TOKEN_PROGRAM_ID, - ); + : await findPositions(ctx, owner, TOKEN_PROGRAM_ID); const positionsWithTokenExtensions = !includesPositionsWithTokenExtensions ? new Map() - : await findPositions( - ctx, - owner, - TOKEN_2022_PROGRAM_ID, - ); + : await findPositions(ctx, owner, TOKEN_2022_PROGRAM_ID); const positionBundles = !includesBundledPositions ? [] - : await findBundledPositions( - ctx, - owner, - ); + : await findBundledPositions(ctx, owner); return { positions, @@ -145,9 +142,9 @@ async function findPositions( AddressUtil.toPubKey(owner), { programId, - } + }, ); - + // Get candidate addresses for the position const candidatePubkeys: PublicKey[] = []; tokenAccounts.value.forEach((ta) => { @@ -156,31 +153,38 @@ async function findPositions( const pda = PDAUtil.getPosition(ctx.program.programId, parsed.mint); candidatePubkeys.push(pda.publicKey); } - }); + }); // Fetch candidate accounts - const positionData = await ctx.fetcher.getPositions(candidatePubkeys, IGNORE_CACHE); + const positionData = await ctx.fetcher.getPositions( + candidatePubkeys, + IGNORE_CACHE, + ); // Drop null return new Map( - Array.from(positionData.entries()) - .filter(([_, v]) => v !== null) as [string, PositionData][] + Array.from(positionData.entries()).filter(([_, v]) => v !== null) as [ + string, + PositionData, + ][], ); } async function findBundledPositions( ctx: WhirlpoolContext, owner: Address, -): Promise<{ - positionBundleAddress: Address, - positionBundleData: PositionBundleData, - bundledPositions: ReadonlyMap -}[]> { +): Promise< + { + positionBundleAddress: Address; + positionBundleData: PositionBundleData; + bundledPositions: ReadonlyMap; + }[] +> { const tokenAccounts = await ctx.connection.getTokenAccountsByOwner( AddressUtil.toPubKey(owner), { - programId: TOKEN_PROGRAM_ID - } + programId: TOKEN_PROGRAM_ID, + }, ); // Get candidate addresses for the position bundle @@ -194,39 +198,59 @@ async function findBundledPositions( }); // Fetch candidate accounts - const positionBundleData = await ctx.fetcher.getPositionBundles(candidatePubkeys, IGNORE_CACHE); + const positionBundleData = await ctx.fetcher.getPositionBundles( + candidatePubkeys, + IGNORE_CACHE, + ); // Drop null - const positionBundles = Array.from(positionBundleData.entries()) - .filter(([_, v]) => v !== null) as [string, PositionBundleData][]; + const positionBundles = Array.from(positionBundleData.entries()).filter( + ([_, v]) => v !== null, + ) as [string, PositionBundleData][]; const bundledPositionPubkeys: PublicKey[] = []; positionBundles.forEach(([_, positionBundle]) => { - const bundleIndexes = PositionBundleUtil.getOccupiedBundleIndexes(positionBundle); + const bundleIndexes = + PositionBundleUtil.getOccupiedBundleIndexes(positionBundle); bundleIndexes.forEach((bundleIndex) => { - const pda = PDAUtil.getBundledPosition(ctx.program.programId, positionBundle.positionBundleMint, bundleIndex); + const pda = PDAUtil.getBundledPosition( + ctx.program.programId, + positionBundle.positionBundleMint, + bundleIndex, + ); bundledPositionPubkeys.push(pda.publicKey); }); }); // Fetch bundled positions - const bundledPositionData = await ctx.fetcher.getPositions(bundledPositionPubkeys, IGNORE_CACHE); + const bundledPositionData = await ctx.fetcher.getPositions( + bundledPositionPubkeys, + IGNORE_CACHE, + ); return positionBundles.map(([positionBundleAddress, positionBundleData]) => { - const bundleIndexes = PositionBundleUtil.getOccupiedBundleIndexes(positionBundleData); + const bundleIndexes = + PositionBundleUtil.getOccupiedBundleIndexes(positionBundleData); const bundledPositions = new Map( bundleIndexes - .map((bundleIndex) => { - const pda = PDAUtil.getBundledPosition(ctx.program.programId, positionBundleData.positionBundleMint, bundleIndex); - return [bundleIndex, bundledPositionData.get(AddressUtil.toString(pda.publicKey))]; - }) - .filter(([_, v]) => v !== null) as [number, PositionData][] + .map((bundleIndex) => { + const pda = PDAUtil.getBundledPosition( + ctx.program.programId, + positionBundleData.positionBundleMint, + bundleIndex, + ); + return [ + bundleIndex, + bundledPositionData.get(AddressUtil.toString(pda.publicKey)), + ]; + }) + .filter(([_, v]) => v !== null) as [number, PositionData][], ); return { positionBundleAddress, positionBundleData, bundledPositions, - } - }); -} \ No newline at end of file + }; + }); +} diff --git a/legacy-sdk/whirlpool/src/quotes/swap/swap-manager.ts b/legacy-sdk/whirlpool/src/quotes/swap/swap-manager.ts index 25439f3fc..f72f0f0dc 100644 --- a/legacy-sdk/whirlpool/src/quotes/swap/swap-manager.ts +++ b/legacy-sdk/whirlpool/src/quotes/swap/swap-manager.ts @@ -69,7 +69,7 @@ export function computeSwap( throw new WhirlpoolsError( "Amount remaining is negative.", SwapErrorCode.AmountRemainingOverflow, - ); + ); } if (amountCalculated.gt(U64_MAX)) { throw new WhirlpoolsError( diff --git a/legacy-sdk/whirlpool/src/utils/instructions-util.ts b/legacy-sdk/whirlpool/src/utils/instructions-util.ts index 8957e2b05..8b886a02e 100644 --- a/legacy-sdk/whirlpool/src/utils/instructions-util.ts +++ b/legacy-sdk/whirlpool/src/utils/instructions-util.ts @@ -5,7 +5,10 @@ import { TOKEN_PROGRAM_ID, } from "@solana/spl-token"; import { SystemProgram } from "@solana/web3.js"; -import type { OpenPositionParams, OpenPositionWithTokenExtensionsParams } from "../instructions"; +import type { + OpenPositionParams, + OpenPositionWithTokenExtensionsParams, +} from "../instructions"; export function openPositionAccounts(params: OpenPositionParams) { const { @@ -30,7 +33,9 @@ export function openPositionAccounts(params: OpenPositionParams) { }; } -export function openPositionWithTokenExtensionsAccounts(params: OpenPositionWithTokenExtensionsParams) { +export function openPositionWithTokenExtensionsAccounts( + params: OpenPositionWithTokenExtensionsParams, +) { const { funder, owner, diff --git a/legacy-sdk/whirlpool/src/utils/math/swap-math.ts b/legacy-sdk/whirlpool/src/utils/math/swap-math.ts index 0fec3a1cb..bddd8d788 100644 --- a/legacy-sdk/whirlpool/src/utils/math/swap-math.ts +++ b/legacy-sdk/whirlpool/src/utils/math/swap-math.ts @@ -64,15 +64,16 @@ export function computeSwapStep( aToB, ); - let amountFixedDelta = (!isMaxSwap || initialAmountFixedDelta.exceedsMax()) - ? getAmountFixedDelta( - currSqrtPrice, - nextSqrtPrice, - currLiquidity, - amountSpecifiedIsInput, - aToB, - ) - : initialAmountFixedDelta.value(); + let amountFixedDelta = + !isMaxSwap || initialAmountFixedDelta.exceedsMax() + ? getAmountFixedDelta( + currSqrtPrice, + nextSqrtPrice, + currLiquidity, + amountSpecifiedIsInput, + aToB, + ) + : initialAmountFixedDelta.value(); let amountIn = amountSpecifiedIsInput ? amountFixedDelta : amountUnfixedDelta; let amountOut = amountSpecifiedIsInput diff --git a/legacy-sdk/whirlpool/src/utils/math/token-math.ts b/legacy-sdk/whirlpool/src/utils/math/token-math.ts index c5049279e..f1bd74a87 100644 --- a/legacy-sdk/whirlpool/src/utils/math/token-math.ts +++ b/legacy-sdk/whirlpool/src/utils/math/token-math.ts @@ -97,10 +97,12 @@ export function tryGetAmountDeltaA( roundUp && !remainder.eq(ZERO) ? quotient.add(new BN(1)) : quotient; if (result.gt(U64_MAX)) { - return AmountDeltaU64.fromExceedsMax(new WhirlpoolsError( - "Results larger than U64", - TokenErrorCode.TokenMaxExceeded, - )); + return AmountDeltaU64.fromExceedsMax( + new WhirlpoolsError( + "Results larger than U64", + TokenErrorCode.TokenMaxExceeded, + ), + ); } return AmountDeltaU64.fromValid(result); @@ -145,18 +147,22 @@ export function tryGetAmountDeltaB( // we check the overflow in the next step and return wrapped error if it happens. const p = BitMath.mul(n0, n1, limit * 2); if (BitMath.isOverLimit(p, limit)) { - return AmountDeltaU64.fromExceedsMax(new WhirlpoolsError( - `MulShiftRight overflowed u${limit}.`, - MathErrorCode.MultiplicationShiftRightOverflow, - )); + return AmountDeltaU64.fromExceedsMax( + new WhirlpoolsError( + `MulShiftRight overflowed u${limit}.`, + MathErrorCode.MultiplicationShiftRightOverflow, + ), + ); } const result = MathUtil.fromX64_BN(p); const shouldRound = roundUp && p.and(U64_MAX).gt(ZERO); if (shouldRound && result.eq(U64_MAX)) { - return AmountDeltaU64.fromExceedsMax(new WhirlpoolsError( - `MulShiftRight overflowed u${limit}.`, - MathErrorCode.MultiplicationOverflow, - )); + return AmountDeltaU64.fromExceedsMax( + new WhirlpoolsError( + `MulShiftRight overflowed u${limit}.`, + MathErrorCode.MultiplicationOverflow, + ), + ); } return AmountDeltaU64.fromValid(shouldRound ? result.add(ONE) : result); diff --git a/legacy-sdk/whirlpool/src/utils/public/pool-utils.ts b/legacy-sdk/whirlpool/src/utils/public/pool-utils.ts index 936b0ee68..1d96c2d3e 100644 --- a/legacy-sdk/whirlpool/src/utils/public/pool-utils.ts +++ b/legacy-sdk/whirlpool/src/utils/public/pool-utils.ts @@ -222,7 +222,6 @@ export class PoolUtil { } } - /** * Given an arbitrary pair of token mints, this function returns an ordering of the token mints * in the format [base, quote]. USD based stable coins are prioritized as the quote currency diff --git a/legacy-sdk/whirlpool/tests/integration/close_bundled_position.test.ts b/legacy-sdk/whirlpool/tests/integration/close_bundled_position.test.ts index b8187549a..e06bb02a6 100644 --- a/legacy-sdk/whirlpool/tests/integration/close_bundled_position.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/close_bundled_position.test.ts @@ -28,7 +28,10 @@ import { openBundledPosition, openPosition, } from "../utils/init-utils"; -import { generateDefaultOpenPositionWithTokenExtensionsParams, mintTokensToTestAccount } from "../utils/test-builders"; +import { + generateDefaultOpenPositionWithTokenExtensionsParams, + mintTokensToTestAccount, +} from "../utils/test-builders"; import { TokenExtensionUtil } from "../../src/utils/public/token-extension-util"; describe("close_bundled_position", () => { @@ -740,20 +743,21 @@ describe("close_bundled_position", () => { ); // open position with TokenExtensions - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - poolInitInfo.whirlpoolPda.publicKey, - true, - 0, - poolInitInfo.tickSpacing, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + poolInitInfo.whirlpoolPda.publicKey, + true, + 0, + poolInitInfo.tickSpacing, + provider.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); const tx = toTx( ctx, diff --git a/legacy-sdk/whirlpool/tests/integration/close_position.test.ts b/legacy-sdk/whirlpool/tests/integration/close_position.test.ts index 636425036..5636247b5 100644 --- a/legacy-sdk/whirlpool/tests/integration/close_position.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/close_position.test.ts @@ -544,20 +544,21 @@ describe("close_position", () => { const { poolInitInfo } = fixture.getInfos(); // open position with TokenExtensions - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - poolInitInfo.whirlpoolPda.publicKey, - true, - 0, - poolInitInfo.tickSpacing, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + poolInitInfo.whirlpoolPda.publicKey, + true, + 0, + poolInitInfo.tickSpacing, + provider.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); // try to close bundled position await assert.rejects( diff --git a/legacy-sdk/whirlpool/tests/integration/close_position_with_token_extensions.test.ts b/legacy-sdk/whirlpool/tests/integration/close_position_with_token_extensions.test.ts index c8a04197b..e211570f3 100644 --- a/legacy-sdk/whirlpool/tests/integration/close_position_with_token_extensions.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/close_position_with_token_extensions.test.ts @@ -1,7 +1,20 @@ import * as anchor from "@coral-xyz/anchor"; -import { AuthorityType, createCloseAccountInstruction, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { + AuthorityType, + createCloseAccountInstruction, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; import * as assert from "assert"; -import { IGNORE_CACHE, increaseLiquidityQuoteByLiquidityWithParams, NO_TOKEN_EXTENSION_CONTEXT, PDAUtil, TickUtil, toTx, WhirlpoolIx } from "../../src"; +import { + IGNORE_CACHE, + increaseLiquidityQuoteByLiquidityWithParams, + NO_TOKEN_EXTENSION_CONTEXT, + PDAUtil, + TickUtil, + toTx, + WhirlpoolIx, +} from "../../src"; import type { InitPoolParams } from "../../src"; import { WhirlpoolContext } from "../../src/context"; import { @@ -61,36 +74,34 @@ describe("close_position_with_token_extensions", () => { } async function checkClosed(address: PublicKey): Promise { - assert.equal( - await provider.connection.getAccountInfo(address), - undefined, - ); + assert.equal(await provider.connection.getAccountInfo(address), undefined); } describe("successfully closes an open position", () => { [true, false].map((withMetadata) => { it(`successfully closes an open position ${withMetadata ? "with" : "without"} metadata`, async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - withMetadata, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + withMetadata, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); - + .addSigner(mint) + .buildAndExecute(); + const rentPosition = await getRent(params.positionPda.publicKey); const rentMint = await getRent(params.positionMint); const rentTokenAccount = await getRent(params.positionTokenAccount); const rent = rentPosition + rentMint + rentTokenAccount; assert.ok(rent > 0); - + const receiverKeypair = anchor.web3.Keypair.generate(); await toTx( ctx, @@ -102,14 +113,14 @@ describe("close_position_with_token_extensions", () => { positionTokenAccount: params.positionTokenAccount, }), ).buildAndExecute(); - + // Position account should be closed await checkClosed(params.positionPda.publicKey); - + // Mint and TokenAccount should be closed await checkClosed(params.positionMint); await checkClosed(params.positionTokenAccount); - + const receiverAccount = await provider.connection.getAccountInfo( receiverKeypair.publicKey, ); @@ -123,20 +134,21 @@ describe("close_position_with_token_extensions", () => { const owner = anchor.web3.Keypair.generate(); const delegate = anchor.web3.Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - owner.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + owner.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); await approveToken( ctx.provider, @@ -156,7 +168,10 @@ describe("close_position_with_token_extensions", () => { ); // check delegation - const tokenAccount = await fetcher.getTokenInfo(params.positionTokenAccount, IGNORE_CACHE); + const tokenAccount = await fetcher.getTokenInfo( + params.positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(!!tokenAccount); assert.ok(tokenAccount.delegate?.equals(delegate.publicKey)); assert.ok(tokenAccount.delegatedAmount === 1n); @@ -172,9 +187,9 @@ describe("close_position_with_token_extensions", () => { positionTokenAccount: params.positionTokenAccount, }), ) - // sign with delegate - .addSigner(delegate) - .buildAndExecute(); + // sign with delegate + .addSigner(delegate) + .buildAndExecute(); await Promise.all([ checkClosed(params.positionPda.publicKey), @@ -187,20 +202,21 @@ describe("close_position_with_token_extensions", () => { const owner = anchor.web3.Keypair.generate(); const delegate = anchor.web3.Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - owner.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + owner.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); await approveToken( ctx.provider, @@ -212,7 +228,10 @@ describe("close_position_with_token_extensions", () => { ); // check delegation - const tokenAccount = await fetcher.getTokenInfo(params.positionTokenAccount, IGNORE_CACHE); + const tokenAccount = await fetcher.getTokenInfo( + params.positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(!!tokenAccount); assert.ok(tokenAccount.delegate?.equals(delegate.publicKey)); assert.ok(tokenAccount.delegatedAmount === 1n); @@ -228,9 +247,9 @@ describe("close_position_with_token_extensions", () => { positionTokenAccount: params.positionTokenAccount, }), ) - // sign with owner - .addSigner(owner) - .buildAndExecute(); + // sign with owner + .addSigner(owner) + .buildAndExecute(); await Promise.all([ checkClosed(params.positionPda.publicKey), @@ -240,20 +259,21 @@ describe("close_position_with_token_extensions", () => { }); it("succeeds with position token that was transferred to new owner", async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); const newOwner = anchor.web3.Keypair.generate(); const newOwnerPositionTokenAccount = await createTokenAccountV2( @@ -271,10 +291,16 @@ describe("close_position_with_token_extensions", () => { ); // check transfer - const oldOwnerTokenAccount = await fetcher.getTokenInfo(params.positionTokenAccount, IGNORE_CACHE); + const oldOwnerTokenAccount = await fetcher.getTokenInfo( + params.positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(!!oldOwnerTokenAccount); assert.ok(oldOwnerTokenAccount.amount === 0n); - const newOwnerTokenAccount = await fetcher.getTokenInfo(newOwnerPositionTokenAccount, IGNORE_CACHE); + const newOwnerTokenAccount = await fetcher.getTokenInfo( + newOwnerPositionTokenAccount, + IGNORE_CACHE, + ); assert.ok(!!newOwnerTokenAccount); assert.ok(newOwnerTokenAccount.amount === 1n); @@ -288,9 +314,9 @@ describe("close_position_with_token_extensions", () => { positionTokenAccount: newOwnerPositionTokenAccount, }), ) - // sign with new owner - .addSigner(newOwner) - .buildAndExecute(); + // sign with new owner + .addSigner(newOwner) + .buildAndExecute(); await Promise.all([ checkClosed(params.positionPda.publicKey), @@ -299,44 +325,46 @@ describe("close_position_with_token_extensions", () => { ]); // check original token account - const oldOwnerTokenAccountAfter = await fetcher.getTokenInfo(params.positionTokenAccount, IGNORE_CACHE); + const oldOwnerTokenAccountAfter = await fetcher.getTokenInfo( + params.positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(!!oldOwnerTokenAccountAfter); assert.ok(oldOwnerTokenAccountAfter.amount === 0n); // closing token account should be possible even if Mint have been closed. - await toTx( - ctx, { - instructions: [ - createCloseAccountInstruction( - params.positionTokenAccount, - ctx.wallet.publicKey, - ctx.wallet.publicKey, - [], - TOKEN_2022_PROGRAM_ID, - ) - ], - cleanupInstructions: [], - signers: [], - } - ).buildAndExecute(); + await toTx(ctx, { + instructions: [ + createCloseAccountInstruction( + params.positionTokenAccount, + ctx.wallet.publicKey, + ctx.wallet.publicKey, + [], + TOKEN_2022_PROGRAM_ID, + ), + ], + cleanupInstructions: [], + signers: [], + }).buildAndExecute(); await checkClosed(params.positionTokenAccount); }); it("fails to close a position with liquidity", async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); // add liquidity const pool = await fetcher.getPool(whirlpoolPda.publicKey, IGNORE_CACHE); @@ -359,7 +387,7 @@ describe("close_position_with_token_extensions", () => { provider, pool!.tokenMintA, tokenOwnerAccountA, - quote.tokenMaxA + quote.tokenMaxA, ); const tokenOwnerAccountB = await createTokenAccount( provider, @@ -370,11 +398,17 @@ describe("close_position_with_token_extensions", () => { provider, pool!.tokenMintB, tokenOwnerAccountB, - quote.tokenMaxB + quote.tokenMaxB, ); - const lowerStartTickIndex = TickUtil.getStartTickIndex(tickLowerIndex, pool!.tickSpacing); - const upperStartTickIndex = TickUtil.getStartTickIndex(tickUpperIndex, pool!.tickSpacing); + const lowerStartTickIndex = TickUtil.getStartTickIndex( + tickLowerIndex, + pool!.tickSpacing, + ); + const upperStartTickIndex = TickUtil.getStartTickIndex( + tickUpperIndex, + pool!.tickSpacing, + ); await initTickArray(ctx, whirlpoolPda.publicKey, lowerStartTickIndex); await initTickArray(ctx, whirlpoolPda.publicKey, upperStartTickIndex); await toTx( @@ -389,14 +423,24 @@ describe("close_position_with_token_extensions", () => { whirlpool: whirlpoolPda.publicKey, tokenVaultA: pool!.tokenVaultA, tokenVaultB: pool!.tokenVaultB, - tickArrayLower: PDAUtil.getTickArray(ctx.program.programId, whirlpoolPda.publicKey, lowerStartTickIndex).publicKey, - tickArrayUpper: PDAUtil.getTickArray(ctx.program.programId, whirlpoolPda.publicKey, upperStartTickIndex).publicKey, - }) - ) - .buildAndExecute(); + tickArrayLower: PDAUtil.getTickArray( + ctx.program.programId, + whirlpoolPda.publicKey, + lowerStartTickIndex, + ).publicKey, + tickArrayUpper: PDAUtil.getTickArray( + ctx.program.programId, + whirlpoolPda.publicKey, + upperStartTickIndex, + ).publicKey, + }), + ).buildAndExecute(); // check liquidity (not zero) - const position = await fetcher.getPosition(params.positionPda.publicKey, IGNORE_CACHE); + const position = await fetcher.getPosition( + params.positionPda.publicKey, + IGNORE_CACHE, + ); assert.ok(position!.liquidity.gtn(0)); await assert.rejects( @@ -418,20 +462,21 @@ describe("close_position_with_token_extensions", () => { const owner = anchor.web3.Keypair.generate(); const receiver = anchor.web3.Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - owner.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + owner.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); const ix = WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { positionAuthority: owner.publicKey, @@ -444,7 +489,11 @@ describe("close_position_with_token_extensions", () => { // drop isSigner flag const keysWithoutSign = ix.keys.map((key) => { if (key.pubkey.equals(owner.publicKey)) { - return { pubkey: key.pubkey, isSigner: false, isWritable: key.isWritable }; + return { + pubkey: key.pubkey, + isSigner: false, + isWritable: key.isWritable, + }; } return key; }); @@ -454,17 +503,14 @@ describe("close_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithoutSign], - cleanupInstructions: [], - signers: [], - } - ) - // no signature of owner - .buildAndExecute(), - /0xbc2/ // AccountNotSigner + toTx(ctx, { + instructions: [ixWithoutSign], + cleanupInstructions: [], + signers: [], + }) + // no signature of owner + .buildAndExecute(), + /0xbc2/, // AccountNotSigner ); }); @@ -473,20 +519,21 @@ describe("close_position_with_token_extensions", () => { const delegate = anchor.web3.Keypair.generate(); const receiver = anchor.web3.Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - owner.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + owner.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); await approveToken( ctx.provider, @@ -506,12 +553,15 @@ describe("close_position_with_token_extensions", () => { ); // check delegation - const tokenAccount = await fetcher.getTokenInfo(params.positionTokenAccount, IGNORE_CACHE); + const tokenAccount = await fetcher.getTokenInfo( + params.positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(!!tokenAccount); assert.ok(tokenAccount.delegate?.equals(delegate.publicKey)); assert.ok(tokenAccount.delegatedAmount === 1n); assert.ok(tokenAccount.closeAuthority?.equals(delegate.publicKey)); // needed to close token account by delegate - + const ix = WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { positionAuthority: delegate.publicKey, receiver: receiver.publicKey, @@ -523,7 +573,11 @@ describe("close_position_with_token_extensions", () => { // drop isSigner flag const keysWithoutSign = ix.keys.map((key) => { if (key.pubkey.equals(delegate.publicKey)) { - return { pubkey: key.pubkey, isSigner: false, isWritable: key.isWritable }; + return { + pubkey: key.pubkey, + isSigner: false, + isWritable: key.isWritable, + }; } return key; }); @@ -533,17 +587,14 @@ describe("close_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithoutSign], - cleanupInstructions: [], - signers: [], - } - ) - // no signature of delegate - .buildAndExecute(), - /0xbc2/ // AccountNotSigner + toTx(ctx, { + instructions: [ixWithoutSign], + cleanupInstructions: [], + signers: [], + }) + // no signature of delegate + .buildAndExecute(), + /0xbc2/, // AccountNotSigner ); }); @@ -551,20 +602,21 @@ describe("close_position_with_token_extensions", () => { const owner = anchor.web3.Keypair.generate(); const fakeOwner = anchor.web3.Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - owner.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + owner.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); await assert.rejects( toTx( @@ -577,27 +629,28 @@ describe("close_position_with_token_extensions", () => { positionTokenAccount: params.positionTokenAccount, }), ) - .addSigner(fakeOwner) - .buildAndExecute(), + .addSigner(fakeOwner) + .buildAndExecute(), /0x1783/, // MissingOrInvalidDelegate ); }); it("fails if position token account does not contain exactly one token", async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); // not ATA const fakePositionTokenAccount = await createTokenAccountV2( @@ -626,20 +679,21 @@ describe("close_position_with_token_extensions", () => { const owner = anchor.web3.Keypair.generate(); const delegate = anchor.web3.Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - owner.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + owner.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); await approveToken( ctx.provider, @@ -659,7 +713,10 @@ describe("close_position_with_token_extensions", () => { ); // check delegation (delegated, but 0 amount) - const tokenAccount = await fetcher.getTokenInfo(params.positionTokenAccount, IGNORE_CACHE); + const tokenAccount = await fetcher.getTokenInfo( + params.positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(!!tokenAccount); assert.ok(tokenAccount.delegate?.equals(delegate.publicKey)); assert.ok(tokenAccount.delegatedAmount === 0n); @@ -676,8 +733,8 @@ describe("close_position_with_token_extensions", () => { positionTokenAccount: params.positionTokenAccount, }), ) - .addSigner(delegate) - .buildAndExecute(), + .addSigner(delegate) + .buildAndExecute(), /0x1784/, // InvalidPositionTokenAmount ); }); @@ -687,20 +744,21 @@ describe("close_position_with_token_extensions", () => { const delegate = anchor.web3.Keypair.generate(); const fakeDelegate = anchor.web3.Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - owner.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + owner.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); await approveToken( ctx.provider, @@ -720,12 +778,15 @@ describe("close_position_with_token_extensions", () => { ); // check delegation - const tokenAccount = await fetcher.getTokenInfo(params.positionTokenAccount, IGNORE_CACHE); + const tokenAccount = await fetcher.getTokenInfo( + params.positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(!!tokenAccount); assert.ok(tokenAccount.delegate?.equals(delegate.publicKey)); assert.ok(tokenAccount.delegatedAmount === 1n); assert.ok(tokenAccount.closeAuthority?.equals(delegate.publicKey)); // needed to close token account by delegate - + await assert.rejects( toTx( ctx, @@ -737,42 +798,44 @@ describe("close_position_with_token_extensions", () => { positionTokenAccount: params.positionTokenAccount, }), ) - .addSigner(fakeDelegate) - .buildAndExecute(), + .addSigner(fakeDelegate) + .buildAndExecute(), /0x1783/, // MissingOrInvalidDelegate ); }); it("fails if position token account mint does not match position mint", async () => { - const { params: params1, mint: mint1 } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params: params1, mint: mint1 } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params1), ) - .addSigner(mint1) - .buildAndExecute(); + .addSigner(mint1) + .buildAndExecute(); - const { params: params2, mint: mint2 } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params: params2, mint: mint2 } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params2), ) - .addSigner(mint2) - .buildAndExecute(); + .addSigner(mint2) + .buildAndExecute(); await assert.rejects( toTx( @@ -790,35 +853,37 @@ describe("close_position_with_token_extensions", () => { }); it("fails if position_mint does not match position's position_mint field", async () => { - const { params: params1, mint: mint1 } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params: params1, mint: mint1 } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params1), ) - .addSigner(mint1) - .buildAndExecute(); + .addSigner(mint1) + .buildAndExecute(); - const { params: params2, mint: mint2 } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params: params2, mint: mint2 } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params2), ) - .addSigner(mint2) - .buildAndExecute(); + .addSigner(mint2) + .buildAndExecute(); await assert.rejects( toTx( @@ -837,20 +902,21 @@ describe("close_position_with_token_extensions", () => { }); it("fails if token program is invalid", async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); const ix = WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { positionAuthority: provider.wallet.publicKey, @@ -870,16 +936,12 @@ describe("close_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithWrongAccount], - cleanupInstructions: [], - signers: [], - } - ) - .buildAndExecute(), - /0xbc0/ // InvalidProgramId + toTx(ctx, { + instructions: [ixWithWrongAccount], + cleanupInstructions: [], + signers: [], + }).buildAndExecute(), + /0xbc0/, // InvalidProgramId ); }); diff --git a/legacy-sdk/whirlpool/tests/integration/multi-ix/position_with_token_extensions_management.test.ts b/legacy-sdk/whirlpool/tests/integration/multi-ix/position_with_token_extensions_management.test.ts index 07eaceb58..c2294043e 100644 --- a/legacy-sdk/whirlpool/tests/integration/multi-ix/position_with_token_extensions_management.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/multi-ix/position_with_token_extensions_management.test.ts @@ -12,11 +12,7 @@ import { WhirlpoolIx, } from "../../../src"; import { IGNORE_CACHE } from "../../../src/network/public/fetcher"; -import { - sleep, - TickSpacing, - ZERO_BN, -} from "../../utils"; +import { sleep, TickSpacing, ZERO_BN } from "../../utils"; import { defaultConfirmOptions } from "../../utils/const"; import { WhirlpoolTestFixtureV2 } from "../../utils/v2/fixture-v2"; import { createTokenAccountV2 } from "../../utils/v2/token-2022"; @@ -44,10 +40,7 @@ describe("position with token extensions management tests", () => { } async function checkClosed(address: PublicKey): Promise { - assert.equal( - await provider.connection.getAccountInfo(address), - undefined, - ); + assert.equal(await provider.connection.getAccountInfo(address), undefined); } const isToken2022Variations = [false, true]; @@ -109,18 +102,21 @@ describe("position with token extensions management tests", () => { ); // open position - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), - ).addSigner(mint).buildAndExecute(); + ) + .addSigner(mint) + .buildAndExecute(); const position = params.positionPda.publicKey; const positionTokenAccount = params.positionTokenAccount; @@ -154,20 +150,20 @@ describe("position with token extensions management tests", () => { await toTx( ctx, isToken2022 - // test V2 - ? WhirlpoolIx.increaseLiquidityV2Ix(ctx.program, { - liquidityAmount: depositQuote.liquidityAmount, - tokenMaxA: depositQuote.tokenMaxA, - tokenMaxB: depositQuote.tokenMaxB, - ...baseParams, - }) - // test V1 - : WhirlpoolIx.increaseLiquidityIx(ctx.program, { - liquidityAmount: depositQuote.liquidityAmount, - tokenMaxA: depositQuote.tokenMaxA, - tokenMaxB: depositQuote.tokenMaxB, - ...baseParams, - }) + ? // test V2 + WhirlpoolIx.increaseLiquidityV2Ix(ctx.program, { + liquidityAmount: depositQuote.liquidityAmount, + tokenMaxA: depositQuote.tokenMaxA, + tokenMaxB: depositQuote.tokenMaxB, + ...baseParams, + }) + : // test V1 + WhirlpoolIx.increaseLiquidityIx(ctx.program, { + liquidityAmount: depositQuote.liquidityAmount, + tokenMaxA: depositQuote.tokenMaxA, + tokenMaxB: depositQuote.tokenMaxB, + ...baseParams, + }), ).buildAndExecute(); const positionStep1 = await fetcher.getPosition(position, IGNORE_CACHE); @@ -253,20 +249,20 @@ describe("position with token extensions management tests", () => { await toTx( ctx, isToken2022 - // test V2 - ? WhirlpoolIx.decreaseLiquidityV2Ix(ctx.program, { - liquidityAmount: depositQuote.liquidityAmount, - tokenMinA: ZERO_BN, - tokenMinB: ZERO_BN, - ...baseParams, - }) - // test V1 - : WhirlpoolIx.decreaseLiquidityIx(ctx.program, { - liquidityAmount: depositQuote.liquidityAmount, - tokenMinA: ZERO_BN, - tokenMinB: ZERO_BN, - ...baseParams, - }) + ? // test V2 + WhirlpoolIx.decreaseLiquidityV2Ix(ctx.program, { + liquidityAmount: depositQuote.liquidityAmount, + tokenMinA: ZERO_BN, + tokenMinB: ZERO_BN, + ...baseParams, + }) + : // test V1 + WhirlpoolIx.decreaseLiquidityIx(ctx.program, { + liquidityAmount: depositQuote.liquidityAmount, + tokenMinA: ZERO_BN, + tokenMinB: ZERO_BN, + ...baseParams, + }), ).buildAndExecute(); const positionStep4 = await fetcher.getPosition(position, IGNORE_CACHE); @@ -288,18 +284,18 @@ describe("position with token extensions management tests", () => { await toTx( ctx, isToken2022 - // test V2 - ? WhirlpoolIx.collectFeesV2Ix(ctx.program, { - ...baseParams, - tokenOwnerAccountA: feeAccountA, - tokenOwnerAccountB: feeAccountB, - }) - // test V1 - : WhirlpoolIx.collectFeesIx(ctx.program, { - ...baseParams, - tokenOwnerAccountA: feeAccountA, - tokenOwnerAccountB: feeAccountB, - }) + ? // test V2 + WhirlpoolIx.collectFeesV2Ix(ctx.program, { + ...baseParams, + tokenOwnerAccountA: feeAccountA, + tokenOwnerAccountB: feeAccountB, + }) + : // test V1 + WhirlpoolIx.collectFeesIx(ctx.program, { + ...baseParams, + tokenOwnerAccountA: feeAccountA, + tokenOwnerAccountB: feeAccountB, + }), ).buildAndExecute(); const positionStep5 = await fetcher.getPosition(position, IGNORE_CACHE); @@ -316,24 +312,24 @@ describe("position with token extensions management tests", () => { await toTx( ctx, isToken2022 - // test V2 - ? WhirlpoolIx.collectRewardV2Ix(ctx.program, { - ...baseParams, - rewardIndex: 0, - rewardMint: pool.getData().rewardInfos[0].mint, - rewardOwnerAccount: rewardAccount, - rewardVault: pool.getData().rewardInfos[0].vault, - rewardTokenProgram: TOKEN_PROGRAM_ID, - }) - // test V1 - : WhirlpoolIx.collectRewardIx(ctx.program, { - ...baseParams, - rewardIndex: 0, - rewardOwnerAccount: rewardAccount, - rewardVault: pool.getData().rewardInfos[0].vault, - }) + ? // test V2 + WhirlpoolIx.collectRewardV2Ix(ctx.program, { + ...baseParams, + rewardIndex: 0, + rewardMint: pool.getData().rewardInfos[0].mint, + rewardOwnerAccount: rewardAccount, + rewardVault: pool.getData().rewardInfos[0].vault, + rewardTokenProgram: TOKEN_PROGRAM_ID, + }) + : // test V1 + WhirlpoolIx.collectRewardIx(ctx.program, { + ...baseParams, + rewardIndex: 0, + rewardOwnerAccount: rewardAccount, + rewardVault: pool.getData().rewardInfos[0].vault, + }), ).buildAndExecute(); - + const positionStep6 = await fetcher.getPosition(position, IGNORE_CACHE); assert.ok(positionStep6!.rewardInfos[0].amountOwed.isZero()); @@ -347,8 +343,7 @@ describe("position with token extensions management tests", () => { positionMint: params.positionMint, positionTokenAccount: params.positionTokenAccount, }), - ) - .buildAndExecute(); + ).buildAndExecute(); checkClosed(params.positionPda.publicKey); }); @@ -364,27 +359,32 @@ describe("position with token extensions management tests", () => { const receiver = Keypair.generate(); // open - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - poolInitInfo.whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + poolInitInfo.whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, + ); builder - .addInstruction(WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params)) - .addSigner(mint); + .addInstruction( + WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), + ) + .addSigner(mint); // close - builder.addInstruction(WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { - positionAuthority: ctx.wallet.publicKey, - receiver: receiver.publicKey, - position: params.positionPda.publicKey, - positionMint: params.positionMint, - positionTokenAccount: params.positionTokenAccount, - })); + builder.addInstruction( + WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { + positionAuthority: ctx.wallet.publicKey, + receiver: receiver.publicKey, + position: params.positionPda.publicKey, + positionMint: params.positionMint, + positionTokenAccount: params.positionTokenAccount, + }), + ); await builder.buildAndExecute(); @@ -406,30 +406,35 @@ describe("position with token extensions management tests", () => { const builder = new TransactionBuilder(ctx.connection, ctx.wallet); const receiver = Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - poolInitInfo.whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + poolInitInfo.whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, + ); const numRepeat = 3; for (let i = 0; i < numRepeat; i++) { // open builder - .addInstruction(WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params)) - .addSigner(mint); + .addInstruction( + WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), + ) + .addSigner(mint); // close - builder.addInstruction(WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { - positionAuthority: ctx.wallet.publicKey, - receiver: receiver.publicKey, - position: params.positionPda.publicKey, - positionMint: params.positionMint, - positionTokenAccount: params.positionTokenAccount, - })); + builder.addInstruction( + WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { + positionAuthority: ctx.wallet.publicKey, + receiver: receiver.publicKey, + position: params.positionPda.publicKey, + positionMint: params.positionMint, + positionTokenAccount: params.positionTokenAccount, + }), + ); } await builder.buildAndExecute(); @@ -449,40 +454,45 @@ describe("position with token extensions management tests", () => { const tickLowerIndex = 0; const tickUpperIndex = 128; - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - poolInitInfo.whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + poolInitInfo.whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, + ); const numRepeat = 3; for (let i = 0; i < numRepeat; i++) { const builder = new TransactionBuilder(ctx.connection, ctx.wallet); const receiver = Keypair.generate(); - + // open builder - .addInstruction(WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params)) - .addSigner(mint); + .addInstruction( + WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), + ) + .addSigner(mint); // close - builder.addInstruction(WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { - positionAuthority: ctx.wallet.publicKey, - receiver: receiver.publicKey, - position: params.positionPda.publicKey, - positionMint: params.positionMint, - positionTokenAccount: params.positionTokenAccount, - })); + builder.addInstruction( + WhirlpoolIx.closePositionWithTokenExtensionsIx(ctx.program, { + positionAuthority: ctx.wallet.publicKey, + receiver: receiver.publicKey, + position: params.positionPda.publicKey, + positionMint: params.positionMint, + positionTokenAccount: params.positionTokenAccount, + }), + ); - await builder.buildAndExecute(undefined, {skipPreflight: true}); + await builder.buildAndExecute(undefined, { skipPreflight: true }); checkClosed(params.positionPda.publicKey); checkClosed(params.positionMint); checkClosed(params.positionTokenAccount); - + // receiver received the rent (= transaction have been executed) const received = await getRent(receiver.publicKey); assert.ok(received > 0); diff --git a/legacy-sdk/whirlpool/tests/integration/multi-ix/splash_pool.test.ts b/legacy-sdk/whirlpool/tests/integration/multi-ix/splash_pool.test.ts index c599d4228..c68ed924e 100644 --- a/legacy-sdk/whirlpool/tests/integration/multi-ix/splash_pool.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/multi-ix/splash_pool.test.ts @@ -3,9 +3,7 @@ import { DecimalUtil, Percentage, U64_MAX } from "@orca-so/common-sdk"; import type { PublicKey } from "@solana/web3.js"; import * as assert from "assert"; import BN from "bn.js"; -import type { - WhirlpoolClient, -} from "../../../src"; +import type { WhirlpoolClient } from "../../../src"; import { MAX_SQRT_PRICE_BN, MAX_TICK_INDEX, @@ -26,9 +24,7 @@ import { import { WhirlpoolContext } from "../../../src/context"; import { IGNORE_CACHE } from "../../../src/network/public/fetcher"; import { defaultConfirmOptions } from "../../utils/const"; -import { - initTestPoolWithTokens, -} from "../../utils/init-utils"; +import { initTestPoolWithTokens } from "../../utils/init-utils"; import { NO_TOKEN_EXTENSION_CONTEXT } from "../../../src/utils/public/token-extension-util"; import { MAX_U64, getTokenBalance } from "../../utils"; @@ -96,7 +92,8 @@ describe("splash pool tests", () => { // ExactIn, BtoA, min to ... { - figure: "(toB) |-----mS----l**********T*********|********************u-----x-----| (toA)", + figure: + "(toB) |-----mS----l**********T*********|********************u-----x-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MIN_TICK_INDEX + 1, poolLiquidity: powBN(2, 33), @@ -106,7 +103,8 @@ describe("splash pool tests", () => { expectedPartialFill: false, }, { - figure: "(toB) |-----mS----l********************|**********T*********u-----x-----| (toA)", + figure: + "(toB) |-----mS----l********************|**********T*********u-----x-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MIN_TICK_INDEX + 1, poolLiquidity: powBN(2, 33), @@ -116,7 +114,8 @@ describe("splash pool tests", () => { expectedPartialFill: false, }, { - figure: "(toB) |-----mS----l********************|********************u----Tx-----| (toA)", + figure: + "(toB) |-----mS----l********************|********************u----Tx-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MIN_TICK_INDEX + 1, poolLiquidity: powBN(2, 33), @@ -128,7 +127,8 @@ describe("splash pool tests", () => { // ExactIn, AtoB, max to ... { - figure: "(toB) |-----m-----l********************|**********T*********u----Sx-----| (toA)", + figure: + "(toB) |-----m-----l********************|**********T*********u----Sx-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MAX_TICK_INDEX - 1, poolLiquidity: powBN(2, 33), @@ -138,7 +138,8 @@ describe("splash pool tests", () => { expectedPartialFill: false, }, { - figure: "(toB) |-----m-----l**********T*********|********************u----Sx-----| (toA)", + figure: + "(toB) |-----m-----l**********T*********|********************u----Sx-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MAX_TICK_INDEX - 1, poolLiquidity: powBN(2, 33), @@ -148,7 +149,8 @@ describe("splash pool tests", () => { expectedPartialFill: false, }, { - figure: "(toB) |-----mT----l********************|********************u----Sx-----| (toA)", + figure: + "(toB) |-----mT----l********************|********************u----Sx-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MAX_TICK_INDEX - 1, poolLiquidity: powBN(2, 33), @@ -160,7 +162,8 @@ describe("splash pool tests", () => { // ExactIn, BtoA, 1 to ... { - figure: "(toB) |-----m-----l********************|S****T**************u-----x-----| (toA)", + figure: + "(toB) |-----m-----l********************|S****T**************u-----x-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: 0, poolLiquidity: powBN(2, 63), // to use the remaining 2^63 amount in the trade @@ -172,7 +175,8 @@ describe("splash pool tests", () => { // ExactIn, AtoB, 1 to ... { - figure: "(toB) |-----m-----l**************T****S|********************u-----x-----| (toA)", + figure: + "(toB) |-----m-----l**************T****S|********************u-----x-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: 0, poolLiquidity: powBN(2, 63), // to use the remaining 2^63 amount in the trade @@ -188,7 +192,8 @@ describe("splash pool tests", () => { // ExactOut, BtoA, min to ... { - figure: "(toB) |-----mS----l**********T*********|********************u-----x-----| (toA)", + figure: + "(toB) |-----mS----l**********T*********|********************u-----x-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MIN_TICK_INDEX + 1, poolLiquidity: powBN(2, 33), @@ -198,7 +203,8 @@ describe("splash pool tests", () => { expectedPartialFill: false, }, { - figure: "(toB) |-----mS----l********************|**********T*********u-----x-----| (toA)", + figure: + "(toB) |-----mS----l********************|**********T*********u-----x-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MIN_TICK_INDEX + 1, poolLiquidity: powBN(2, 33), @@ -208,7 +214,8 @@ describe("splash pool tests", () => { expectedPartialFill: false, }, { - figure: "(toB) |-----mS----l********************|********************u----Tx-----| (toA)", + figure: + "(toB) |-----mS----l********************|********************u----Tx-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MIN_TICK_INDEX + 1, poolLiquidity: powBN(2, 33), @@ -220,7 +227,8 @@ describe("splash pool tests", () => { // ExactOut, AtoB, max to ... { - figure: "(toB) |-----m-----l********************|**********T*********u----Sx-----| (toA)", + figure: + "(toB) |-----m-----l********************|**********T*********u----Sx-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MAX_TICK_INDEX - 1, poolLiquidity: powBN(2, 33), @@ -230,7 +238,8 @@ describe("splash pool tests", () => { expectedPartialFill: false, }, { - figure: "(toB) |-----m-----l**********T*********|********************u----Sx-----| (toA)", + figure: + "(toB) |-----m-----l**********T*********|********************u----Sx-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MAX_TICK_INDEX - 1, poolLiquidity: powBN(2, 33), @@ -240,7 +249,8 @@ describe("splash pool tests", () => { expectedPartialFill: false, }, { - figure: "(toB) |-----mT----l********************|********************u----Sx-----| (toA)", + figure: + "(toB) |-----mT----l********************|********************u----Sx-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: MAX_TICK_INDEX - 1, poolLiquidity: powBN(2, 33), @@ -252,7 +262,8 @@ describe("splash pool tests", () => { // ExactOut, BtoA, 1 to ... { - figure: "(toB) |-----m-----l********************|S****T**************u-----x-----| (toA)", + figure: + "(toB) |-----m-----l********************|S****T**************u-----x-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: 0, poolLiquidity: powBN(2, 63), // to use the remaining 2^63 amount in the trade @@ -264,7 +275,8 @@ describe("splash pool tests", () => { // ExactOut, AtoB, 1 to ... { - figure: "(toB) |-----m-----l**************T****S|********************u-----x-----| (toA)", + figure: + "(toB) |-----m-----l**************T****S|********************u-----x-----| (toA)", poolTickSpacing: 32768 + 128, poolInitialTickIndex: 0, poolLiquidity: powBN(2, 63), // to use the remaining 2^63 amount in the trade @@ -291,24 +303,24 @@ describe("splash pool tests", () => { const tradeAToB = tradeDirection === "AtoB"; const { whirlpoolPda, tokenAccountA, tokenAccountB } = - await initTestPoolWithTokens( - testCtx.whirlpoolCtx, - poolTickSpacing, - PriceMath.tickIndexToSqrtPriceX64(poolInitialTickIndex), - MAX_U64, - ); - + await initTestPoolWithTokens( + testCtx.whirlpoolCtx, + poolTickSpacing, + PriceMath.tickIndexToSqrtPriceX64(poolInitialTickIndex), + MAX_U64, + ); + const pool = await testCtx.whirlpoolClient.getPool( whirlpoolPda.publicKey, ); - + // SplashPool has only 2 TickArrays for negative and positive ticks - await (await pool.initTickArrayForTicks([ - -1, +1 - ]))!.buildAndExecute(); - - const fullRange = TickUtil.getFullRangeTickIndex(pool.getData().tickSpacing); - + await (await pool.initTickArrayForTicks([-1, +1]))!.buildAndExecute(); + + const fullRange = TickUtil.getFullRangeTickIndex( + pool.getData().tickSpacing, + ); + // provide liquidity const depositQuote = increaseLiquidityQuoteByLiquidityWithParams({ liquidity: poolLiquidity, @@ -319,61 +331,95 @@ describe("splash pool tests", () => { tickUpperIndex: fullRange[1], tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, }); - const txAndMint = await pool.openPosition(fullRange[0], fullRange[1], depositQuote); + const txAndMint = await pool.openPosition( + fullRange[0], + fullRange[1], + depositQuote, + ); await txAndMint.tx.buildAndExecute(); await pool.refreshData(); // reflect new liquidity - debug(`pool state: tick = ${pool.getData().tickCurrentIndex}, liquidity = ${depositQuote.liquidityAmount.toString()}, tokenA = ${depositQuote.tokenEstA.toString()}, tokenB = ${depositQuote.tokenEstB.toString()}`); - - const swapQuote = swapQuoteWithParams({ - amountSpecifiedIsInput: tradeAmountSpecifiedIsInput, - aToB: tradeAToB, - otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold(tradeAmountSpecifiedIsInput), - sqrtPriceLimit: tradeAToB ? MIN_SQRT_PRICE_BN : MAX_SQRT_PRICE_BN, - tickArrays: await SwapUtils.getTickArrays( - pool.getData().tickCurrentIndex, - pool.getData().tickSpacing, - tradeAToB, - testCtx.whirlpoolCtx.program.programId, - pool.getAddress(), - testCtx.whirlpoolCtx.fetcher, - IGNORE_CACHE, - ), - tokenAmount: tradeTokenAmount, - whirlpoolData: pool.getData(), - tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, - }, Percentage.fromFraction(0, 100)); + debug( + `pool state: tick = ${pool.getData().tickCurrentIndex}, liquidity = ${depositQuote.liquidityAmount.toString()}, tokenA = ${depositQuote.tokenEstA.toString()}, tokenB = ${depositQuote.tokenEstB.toString()}`, + ); + + const swapQuote = swapQuoteWithParams( + { + amountSpecifiedIsInput: tradeAmountSpecifiedIsInput, + aToB: tradeAToB, + otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold( + tradeAmountSpecifiedIsInput, + ), + sqrtPriceLimit: tradeAToB ? MIN_SQRT_PRICE_BN : MAX_SQRT_PRICE_BN, + tickArrays: await SwapUtils.getTickArrays( + pool.getData().tickCurrentIndex, + pool.getData().tickSpacing, + tradeAToB, + testCtx.whirlpoolCtx.program.programId, + pool.getAddress(), + testCtx.whirlpoolCtx.fetcher, + IGNORE_CACHE, + ), + tokenAmount: tradeTokenAmount, + whirlpoolData: pool.getData(), + tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, + }, + Percentage.fromFraction(0, 100), + ); const preTickIndex = pool.getData().tickCurrentIndex; - const [preOwnerA, preOwnerB] = await getTokenBalances(tokenAccountA, tokenAccountB); - const [preVaultA, preVaultB] = await getTokenBalances(pool.getData().tokenVaultA, pool.getData().tokenVaultB); + const [preOwnerA, preOwnerB] = await getTokenBalances( + tokenAccountA, + tokenAccountB, + ); + const [preVaultA, preVaultB] = await getTokenBalances( + pool.getData().tokenVaultA, + pool.getData().tokenVaultB, + ); await toTx( testCtx.whirlpoolCtx, - WhirlpoolIx.swapIx(testCtx.whirlpoolCtx.program, SwapUtils.getSwapParamsFromQuote( - swapQuote, - testCtx.whirlpoolCtx, - pool, - swapQuote.aToB ? tokenAccountA : tokenAccountB, - swapQuote.aToB ? tokenAccountB : tokenAccountA, - testCtx.provider.wallet.publicKey, - )), + WhirlpoolIx.swapIx( + testCtx.whirlpoolCtx.program, + SwapUtils.getSwapParamsFromQuote( + swapQuote, + testCtx.whirlpoolCtx, + pool, + swapQuote.aToB ? tokenAccountA : tokenAccountB, + swapQuote.aToB ? tokenAccountB : tokenAccountA, + testCtx.provider.wallet.publicKey, + ), + ), ).buildAndExecute(); await pool.refreshData(); // reflect new tickCurrentIndex const postTickIndex = pool.getData().tickCurrentIndex; - const [postOwnerA, postOwnerB] = await getTokenBalances(tokenAccountA, tokenAccountB); - const [postVaultA, postVaultB] = await getTokenBalances(pool.getData().tokenVaultA, pool.getData().tokenVaultB); + const [postOwnerA, postOwnerB] = await getTokenBalances( + tokenAccountA, + tokenAccountB, + ); + const [postVaultA, postVaultB] = await getTokenBalances( + pool.getData().tokenVaultA, + pool.getData().tokenVaultB, + ); // display pre & post debug(`amount: ${tradeTokenAmount.toString()}`); - debug(`estimate: ${swapQuote.estimatedAmountIn.toString()} --> ${swapQuote.estimatedAmountOut.toString()}`); - debug(`owner: A = ${preOwnerA.toString()} -> ${postOwnerA.toString()}, B = ${preOwnerB.toString()} -> ${postOwnerB.toString()}`); - debug(`vault: A = ${preVaultA.toString()} -> ${postVaultA.toString()}, B = ${preVaultB.toString()} -> ${postVaultB.toString()}`); + debug( + `estimate: ${swapQuote.estimatedAmountIn.toString()} --> ${swapQuote.estimatedAmountOut.toString()}`, + ); + debug( + `owner: A = ${preOwnerA.toString()} -> ${postOwnerA.toString()}, B = ${preOwnerB.toString()} -> ${postOwnerB.toString()}`, + ); + debug( + `vault: A = ${preVaultA.toString()} -> ${postVaultA.toString()}, B = ${preVaultB.toString()} -> ${postVaultB.toString()}`, + ); debug(`tick index: ${preTickIndex} --> ${postTickIndex}`); // verify: partial fill - const actualAmount = swapQuote.amountSpecifiedIsInput ? swapQuote.estimatedAmountIn : swapQuote.estimatedAmountOut; + const actualAmount = swapQuote.amountSpecifiedIsInput + ? swapQuote.estimatedAmountIn + : swapQuote.estimatedAmountOut; if (variation.expectedPartialFill) { assert.ok(actualAmount.lt(tradeTokenAmount)); } else { @@ -385,32 +431,49 @@ describe("splash pool tests", () => { const diffOwnerB = postOwnerB.sub(preOwnerB); const diffVaultA = postVaultA.sub(preVaultA); const diffVaultB = postVaultB.sub(preVaultB); - debug(`diff: owner A = ${diffOwnerA.toString()}, owner B = ${diffOwnerB.toString()}`); - debug(`estimated: in = ${swapQuote.estimatedAmountIn.toString()}, out = ${swapQuote.estimatedAmountOut.toString()}`); - debug(`sqrtPrice: quote = ${swapQuote.estimatedEndSqrtPrice.toString()}, pool = ${pool.getData().sqrtPrice.toString()}`); + debug( + `diff: owner A = ${diffOwnerA.toString()}, owner B = ${diffOwnerB.toString()}`, + ); + debug( + `estimated: in = ${swapQuote.estimatedAmountIn.toString()}, out = ${swapQuote.estimatedAmountOut.toString()}`, + ); + debug( + `sqrtPrice: quote = ${swapQuote.estimatedEndSqrtPrice.toString()}, pool = ${pool.getData().sqrtPrice.toString()}`, + ); assert.ok(diffOwnerA.eq(diffVaultA.neg())); assert.ok(diffOwnerB.eq(diffVaultB.neg())); - assert.ok(diffOwnerA.eq(tradeAToB ? swapQuote.estimatedAmountIn.neg() : swapQuote.estimatedAmountOut)); - assert.ok(diffOwnerB.eq(tradeAToB ? swapQuote.estimatedAmountOut : swapQuote.estimatedAmountIn.neg())); + assert.ok( + diffOwnerA.eq( + tradeAToB + ? swapQuote.estimatedAmountIn.neg() + : swapQuote.estimatedAmountOut, + ), + ); + assert.ok( + diffOwnerB.eq( + tradeAToB + ? swapQuote.estimatedAmountOut + : swapQuote.estimatedAmountIn.neg(), + ), + ); assert.ok(swapQuote.estimatedEndSqrtPrice.eq(pool.getData().sqrtPrice)); - assert.ok(swapQuote.estimatedEndTickIndex === pool.getData().tickCurrentIndex); + assert.ok( + swapQuote.estimatedEndTickIndex === pool.getData().tickCurrentIndex, + ); }); }); }); - async function getTokenBalances(tokenAccountA: PublicKey, tokenAccountB: PublicKey): Promise<[BN, BN]> { + async function getTokenBalances( + tokenAccountA: PublicKey, + tokenAccountB: PublicKey, + ): Promise<[BN, BN]> { const tokenVaultA = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountA, - ), + await getTokenBalance(provider, tokenAccountA), ); const tokenVaultB = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountB, - ), + await getTokenBalance(provider, tokenAccountB), ); return [tokenVaultA, tokenVaultB]; } @@ -428,23 +491,23 @@ describe("splash pool tests", () => { const tradeAToB = false; const { whirlpoolPda, tokenAccountA, tokenAccountB } = - await initTestPoolWithTokens( - testCtx.whirlpoolCtx, - poolTickSpacing, - PriceMath.tickIndexToSqrtPriceX64(poolInitialTickIndex), - MAX_U64, - ); - + await initTestPoolWithTokens( + testCtx.whirlpoolCtx, + poolTickSpacing, + PriceMath.tickIndexToSqrtPriceX64(poolInitialTickIndex), + MAX_U64, + ); + const pool = await testCtx.whirlpoolClient.getPool( whirlpoolPda.publicKey, ); - - await (await pool.initTickArrayForTicks([ - -1, +1 - ]))!.buildAndExecute(); - - const fullRange = TickUtil.getFullRangeTickIndex(pool.getData().tickSpacing); - + + await (await pool.initTickArrayForTicks([-1, +1]))!.buildAndExecute(); + + const fullRange = TickUtil.getFullRangeTickIndex( + pool.getData().tickSpacing, + ); + // provide liquidity const depositQuote = increaseLiquidityQuoteByLiquidityWithParams({ liquidity: poolLiquidity, @@ -455,32 +518,43 @@ describe("splash pool tests", () => { tickUpperIndex: fullRange[1], tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, }); - const txAndMint = await pool.openPosition(fullRange[0], fullRange[1], depositQuote); + const txAndMint = await pool.openPosition( + fullRange[0], + fullRange[1], + depositQuote, + ); await txAndMint.tx.buildAndExecute(); await pool.refreshData(); // reflect new liquidity // try to output all tokenA const tradeTokenAmount = depositQuote.tokenEstA; - - await assert.rejects(async () => swapQuoteWithParams({ - amountSpecifiedIsInput: tradeAmountSpecifiedIsInput, - aToB: tradeAToB, - otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold(tradeAmountSpecifiedIsInput), - sqrtPriceLimit: tradeAToB ? MIN_SQRT_PRICE_BN : MAX_SQRT_PRICE_BN, - tickArrays: await SwapUtils.getTickArrays( - pool.getData().tickCurrentIndex, - pool.getData().tickSpacing, - tradeAToB, - testCtx.whirlpoolCtx.program.programId, - pool.getAddress(), - testCtx.whirlpoolCtx.fetcher, - IGNORE_CACHE, + + await assert.rejects( + async () => + swapQuoteWithParams( + { + amountSpecifiedIsInput: tradeAmountSpecifiedIsInput, + aToB: tradeAToB, + otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold( + tradeAmountSpecifiedIsInput, + ), + sqrtPriceLimit: tradeAToB ? MIN_SQRT_PRICE_BN : MAX_SQRT_PRICE_BN, + tickArrays: await SwapUtils.getTickArrays( + pool.getData().tickCurrentIndex, + pool.getData().tickSpacing, + tradeAToB, + testCtx.whirlpoolCtx.program.programId, + pool.getAddress(), + testCtx.whirlpoolCtx.fetcher, + IGNORE_CACHE, + ), + tokenAmount: tradeTokenAmount, + whirlpoolData: pool.getData(), + tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, + }, + Percentage.fromFraction(0, 100), ), - tokenAmount: tradeTokenAmount, - whirlpoolData: pool.getData(), - tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, - }, Percentage.fromFraction(0, 100)), - /MulShiftRight overflowed u128/ // at getAmountUnfixedDelta for tokenB (too much tokenB is required) + /MulShiftRight overflowed u128/, // at getAmountUnfixedDelta for tokenB (too much tokenB is required) ); await assert.rejects( @@ -498,14 +572,32 @@ describe("splash pool tests", () => { tokenVaultA: pool.getData().tokenVaultA, tokenVaultB: pool.getData().tokenVaultB, whirlpool: pool.getAddress(), - tickArray0: PDAUtil.getTickArrayFromTickIndex(0, poolTickSpacing, pool.getAddress(), testCtx.whirlpoolCtx.program.programId).publicKey, - tickArray1: PDAUtil.getTickArrayFromTickIndex(0, poolTickSpacing, pool.getAddress(), testCtx.whirlpoolCtx.program.programId).publicKey, - tickArray2: PDAUtil.getTickArrayFromTickIndex(0, poolTickSpacing, pool.getAddress(), testCtx.whirlpoolCtx.program.programId).publicKey, - oracle: PDAUtil.getOracle(testCtx.whirlpoolCtx.program.programId, pool.getAddress()).publicKey, - }) + tickArray0: PDAUtil.getTickArrayFromTickIndex( + 0, + poolTickSpacing, + pool.getAddress(), + testCtx.whirlpoolCtx.program.programId, + ).publicKey, + tickArray1: PDAUtil.getTickArrayFromTickIndex( + 0, + poolTickSpacing, + pool.getAddress(), + testCtx.whirlpoolCtx.program.programId, + ).publicKey, + tickArray2: PDAUtil.getTickArrayFromTickIndex( + 0, + poolTickSpacing, + pool.getAddress(), + testCtx.whirlpoolCtx.program.programId, + ).publicKey, + oracle: PDAUtil.getOracle( + testCtx.whirlpoolCtx.program.programId, + pool.getAddress(), + ).publicKey, + }), ).buildAndExecute(), - /MultiplicationShiftRightOverflow/ // at get_amount_unfixed_delta for tokenB (too much tokenB is required) - ); + /MultiplicationShiftRightOverflow/, // at get_amount_unfixed_delta for tokenB (too much tokenB is required) + ); }); // A to B (too much tokenA is required) @@ -517,23 +609,23 @@ describe("splash pool tests", () => { const tradeAToB = true; const { whirlpoolPda, tokenAccountA, tokenAccountB } = - await initTestPoolWithTokens( - testCtx.whirlpoolCtx, - poolTickSpacing, - PriceMath.tickIndexToSqrtPriceX64(poolInitialTickIndex), - MAX_U64, - ); - + await initTestPoolWithTokens( + testCtx.whirlpoolCtx, + poolTickSpacing, + PriceMath.tickIndexToSqrtPriceX64(poolInitialTickIndex), + MAX_U64, + ); + const pool = await testCtx.whirlpoolClient.getPool( whirlpoolPda.publicKey, ); - - await (await pool.initTickArrayForTicks([ - -1, +1 - ]))!.buildAndExecute(); - - const fullRange = TickUtil.getFullRangeTickIndex(pool.getData().tickSpacing); - + + await (await pool.initTickArrayForTicks([-1, +1]))!.buildAndExecute(); + + const fullRange = TickUtil.getFullRangeTickIndex( + pool.getData().tickSpacing, + ); + // provide liquidity const depositQuote = increaseLiquidityQuoteByLiquidityWithParams({ liquidity: poolLiquidity, @@ -544,32 +636,43 @@ describe("splash pool tests", () => { tickUpperIndex: fullRange[1], tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, }); - const txAndMint = await pool.openPosition(fullRange[0], fullRange[1], depositQuote); + const txAndMint = await pool.openPosition( + fullRange[0], + fullRange[1], + depositQuote, + ); await txAndMint.tx.buildAndExecute(); await pool.refreshData(); // reflect new liquidity // try to output all tokenB const tradeTokenAmount = depositQuote.tokenEstB; - - await assert.rejects(async () => swapQuoteWithParams({ - amountSpecifiedIsInput: tradeAmountSpecifiedIsInput, - aToB: tradeAToB, - otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold(tradeAmountSpecifiedIsInput), - sqrtPriceLimit: tradeAToB ? MIN_SQRT_PRICE_BN : MAX_SQRT_PRICE_BN, - tickArrays: await SwapUtils.getTickArrays( - pool.getData().tickCurrentIndex, - pool.getData().tickSpacing, - tradeAToB, - testCtx.whirlpoolCtx.program.programId, - pool.getAddress(), - testCtx.whirlpoolCtx.fetcher, - IGNORE_CACHE, + + await assert.rejects( + async () => + swapQuoteWithParams( + { + amountSpecifiedIsInput: tradeAmountSpecifiedIsInput, + aToB: tradeAToB, + otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold( + tradeAmountSpecifiedIsInput, + ), + sqrtPriceLimit: tradeAToB ? MIN_SQRT_PRICE_BN : MAX_SQRT_PRICE_BN, + tickArrays: await SwapUtils.getTickArrays( + pool.getData().tickCurrentIndex, + pool.getData().tickSpacing, + tradeAToB, + testCtx.whirlpoolCtx.program.programId, + pool.getAddress(), + testCtx.whirlpoolCtx.fetcher, + IGNORE_CACHE, + ), + tokenAmount: tradeTokenAmount, + whirlpoolData: pool.getData(), + tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, + }, + Percentage.fromFraction(0, 100), ), - tokenAmount: tradeTokenAmount, - whirlpoolData: pool.getData(), - tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, - }, Percentage.fromFraction(0, 100)), - /Results larger than U64/ // at getAmountUnfixedDelta for tokenA (too much tokenA is required) + /Results larger than U64/, // at getAmountUnfixedDelta for tokenA (too much tokenA is required) ); await assert.rejects( @@ -587,14 +690,34 @@ describe("splash pool tests", () => { tokenVaultA: pool.getData().tokenVaultA, tokenVaultB: pool.getData().tokenVaultB, whirlpool: pool.getAddress(), - tickArray0: PDAUtil.getTickArrayFromTickIndex(0, poolTickSpacing, pool.getAddress(), testCtx.whirlpoolCtx.program.programId).publicKey, - tickArray1: PDAUtil.getTickArrayFromTickIndex(0, poolTickSpacing, pool.getAddress(), testCtx.whirlpoolCtx.program.programId, -1).publicKey, - tickArray2: PDAUtil.getTickArrayFromTickIndex(0, poolTickSpacing, pool.getAddress(), testCtx.whirlpoolCtx.program.programId, -1).publicKey, - oracle: PDAUtil.getOracle(testCtx.whirlpoolCtx.program.programId, pool.getAddress()).publicKey, - }) + tickArray0: PDAUtil.getTickArrayFromTickIndex( + 0, + poolTickSpacing, + pool.getAddress(), + testCtx.whirlpoolCtx.program.programId, + ).publicKey, + tickArray1: PDAUtil.getTickArrayFromTickIndex( + 0, + poolTickSpacing, + pool.getAddress(), + testCtx.whirlpoolCtx.program.programId, + -1, + ).publicKey, + tickArray2: PDAUtil.getTickArrayFromTickIndex( + 0, + poolTickSpacing, + pool.getAddress(), + testCtx.whirlpoolCtx.program.programId, + -1, + ).publicKey, + oracle: PDAUtil.getOracle( + testCtx.whirlpoolCtx.program.programId, + pool.getAddress(), + ).publicKey, + }), ).buildAndExecute(), - /TokenMaxExceeded/ // at get_amount_unfixed_delta for tokenA (too much tokenA is required) - ); + /TokenMaxExceeded/, // at get_amount_unfixed_delta for tokenA (too much tokenA is required) + ); }); }); @@ -602,24 +725,24 @@ describe("splash pool tests", () => { const tickSpacingSplash128 = 32768 + 128; const { poolInitInfo, whirlpoolPda, tokenAccountA, tokenAccountB } = - await initTestPoolWithTokens( - testCtx.whirlpoolCtx, - tickSpacingSplash128, - PriceMath.tickIndexToSqrtPriceX64(0), // 1 B/A - new BN(2_000_000_000), - ); + await initTestPoolWithTokens( + testCtx.whirlpoolCtx, + tickSpacingSplash128, + PriceMath.tickIndexToSqrtPriceX64(0), // 1 B/A + new BN(2_000_000_000), + ); - const pool = await testCtx.whirlpoolClient.getPool( - whirlpoolPda.publicKey, - ); + const pool = await testCtx.whirlpoolClient.getPool(whirlpoolPda.publicKey); // [-2,894,848 ][0 ][ await (await pool.initTickArrayForTicks([ // SplashPool has only 2 TickArrays for negative and positive ticks - -1, +1 + -1, +1, ]))!.buildAndExecute(); - const fullRange = TickUtil.getFullRangeTickIndex(pool.getData().tickSpacing); + const fullRange = TickUtil.getFullRangeTickIndex( + pool.getData().tickSpacing, + ); // create 2 position (small & large) const depositQuoteSmall = increaseLiquidityQuoteByInputToken( @@ -631,7 +754,11 @@ describe("splash pool tests", () => { pool, NO_TOKEN_EXTENSION_CONTEXT, ); - const small = await pool.openPosition(fullRange[0], fullRange[1], depositQuoteSmall); + const small = await pool.openPosition( + fullRange[0], + fullRange[1], + depositQuoteSmall, + ); await small.tx.buildAndExecute(); const depositQuoteLarge = increaseLiquidityQuoteByInputToken( @@ -643,7 +770,11 @@ describe("splash pool tests", () => { pool, NO_TOKEN_EXTENSION_CONTEXT, ); - const large = await pool.openPosition(fullRange[0], fullRange[1], depositQuoteLarge); + const large = await pool.openPosition( + fullRange[0], + fullRange[1], + depositQuoteLarge, + ); await large.tx.buildAndExecute(); await pool.refreshData(); @@ -671,7 +802,10 @@ describe("splash pool tests", () => { ); // close large position - const largePosition = PDAUtil.getPosition(testCtx.whirlpoolCtx.program.programId, large.positionMint).publicKey; + const largePosition = PDAUtil.getPosition( + testCtx.whirlpoolCtx.program.programId, + large.positionMint, + ).publicKey; const closeTx = await pool.closePosition( largePosition, @@ -714,14 +848,13 @@ describe("splash pool tests", () => { await pool.refreshData(); - // input (partial) + // input (partial) assert.ok(preA.sub(postA).lt(swapQuote.estimatedAmountIn)); // output (partial) assert.ok(postB.sub(preB).lt(swapQuote.estimatedAmountOut)); // hit min assert.ok(pool.getData().sqrtPrice.eq(MIN_SQRT_PRICE_BN)); }); - }); function powBN(base: number, exp: number): BN { @@ -731,4 +864,4 @@ function powBN(base: number, exp: number): BN { function debug(msg: string) { if (!DEBUG_OUTPUT) return; console.debug(msg); -} \ No newline at end of file +} diff --git a/legacy-sdk/whirlpool/tests/integration/open_position_with_token_extensions.test.ts b/legacy-sdk/whirlpool/tests/integration/open_position_with_token_extensions.test.ts index b656833ef..dc1431a6b 100644 --- a/legacy-sdk/whirlpool/tests/integration/open_position_with_token_extensions.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/open_position_with_token_extensions.test.ts @@ -14,15 +14,12 @@ import { createMint, ASSOCIATED_TOKEN_PROGRAM_ID, } from "@solana/spl-token"; -import { unpack as unpackTokenMetadata } from '@solana/spl-token-metadata'; -import type { TokenMetadata } from '@solana/spl-token-metadata'; +import { unpack as unpackTokenMetadata } from "@solana/spl-token-metadata"; +import type { TokenMetadata } from "@solana/spl-token-metadata"; import { Keypair, SystemProgram } from "@solana/web3.js"; import type { PublicKey } from "@solana/web3.js"; import * as assert from "assert"; -import type { - InitPoolParams, - PositionData, -} from "../../src"; +import type { InitPoolParams, PositionData } from "../../src"; import { IGNORE_CACHE, MAX_TICK_INDEX, @@ -33,12 +30,7 @@ import { WhirlpoolIx, toTx, } from "../../src"; -import { - ONE_SOL, - TickSpacing, - ZERO_BN, - systemTransferTx, -} from "../utils"; +import { ONE_SOL, TickSpacing, ZERO_BN, systemTransferTx } from "../utils"; import { defaultConfirmOptions } from "../utils/const"; import { initTestPool } from "../utils/init-utils"; import { generateDefaultOpenPositionWithTokenExtensionsParams } from "../utils/test-builders"; @@ -110,7 +102,10 @@ describe("open_position_with_token_extensions", () => { withTokenMetadataExtension: boolean, poolAddress: PublicKey, ) { - const positionPda = PDAUtil.getPosition(ctx.program.programId, positionMint); + const positionPda = PDAUtil.getPosition( + ctx.program.programId, + positionMint, + ); const mint = await fetcher.getMintInfo(positionMint, IGNORE_CACHE); @@ -130,7 +125,8 @@ describe("open_position_with_token_extensions", () => { const mintAccount = await ctx.connection.getAccountInfo(positionMint); assert.ok(mintAccount !== null); const dataLength = mintAccount.data.length; - const rentRequired = await ctx.connection.getMinimumBalanceForRentExemption(dataLength); + const rentRequired = + await ctx.connection.getMinimumBalanceForRentExemption(dataLength); assert.ok(mintAccount.lamports === rentRequired); // check initialized extensions @@ -164,12 +160,20 @@ describe("open_position_with_token_extensions", () => { // check TokenMetadata extension const tokenMetadata = (() => { - const data = getExtensionData(ExtensionType.TokenMetadata, mint.tlvData); + const data = getExtensionData( + ExtensionType.TokenMetadata, + mint.tlvData, + ); if (data === null) return null; return unpackTokenMetadata(data); })(); assert.ok(tokenMetadata !== null); - checkMetadata(tokenMetadata, positionMint, poolAddress, positionPda.publicKey); + checkMetadata( + tokenMetadata, + positionMint, + poolAddress, + positionPda.publicKey, + ); } } @@ -178,7 +182,10 @@ describe("open_position_with_token_extensions", () => { positionMint: PublicKey, owner: PublicKey, ) { - const tokenAccount = await fetcher.getTokenInfo(positionTokenAccount, IGNORE_CACHE); + const tokenAccount = await fetcher.getTokenInfo( + positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(tokenAccount !== null); assert.ok(tokenAccount.tokenProgram.equals(TOKEN_2022_PROGRAM_ID)); @@ -216,21 +223,29 @@ describe("open_position_with_token_extensions", () => { const withTokenMetadataExtension = true; // open position - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - withTokenMetadataExtension, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + withTokenMetadataExtension, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), - ).addSigner(mint).prependInstruction(useMaxCU()).buildAndExecute(); + ) + .addSigner(mint) + .prependInstruction(useMaxCU()) + .buildAndExecute(); // check Mint state (with metadata) - await checkMintState(params.positionMint, withTokenMetadataExtension, whirlpoolPda.publicKey); + await checkMintState( + params.positionMint, + withTokenMetadataExtension, + whirlpoolPda.publicKey, + ); // check TokenAccount state await checkTokenAccountState( @@ -247,21 +262,28 @@ describe("open_position_with_token_extensions", () => { const withTokenMetadataExtension = false; // open position - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - withTokenMetadataExtension, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + withTokenMetadataExtension, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), - ).addSigner(mint).buildAndExecute(); + ) + .addSigner(mint) + .buildAndExecute(); // check Mint state (with metadata) - await checkMintState(params.positionMint, withTokenMetadataExtension, whirlpoolPda.publicKey); + await checkMintState( + params.positionMint, + withTokenMetadataExtension, + whirlpoolPda.publicKey, + ); // check TokenAccount state await checkTokenAccountState( @@ -275,22 +297,23 @@ describe("open_position_with_token_extensions", () => { }); it("succeeds when funder is different than account paying for transaction fee", async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, // owner - funderKeypair.publicKey, // funder - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, // owner + funderKeypair.publicKey, // funder + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .addSigner(funderKeypair) - .buildAndExecute(); + .addSigner(mint) + .addSigner(funderKeypair) + .buildAndExecute(); await checkInitialPositionState(params); }); @@ -298,43 +321,48 @@ describe("open_position_with_token_extensions", () => { it("succeeds when owner is different than account paying for transaction fee", async () => { const ownerKeypair = anchor.web3.Keypair.generate(); - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ownerKeypair.publicKey, // owner - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ownerKeypair.publicKey, // owner + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); await checkInitialPositionState(params); - const tokenAccount = await fetcher.getTokenInfo(params.positionTokenAccount, IGNORE_CACHE); + const tokenAccount = await fetcher.getTokenInfo( + params.positionTokenAccount, + IGNORE_CACHE, + ); assert.ok(tokenAccount !== null); assert.ok(tokenAccount.owner.equals(ownerKeypair.publicKey)); }); it("should be failed: mint one more position token", async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - ctx.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + ctx.wallet.publicKey, + ); await toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(); + .addSigner(mint) + .buildAndExecute(); await checkInitialPositionState(params); @@ -362,22 +390,23 @@ describe("open_position_with_token_extensions", () => { describe("should be failed: invalid ticks", () => { async function assertTicksFail(lowerTick: number, upperTick: number) { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - lowerTick, - upperTick, - provider.wallet.publicKey, - ); - + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + lowerTick, + upperTick, + provider.wallet.publicKey, + ); + await assert.rejects( toTx( ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(), + .addSigner(mint) + .buildAndExecute(), /0x177a/, // InvalidTickIndex ); } @@ -412,36 +441,45 @@ describe("open_position_with_token_extensions", () => { let defaultMint: Keypair; beforeAll(async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, // owner - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, // owner + ); defaultParams = params; defaultMint = mint; }); it("no signature of funder", async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, // owner - funderKeypair.publicKey, // funder - ); - - const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params).instructions[0]; + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, // owner + funderKeypair.publicKey, // funder + ); + + const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx( + ctx.program, + params, + ).instructions[0]; // drop isSigner flag const keysWithoutSign = ix.keys.map((key) => { if (key.pubkey.equals(funderKeypair.publicKey)) { - return { pubkey: key.pubkey, isSigner: false, isWritable: key.isWritable }; + return { + pubkey: key.pubkey, + isSigner: false, + isWritable: key.isWritable, + }; } return key; }); @@ -451,18 +489,15 @@ describe("open_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithoutSign], - cleanupInstructions: [], - signers: [], - } - ) - .addSigner(mint) - // no signature of funder - .buildAndExecute(), - /0xbc2/ // AccountNotSigner + toTx(ctx, { + instructions: [ixWithoutSign], + cleanupInstructions: [], + signers: [], + }) + .addSigner(mint) + // no signature of funder + .buildAndExecute(), + /0xbc2/, // AccountNotSigner ); }); @@ -472,22 +507,32 @@ describe("open_position_with_token_extensions", () => { ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, { ...defaultParams, - positionPda: PDAUtil.getPosition(ctx.program.programId, Keypair.generate().publicKey), - }) + positionPda: PDAUtil.getPosition( + ctx.program.programId, + Keypair.generate().publicKey, + ), + }), ) - .addSigner(defaultMint) - .buildAndExecute(), - /0x7d6/ // ConstraintSeeds + .addSigner(defaultMint) + .buildAndExecute(), + /0x7d6/, // ConstraintSeeds ); }); it("no signature of position mint", async () => { - const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, defaultParams).instructions[0]; + const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx( + ctx.program, + defaultParams, + ).instructions[0]; // drop isSigner flag const keysWithoutSign = ix.keys.map((key) => { if (key.pubkey.equals(defaultParams.positionMint)) { - return { pubkey: key.pubkey, isSigner: false, isWritable: key.isWritable }; + return { + pubkey: key.pubkey, + isSigner: false, + isWritable: key.isWritable, + }; } return key; }); @@ -497,29 +542,27 @@ describe("open_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithoutSign], - cleanupInstructions: [], - signers: [], - } - ) - // no signature of position mint - .buildAndExecute(), - /0xbc2/ // AccountNotSigner + toTx(ctx, { + instructions: [ixWithoutSign], + cleanupInstructions: [], + signers: [], + }) + // no signature of position mint + .buildAndExecute(), + /0xbc2/, // AccountNotSigner ); }); it("position mint already initialized", async () => { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( - ctx, - whirlpoolPda.publicKey, - true, - tickLowerIndex, - tickUpperIndex, - provider.wallet.publicKey, - ); + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpoolPda.publicKey, + true, + tickLowerIndex, + tickUpperIndex, + provider.wallet.publicKey, + ); await createMint( ctx.connection, @@ -528,11 +571,14 @@ describe("open_position_with_token_extensions", () => { null, 6, mint, - {commitment: "confirmed"}, + { commitment: "confirmed" }, TOKEN_2022_PROGRAM_ID, ); - const created = await fetcher.getMintInfo(params.positionMint, IGNORE_CACHE); + const created = await fetcher.getMintInfo( + params.positionMint, + IGNORE_CACHE, + ); assert.ok(created !== null); await assert.rejects( @@ -540,9 +586,9 @@ describe("open_position_with_token_extensions", () => { ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ) - .addSigner(mint) - .buildAndExecute(), - /already in use/ + .addSigner(mint) + .buildAndExecute(), + /already in use/, ); }); @@ -561,11 +607,11 @@ describe("open_position_with_token_extensions", () => { WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, { ...defaultParams, positionTokenAccount: ataForAnotherMint, - }) + }), ) - .addSigner(defaultMint) - .buildAndExecute(), - /An account required by the instruction is missing/ // missing valid ATA address + .addSigner(defaultMint) + .buildAndExecute(), + /An account required by the instruction is missing/, // missing valid ATA address ); }); @@ -577,7 +623,9 @@ describe("open_position_with_token_extensions", () => { TOKEN_PROGRAM_ID, ); - assert.ok(!defaultParams.positionTokenAccount.equals(ataWithTokenProgram)); + assert.ok( + !defaultParams.positionTokenAccount.equals(ataWithTokenProgram), + ); await assert.rejects( toTx( @@ -585,11 +633,11 @@ describe("open_position_with_token_extensions", () => { WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, { ...defaultParams, positionTokenAccount: ataWithTokenProgram, - }) + }), ) - .addSigner(defaultMint) - .buildAndExecute(), - /An account required by the instruction is missing/ // missing valid ATA address + .addSigner(defaultMint) + .buildAndExecute(), + /An account required by the instruction is missing/, // missing valid ATA address ); }); @@ -601,11 +649,11 @@ describe("open_position_with_token_extensions", () => { WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, { ...defaultParams, whirlpool: Keypair.generate().publicKey, - }) + }), ) - .addSigner(defaultMint) - .buildAndExecute(), - /0xbc4/ // AccountNotInitialized + .addSigner(defaultMint) + .buildAndExecute(), + /0xbc4/, // AccountNotInitialized ); // not Whirlpool account @@ -615,16 +663,19 @@ describe("open_position_with_token_extensions", () => { WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, { ...defaultParams, whirlpool: poolInitInfo.whirlpoolsConfig, - }) + }), ) - .addSigner(defaultMint) - .buildAndExecute(), - /0xbba/ // AccountDiscriminatorMismatch + .addSigner(defaultMint) + .buildAndExecute(), + /0xbba/, // AccountDiscriminatorMismatch ); }); it("invalid token 2022 program", async () => { - const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, defaultParams).instructions[0]; + const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx( + ctx.program, + defaultParams, + ).instructions[0]; const ixWithWrongAccount = { ...ix, keys: ix.keys.map((key) => { @@ -636,22 +687,22 @@ describe("open_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithWrongAccount], - cleanupInstructions: [], - signers: [], - } - ) - .addSigner(defaultMint) - .buildAndExecute(), - /0xbc0/ // InvalidProgramId + toTx(ctx, { + instructions: [ixWithWrongAccount], + cleanupInstructions: [], + signers: [], + }) + .addSigner(defaultMint) + .buildAndExecute(), + /0xbc0/, // InvalidProgramId ); }); it("invalid system program", async () => { - const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, defaultParams).instructions[0]; + const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx( + ctx.program, + defaultParams, + ).instructions[0]; const ixWithWrongAccount = { ...ix, keys: ix.keys.map((key) => { @@ -663,22 +714,22 @@ describe("open_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithWrongAccount], - cleanupInstructions: [], - signers: [], - } - ) - .addSigner(defaultMint) - .buildAndExecute(), - /0xbc0/ // InvalidProgramId + toTx(ctx, { + instructions: [ixWithWrongAccount], + cleanupInstructions: [], + signers: [], + }) + .addSigner(defaultMint) + .buildAndExecute(), + /0xbc0/, // InvalidProgramId ); }); it("invalid associated token program", async () => { - const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, defaultParams).instructions[0]; + const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx( + ctx.program, + defaultParams, + ).instructions[0]; const ixWithWrongAccount = { ...ix, keys: ix.keys.map((key) => { @@ -690,22 +741,22 @@ describe("open_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithWrongAccount], - cleanupInstructions: [], - signers: [], - } - ) - .addSigner(defaultMint) - .buildAndExecute(), - /0xbc0/ // InvalidProgramId + toTx(ctx, { + instructions: [ixWithWrongAccount], + cleanupInstructions: [], + signers: [], + }) + .addSigner(defaultMint) + .buildAndExecute(), + /0xbc0/, // InvalidProgramId ); }); it("invalid metadata update auth", async () => { - const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, defaultParams).instructions[0]; + const ix = WhirlpoolIx.openPositionWithTokenExtensionsIx( + ctx.program, + defaultParams, + ).instructions[0]; const ixWithWrongAccount = { ...ix, keys: ix.keys.map((key) => { @@ -717,17 +768,14 @@ describe("open_position_with_token_extensions", () => { }; await assert.rejects( - toTx( - ctx, - { - instructions: [ixWithWrongAccount], - cleanupInstructions: [], - signers: [], - } - ) - .addSigner(defaultMint) - .buildAndExecute(), - /0x7dc/ // ConstraintAddress + toTx(ctx, { + instructions: [ixWithWrongAccount], + cleanupInstructions: [], + signers: [], + }) + .addSigner(defaultMint) + .buildAndExecute(), + /0x7dc/, // ConstraintAddress ); }); }); diff --git a/legacy-sdk/whirlpool/tests/integration/swap.test.ts b/legacy-sdk/whirlpool/tests/integration/swap.test.ts index f3f7a1b55..b7a76a377 100644 --- a/legacy-sdk/whirlpool/tests/integration/swap.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/swap.test.ts @@ -1,6 +1,6 @@ import * as anchor from "@coral-xyz/anchor"; import { web3 } from "@coral-xyz/anchor"; -import { MathUtil, Percentage } from "@orca-so/common-sdk"; +import { MathUtil, Percentage } from "@orca-so/common-sdk"; import type { PDA } from "@orca-so/common-sdk"; import * as assert from "assert"; import BN from "bn.js"; @@ -2068,8 +2068,12 @@ describe("swap", () => { let oraclePda: PDA; beforeEach(async () => { - const init = - await initTestPoolWithTokens(ctx, tickSpacing, PriceMath.tickIndexToSqrtPriceX64(439296 + 1), new BN("10000000000000000000000")); + const init = await initTestPoolWithTokens( + ctx, + tickSpacing, + PriceMath.tickIndexToSqrtPriceX64(439296 + 1), + new BN("10000000000000000000000"), + ); poolInitInfo = init.poolInitInfo; whirlpoolPda = poolInitInfo.whirlpoolPda; @@ -2086,7 +2090,7 @@ describe("swap", () => { tickSpacing, aToB, ); - + // a: 1 (round up) // b: 223379095563402706 (to get 1, need >= 223379095563402706) const fundParams: FundedPositionParams[] = [ @@ -2096,7 +2100,7 @@ describe("swap", () => { tickUpperIndex: 439552, }, ]; - + await fundPositions( ctx, poolInitInfo, @@ -2108,16 +2112,10 @@ describe("swap", () => { async function getTokenBalances(): Promise<[BN, BN]> { const tokenVaultA = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountA, - ), + await getTokenBalance(provider, tokenAccountA), ); const tokenVaultB = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountB, - ), + await getTokenBalance(provider, tokenAccountB), ); return [tokenVaultA, tokenVaultB]; } @@ -2126,7 +2124,7 @@ describe("swap", () => { it("ExactIn, sqrt_price_limit = 0", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("223379095563402706"); const quote = await swapQuoteByInputToken( whirlpool, @@ -2139,7 +2137,7 @@ describe("swap", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapIx(ctx.program, { @@ -2172,7 +2170,7 @@ describe("swap", () => { it("ExactIn, sqrt_price_limit = MAX_SQRT_PRICE", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("223379095563402706"); const quote = await swapQuoteByInputToken( whirlpool, @@ -2185,7 +2183,7 @@ describe("swap", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapIx(ctx.program, { @@ -2218,7 +2216,7 @@ describe("swap", () => { it("Fails ExactOut, sqrt_price_limit = 0", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("1"); const quote = await swapQuoteByOutputToken( whirlpool, @@ -2257,7 +2255,7 @@ describe("swap", () => { it("ExactOut, sqrt_price_limit = MAX_SQRT_PRICE", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("1"); const quote = await swapQuoteByOutputToken( whirlpool, @@ -2270,7 +2268,7 @@ describe("swap", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapIx(ctx.program, { @@ -2312,8 +2310,12 @@ describe("swap", () => { let oraclePda: PDA; beforeEach(async () => { - const init = - await initTestPoolWithTokens(ctx, tickSpacing, PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), new BN("10000000000000000000000")); + const init = await initTestPoolWithTokens( + ctx, + tickSpacing, + PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), + new BN("10000000000000000000000"), + ); poolInitInfo = init.poolInitInfo; whirlpoolPda = poolInitInfo.whirlpoolPda; @@ -2330,7 +2332,7 @@ describe("swap", () => { tickSpacing, aToB, ); - + // a: 223379098170764880 (to get 1, need >= 223379098170764880) // b: 1 (round up) const fundParams: FundedPositionParams[] = [ @@ -2340,28 +2342,22 @@ describe("swap", () => { tickUpperIndex: -439424, }, ]; - + await fundPositions( ctx, poolInitInfo, tokenAccountA, tokenAccountB, fundParams, - ); + ); }); async function getTokenBalances(): Promise<[BN, BN]> { const tokenVaultA = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountA, - ), + await getTokenBalance(provider, tokenAccountA), ); const tokenVaultB = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountB, - ), + await getTokenBalance(provider, tokenAccountB), ); return [tokenVaultA, tokenVaultB]; } @@ -2370,7 +2366,7 @@ describe("swap", () => { it("ExactIn, sqrt_price_limit = 0", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("223379098170764880"); const quote = await swapQuoteByInputToken( whirlpool, @@ -2383,7 +2379,7 @@ describe("swap", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapIx(ctx.program, { @@ -2416,7 +2412,7 @@ describe("swap", () => { it("ExactIn, sqrt_price_limit = MIN_SQRT_PRICE", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("223379098170764880"); const quote = await swapQuoteByInputToken( whirlpool, @@ -2429,7 +2425,7 @@ describe("swap", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapIx(ctx.program, { @@ -2462,7 +2458,7 @@ describe("swap", () => { it("Fails ExactOut, sqrt_price_limit = 0", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("1"); const quote = await swapQuoteByOutputToken( whirlpool, @@ -2473,7 +2469,7 @@ describe("swap", () => { fetcher, IGNORE_CACHE, ); - + await assert.rejects( toTx( ctx, @@ -2501,7 +2497,7 @@ describe("swap", () => { it("ExactOut, sqrt_price_limit = MAX_SQRT_PRICE", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("1"); const quote = await swapQuoteByOutputToken( whirlpool, @@ -2514,7 +2510,7 @@ describe("swap", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapIx(ctx.program, { @@ -2543,6 +2539,5 @@ describe("swap", () => { assert.ok(diffB.isZero()); // no output (round up is not used to calculate output) assert.ok(postWhirlpoolData.sqrtPrice.eq(MIN_SQRT_PRICE_BN)); // hit min }); - }); }); diff --git a/legacy-sdk/whirlpool/tests/integration/two_hop_swap.test.ts b/legacy-sdk/whirlpool/tests/integration/two_hop_swap.test.ts index 8fc246258..07da85307 100644 --- a/legacy-sdk/whirlpool/tests/integration/two_hop_swap.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/two_hop_swap.test.ts @@ -834,38 +834,68 @@ describe("two-hop swap", () => { // Partial fill on second swap in ExactOut is allowed // |--***T**-S-| --> |--***T,limit**-S-| (where *: liquidity, S: start, T: end) it("ExactOut, partial fill on second swap", async () => { - const aquarium = (await buildTestAquariums(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: aqConfig.initFeeTierParams, - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ + const aquarium = ( + await buildTestAquariums(ctx, [ { - poolIndex: 0, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: aqConfig.initFeeTierParams, + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + ], }, - { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -886,7 +916,7 @@ describe("two-hop swap", () => { ctx.program.programId, whirlpoolTwoKey, ctx.fetcher, - IGNORE_CACHE + IGNORE_CACHE, ), tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, whirlpoolData: whirlpoolOne.getData(), @@ -894,20 +924,34 @@ describe("two-hop swap", () => { }; // 906251 --> 1000000 (end tick: 1004) - const quoteSecondWithoutLimit = swapQuoteWithParams({ - ...quoteParams, - sqrtPriceLimit: MIN_SQRT_PRICE_BN, - }, Percentage.fromFraction(0, 100)); + const quoteSecondWithoutLimit = swapQuoteWithParams( + { + ...quoteParams, + sqrtPriceLimit: MIN_SQRT_PRICE_BN, + }, + Percentage.fromFraction(0, 100), + ); assert.ok(quoteSecondWithoutLimit.estimatedEndTickIndex < 1008); // 762627 --> 841645 (end tick: 1008) - const quoteSecondWithLimit = swapQuoteWithParams({ - ...quoteParams, - sqrtPriceLimit: PriceMath.tickIndexToSqrtPriceX64(1008), - }, Percentage.fromFraction(0, 100)); + const quoteSecondWithLimit = swapQuoteWithParams( + { + ...quoteParams, + sqrtPriceLimit: PriceMath.tickIndexToSqrtPriceX64(1008), + }, + Percentage.fromFraction(0, 100), + ); assert.ok(quoteSecondWithLimit.estimatedEndTickIndex == 1008); - assert.ok(quoteSecondWithLimit.estimatedAmountOut.lt(quoteSecondWithoutLimit.estimatedAmountOut)); - assert.ok(quoteSecondWithLimit.estimatedAmountIn.lt(quoteSecondWithoutLimit.estimatedAmountIn)); + assert.ok( + quoteSecondWithLimit.estimatedAmountOut.lt( + quoteSecondWithoutLimit.estimatedAmountOut, + ), + ); + assert.ok( + quoteSecondWithLimit.estimatedAmountIn.lt( + quoteSecondWithoutLimit.estimatedAmountIn, + ), + ); // 821218 --> 906251 const quoteFirstWithoutLimit = await swapQuoteByOutputToken( @@ -932,7 +976,10 @@ describe("two-hop swap", () => { ); // build without limit - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirstWithoutLimit, quoteSecondWithoutLimit); + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirstWithoutLimit, + quoteSecondWithoutLimit, + ); await assert.rejects( toTx( @@ -948,7 +995,7 @@ describe("two-hop swap", () => { tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x1795/, // AmountInAboveMaximum. + /0x1795/, // AmountInAboveMaximum. ); assert.ok(quoteSecondWithoutLimit.estimatedEndTickIndex > 999); @@ -969,38 +1016,68 @@ describe("two-hop swap", () => { // Reject partial fill result // |--***T**-S-| --> |-min,T----**-S-| (where *: liquidity, S: start, T: end) it("fails ExactOut, partial fill on second swap, sqrt_price_limit_two == 0", async () => { - const aquarium = (await buildTestAquariums(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: aqConfig.initFeeTierParams, - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1) }, - ], - initTickArrayRangeParams: [ + const aquarium = ( + await buildTestAquariums(ctx, [ { - poolIndex: 0, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: aqConfig.initFeeTierParams, + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: -450560, + arrayCount: 1, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: -512, + tickUpperIndex: -128, + liquidityAmount: new BN(5_000_000_000_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: -439296 - 256, + tickUpperIndex: -439296 - 128, + liquidityAmount: new BN(1_000), + }, + ], + }, + ], }, - { - poolIndex: 1, - startTickIndex: -450560, - arrayCount: 1, - aToB: true, - }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: -512, tickUpperIndex: -128, liquidityAmount: new BN(5_000_000_000_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: -439296 - 256, tickUpperIndex: -439296 - 128, liquidityAmount: new BN(1_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -1009,7 +1086,7 @@ describe("two-hop swap", () => { let whirlpoolTwo = await client.getPool(whirlpoolTwoKey, IGNORE_CACHE); const [_inputToken, intermediaryToken, outputToken] = mintKeys; - + const quoteSecond = await swapQuoteByOutputToken( whirlpoolTwo, outputToken, @@ -1019,7 +1096,7 @@ describe("two-hop swap", () => { fetcher, IGNORE_CACHE, ); - + const quoteFirst = await swapQuoteByOutputToken( whirlpoolOne, intermediaryToken, @@ -1029,9 +1106,12 @@ describe("two-hop swap", () => { fetcher, IGNORE_CACHE, ); - - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirst, quoteSecond); - + + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirst, + quoteSecond, + ); + await assert.rejects( toTx( ctx, @@ -1043,7 +1123,7 @@ describe("two-hop swap", () => { tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x17a9/, // PartialFillError + /0x17a9/, // PartialFillError ); await toTx( @@ -1061,38 +1141,68 @@ describe("two-hop swap", () => { // Reject partial fill on the first swap by sqrt_price_limit_one = 0 // |-min,T----**-S-| --> |--***T**-S-| (where *: liquidity, S: start, T: end) it("fails ExactOut, partial fill on first swap, sqrt_price_limit_one == 0", async () => { - const aquarium = (await buildTestAquariums(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: [{tickSpacing: 128, feeRate: 0}], // to realize input = 1 on second swap - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ + const aquarium = ( + await buildTestAquariums(ctx, [ { - poolIndex: 0, - startTickIndex: -450560, - arrayCount: 1, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: [{ tickSpacing: 128, feeRate: 0 }], // to realize input = 1 on second swap + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: -450560, + arrayCount: 1, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: -439296 - 256, + tickUpperIndex: -439296 - 128, + liquidityAmount: new BN(1_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(5_000_000_000_000), + }, + ], + }, + ], }, - { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: -439296 - 256, tickUpperIndex: -439296 - 128, liquidityAmount: new BN(1_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(5_000_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -1101,7 +1211,7 @@ describe("two-hop swap", () => { let whirlpoolTwo = await client.getPool(whirlpoolTwoKey, IGNORE_CACHE); const [_inputToken, intermediaryToken, outputToken] = mintKeys; - + // 1 --> 1 const quoteSecond = await swapQuoteByOutputToken( whirlpoolTwo, @@ -1112,7 +1222,7 @@ describe("two-hop swap", () => { fetcher, IGNORE_CACHE, ); - + // 22337909818 --> 0 (not round up) const quoteFirst = await swapQuoteByOutputToken( whirlpoolOne, @@ -1123,9 +1233,12 @@ describe("two-hop swap", () => { fetcher, IGNORE_CACHE, ); - - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirst, quoteSecond); - + + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirst, + quoteSecond, + ); + await assert.rejects( toTx( ctx, @@ -1137,7 +1250,7 @@ describe("two-hop swap", () => { tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x17a9/, // PartialFillError + /0x17a9/, // PartialFillError ); }); @@ -1145,38 +1258,68 @@ describe("two-hop swap", () => { // Pools are safe, but owner consume intermediate tokens unproportionally // |-min,T----**-S-| --> |--***T**-S-| (where *: liquidity, S: start, T: end) it("fails ExactOut, partial fill on first swap, sqrt_price_limit_one != 0", async () => { - const aquarium = (await buildTestAquariums(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: [{tickSpacing: 128, feeRate: 0}], // to realize input = 1 on second swap - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ + const aquarium = ( + await buildTestAquariums(ctx, [ { - poolIndex: 0, - startTickIndex: -450560, - arrayCount: 1, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: [{ tickSpacing: 128, feeRate: 0 }], // to realize input = 1 on second swap + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: -450560, + arrayCount: 1, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: -439296 - 256, + tickUpperIndex: -439296 - 128, + liquidityAmount: new BN(1_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(5_000_000_000_000), + }, + ], + }, + ], }, - { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: -439296 - 256, tickUpperIndex: -439296 - 128, liquidityAmount: new BN(1_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(5_000_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -1185,7 +1328,7 @@ describe("two-hop swap", () => { let whirlpoolTwo = await client.getPool(whirlpoolTwoKey, IGNORE_CACHE); const [_inputToken, intermediaryToken, outputToken] = mintKeys; - + // 1 --> 1 const quoteSecond = await swapQuoteByOutputToken( whirlpoolTwo, @@ -1196,7 +1339,7 @@ describe("two-hop swap", () => { fetcher, IGNORE_CACHE, ); - + // 22337909818 --> 0 (not round up) const quoteFirst = await swapQuoteByOutputToken( whirlpoolOne, @@ -1207,9 +1350,12 @@ describe("two-hop swap", () => { fetcher, IGNORE_CACHE, ); - - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirst, quoteSecond); - + + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirst, + quoteSecond, + ); + await assert.rejects( toTx( ctx, @@ -1221,45 +1367,75 @@ describe("two-hop swap", () => { tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x17a3/, // IntermediateTokenAmountMismatch + /0x17a3/, // IntermediateTokenAmountMismatch ); }); // Partial fill on the first swap in ExactIn is allowed. // |--***T,limit**-S-| -> |--***T**-S--| (where *: liquidity, S: start, T: end) it("ExactIn, partial fill on first swap", async () => { - const aquarium = (await buildTestAquariums(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: aqConfig.initFeeTierParams, - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ - { - poolIndex: 0, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, + const aquarium = ( + await buildTestAquariums(ctx, [ { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: aqConfig.initFeeTierParams, + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + ], }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -1280,7 +1456,7 @@ describe("two-hop swap", () => { ctx.program.programId, whirlpoolOneKey, ctx.fetcher, - IGNORE_CACHE + IGNORE_CACHE, ), tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, whirlpoolData: whirlpoolOne.getData(), @@ -1288,20 +1464,34 @@ describe("two-hop swap", () => { }; // 1000000 --> 1103339 - const quoteFirstWithoutLimit = swapQuoteWithParams({ - ...quoteParams, - sqrtPriceLimit: MIN_SQRT_PRICE_BN, - }, Percentage.fromFraction(0, 100)); + const quoteFirstWithoutLimit = swapQuoteWithParams( + { + ...quoteParams, + sqrtPriceLimit: MIN_SQRT_PRICE_BN, + }, + Percentage.fromFraction(0, 100), + ); assert.ok(quoteFirstWithoutLimit.estimatedEndTickIndex < 1010); // 667266 --> 736476 - const quoteFirstWithLimit = swapQuoteWithParams({ - ...quoteParams, - sqrtPriceLimit: PriceMath.tickIndexToSqrtPriceX64(1010), - }, Percentage.fromFraction(0, 100)); + const quoteFirstWithLimit = swapQuoteWithParams( + { + ...quoteParams, + sqrtPriceLimit: PriceMath.tickIndexToSqrtPriceX64(1010), + }, + Percentage.fromFraction(0, 100), + ); assert.ok(quoteFirstWithLimit.estimatedEndTickIndex == 1010); - assert.ok(quoteFirstWithLimit.estimatedAmountIn.lt(quoteFirstWithoutLimit.estimatedAmountIn)); - assert.ok(quoteFirstWithLimit.estimatedAmountOut.lt(quoteFirstWithoutLimit.estimatedAmountOut)); + assert.ok( + quoteFirstWithLimit.estimatedAmountIn.lt( + quoteFirstWithoutLimit.estimatedAmountIn, + ), + ); + assert.ok( + quoteFirstWithLimit.estimatedAmountOut.lt( + quoteFirstWithoutLimit.estimatedAmountOut, + ), + ); // 1103339 --> 1217224 const quoteSecondWithoutLimit = await swapQuoteByInputToken( @@ -1326,7 +1516,10 @@ describe("two-hop swap", () => { ); // build without limit - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirstWithoutLimit, quoteSecondWithoutLimit); + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirstWithoutLimit, + quoteSecondWithoutLimit, + ); await assert.rejects( toTx( @@ -1337,12 +1530,13 @@ describe("two-hop swap", () => { sqrtPriceLimitOne: PriceMath.tickIndexToSqrtPriceX64(1010), // partial fill is allowed sqrtPriceLimitTwo: new BN(0), // partial fill on second swap is NOT allowd // +1 to check output amount - otherAmountThreshold: quoteSecondWithLimit.estimatedAmountOut.addn(1), + otherAmountThreshold: + quoteSecondWithLimit.estimatedAmountOut.addn(1), ...getParamsFromPools([pools[0], pools[1]], tokenAccounts), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x1794/, // AmountOutBelowMinimum + /0x1794/, // AmountOutBelowMinimum ); assert.ok(quoteSecondWithoutLimit.estimatedEndTickIndex > 999); @@ -1356,7 +1550,7 @@ describe("two-hop swap", () => { otherAmountThreshold: quoteSecondWithLimit.estimatedAmountOut, ...getParamsFromPools([pools[0], pools[1]], tokenAccounts), tokenAuthority: ctx.wallet.publicKey, - }), + }), ).buildAndExecute(); }); @@ -1364,38 +1558,68 @@ describe("two-hop swap", () => { // Pools and owner are safe, but owner will receive unconsumed intermediate tokens // |--***T**-S-| -> |--***T,limit**-S--| (where *: liquidity, S: start, T: end) it("fails ExactIn, partial fill on second swap", async () => { - const aquarium = (await buildTestAquariums(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: aqConfig.initFeeTierParams, - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ - { - poolIndex: 0, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, + const aquarium = ( + await buildTestAquariums(ctx, [ { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: aqConfig.initFeeTierParams, + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + ], }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -1404,7 +1628,7 @@ describe("two-hop swap", () => { let whirlpoolTwo = await client.getPool(whirlpoolTwoKey, IGNORE_CACHE); const [inputToken, intermediaryToken, _outputToken] = mintKeys; - + // 1000000 --> 1103339 const quoteFirst = await swapQuoteByInputToken( whirlpoolOne, @@ -1426,8 +1650,11 @@ describe("two-hop swap", () => { fetcher, IGNORE_CACHE, ); - - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirst, quoteSecond); + + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirst, + quoteSecond, + ); assert.ok(quoteSecond.estimatedEndTickIndex < 1002); await assert.rejects( @@ -1441,7 +1668,7 @@ describe("two-hop swap", () => { tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x17a3/, // IntermediateTokenAmountMismatch + /0x17a3/, // IntermediateTokenAmountMismatch ); assert.ok(quoteSecond.estimatedEndTickIndex > 999); @@ -1454,7 +1681,6 @@ describe("two-hop swap", () => { tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(); - }); }); diff --git a/legacy-sdk/whirlpool/tests/integration/v2/collect_fees_v2.test.ts b/legacy-sdk/whirlpool/tests/integration/v2/collect_fees_v2.test.ts index 49580258b..eac946457 100644 --- a/legacy-sdk/whirlpool/tests/integration/v2/collect_fees_v2.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/v2/collect_fees_v2.test.ts @@ -1187,7 +1187,7 @@ describe("collect_fees_v2", () => { tokenVaultB: tokenVaultBKeypair.publicKey, }), ).buildAndExecute(), - /0x7dc/ // ConstraintAddress + /0x7dc/, // ConstraintAddress ); }); @@ -1241,7 +1241,7 @@ describe("collect_fees_v2", () => { tokenVaultB: tokenVaultBKeypair.publicKey, }), ).buildAndExecute(), - /0x7dc/ // ConstraintAddress + /0x7dc/, // ConstraintAddress ); }); @@ -1349,7 +1349,7 @@ describe("collect_fees_v2", () => { tokenVaultB: tokenVaultBKeypair.publicKey, }), ).buildAndExecute(), - /0x7dc/ // ConstraintAddress + /0x7dc/, // ConstraintAddress ); }); @@ -1403,7 +1403,7 @@ describe("collect_fees_v2", () => { tokenVaultB: tokenVaultBKeypair.publicKey, }), ).buildAndExecute(), - /0x7dc/ // ConstraintAddress + /0x7dc/, // ConstraintAddress ); }); diff --git a/legacy-sdk/whirlpool/tests/integration/v2/swap_v2.test.ts b/legacy-sdk/whirlpool/tests/integration/v2/swap_v2.test.ts index cdb2017f8..8a6f3d1e9 100644 --- a/legacy-sdk/whirlpool/tests/integration/v2/swap_v2.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/v2/swap_v2.test.ts @@ -5,7 +5,12 @@ import type { PDA } from "@orca-so/common-sdk"; import * as assert from "assert"; import { BN } from "bn.js"; import Decimal from "decimal.js"; -import type { InitPoolV2Params, SwapV2Params, TickArrayData, WhirlpoolData } from "../../../src"; +import type { + InitPoolV2Params, + SwapV2Params, + TickArrayData, + WhirlpoolData, +} from "../../../src"; import { MAX_SQRT_PRICE, MAX_SQRT_PRICE_BN, @@ -2371,14 +2376,13 @@ describe("swap_v2", () => { let oraclePda: PDA; beforeEach(async () => { - const init = - await initTestPoolWithTokensV2( - ctx, - {isToken2022: true}, - {isToken2022: true}, - tickSpacing, - PriceMath.tickIndexToSqrtPriceX64(439296 + 1), - new BN("10000000000000000000000") + const init = await initTestPoolWithTokensV2( + ctx, + { isToken2022: true }, + { isToken2022: true }, + tickSpacing, + PriceMath.tickIndexToSqrtPriceX64(439296 + 1), + new BN("10000000000000000000000"), ); poolInitInfo = init.poolInitInfo; @@ -2396,7 +2400,7 @@ describe("swap_v2", () => { tickSpacing, aToB, ); - + // a: 1 (round up) // b: 223379095563402706 (to get 1, need >= 223379095563402706) const fundParams: FundedPositionV2Params[] = [ @@ -2406,7 +2410,7 @@ describe("swap_v2", () => { tickUpperIndex: 439552, }, ]; - + await fundPositionsV2( ctx, poolInitInfo, @@ -2418,16 +2422,10 @@ describe("swap_v2", () => { async function getTokenBalances(): Promise<[anchor.BN, anchor.BN]> { const tokenVaultA = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountA, - ), + await getTokenBalance(provider, tokenAccountA), ); const tokenVaultB = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountB, - ), + await getTokenBalance(provider, tokenAccountB), ); return [tokenVaultA, tokenVaultB]; } @@ -2436,7 +2434,7 @@ describe("swap_v2", () => { it("ExactIn, sqrt_price_limit = 0", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("223379095563402706"); const quote = await swapQuoteByInputToken( whirlpool, @@ -2449,7 +2447,7 @@ describe("swap_v2", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapV2Ix(ctx.program, { @@ -2486,7 +2484,7 @@ describe("swap_v2", () => { it("ExactIn, sqrt_price_limit = MAX_SQRT_PRICE", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("223379095563402706"); const quote = await swapQuoteByInputToken( whirlpool, @@ -2499,7 +2497,7 @@ describe("swap_v2", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapV2Ix(ctx.program, { @@ -2536,7 +2534,7 @@ describe("swap_v2", () => { it("Fails ExactOut, sqrt_price_limit = 0", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("1"); const quote = await swapQuoteByOutputToken( whirlpool, @@ -2564,7 +2562,7 @@ describe("swap_v2", () => { tokenMintB: whirlpoolData.tokenMintB, tokenProgramA: TEST_TOKEN_2022_PROGRAM_ID, tokenProgramB: TEST_TOKEN_2022_PROGRAM_ID, - + // sqrt_price_limit = 0 sqrtPriceLimit: ZERO_BN, amount, // 1 @@ -2579,7 +2577,7 @@ describe("swap_v2", () => { it("ExactOut, sqrt_price_limit = MAX_SQRT_PRICE", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("1"); const quote = await swapQuoteByOutputToken( whirlpool, @@ -2592,7 +2590,7 @@ describe("swap_v2", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapV2Ix(ctx.program, { @@ -2640,15 +2638,14 @@ describe("swap_v2", () => { let oraclePda: PDA; beforeEach(async () => { - const init = - await initTestPoolWithTokensV2( - ctx, - {isToken2022: true}, - {isToken2022: true}, - tickSpacing, - PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), - new BN("10000000000000000000000") - ); + const init = await initTestPoolWithTokensV2( + ctx, + { isToken2022: true }, + { isToken2022: true }, + tickSpacing, + PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), + new BN("10000000000000000000000"), + ); poolInitInfo = init.poolInitInfo; whirlpoolPda = poolInitInfo.whirlpoolPda; @@ -2665,7 +2662,7 @@ describe("swap_v2", () => { tickSpacing, aToB, ); - + // a: 223379098170764880 (to get 1, need >= 223379098170764880) // b: 1 (round up) const fundParams: FundedPositionV2Params[] = [ @@ -2675,28 +2672,22 @@ describe("swap_v2", () => { tickUpperIndex: -439424, }, ]; - + await fundPositionsV2( ctx, poolInitInfo, tokenAccountA, tokenAccountB, fundParams, - ); + ); }); async function getTokenBalances(): Promise<[anchor.BN, anchor.BN]> { const tokenVaultA = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountA, - ), + await getTokenBalance(provider, tokenAccountA), ); const tokenVaultB = new anchor.BN( - await getTokenBalance( - provider, - tokenAccountB, - ), + await getTokenBalance(provider, tokenAccountB), ); return [tokenVaultA, tokenVaultB]; } @@ -2705,7 +2696,7 @@ describe("swap_v2", () => { it("ExactIn, sqrt_price_limit = 0", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("223379098170764880"); const quote = await swapQuoteByInputToken( whirlpool, @@ -2718,7 +2709,7 @@ describe("swap_v2", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapV2Ix(ctx.program, { @@ -2755,7 +2746,7 @@ describe("swap_v2", () => { it("ExactIn, sqrt_price_limit = MIN_SQRT_PRICE", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("223379098170764880"); const quote = await swapQuoteByInputToken( whirlpool, @@ -2768,7 +2759,7 @@ describe("swap_v2", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapV2Ix(ctx.program, { @@ -2805,7 +2796,7 @@ describe("swap_v2", () => { it("Fails ExactOut, sqrt_price_limit = 0", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("1"); const quote = await swapQuoteByOutputToken( whirlpool, @@ -2833,7 +2824,7 @@ describe("swap_v2", () => { tokenMintB: whirlpoolData.tokenMintB, tokenProgramA: TEST_TOKEN_2022_PROGRAM_ID, tokenProgramB: TEST_TOKEN_2022_PROGRAM_ID, - + // sqrt_price_limit = 0 sqrtPriceLimit: ZERO_BN, amount, // 1 @@ -2848,7 +2839,7 @@ describe("swap_v2", () => { it("ExactOut, sqrt_price_limit = MAX_SQRT_PRICE", async () => { const whirlpool = await client.getPool(whirlpoolKey, IGNORE_CACHE); const whirlpoolData = whirlpool.getData(); - + const amount = new BN("1"); const quote = await swapQuoteByOutputToken( whirlpool, @@ -2861,7 +2852,7 @@ describe("swap_v2", () => { ); const [preA, preB] = await getTokenBalances(); - + await toTx( ctx, WhirlpoolIx.swapV2Ix(ctx.program, { @@ -2894,7 +2885,6 @@ describe("swap_v2", () => { assert.ok(diffB.isZero()); // no output (round up is not used to calculate output) assert.ok(postWhirlpoolData.sqrtPrice.eq(MIN_SQRT_PRICE_BN)); // hit min }); - }); }); diff --git a/legacy-sdk/whirlpool/tests/integration/v2/token-extensions/interest-bearing.test.ts b/legacy-sdk/whirlpool/tests/integration/v2/token-extensions/interest-bearing.test.ts index f844f2685..4bebaa76d 100644 --- a/legacy-sdk/whirlpool/tests/integration/v2/token-extensions/interest-bearing.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/v2/token-extensions/interest-bearing.test.ts @@ -3,9 +3,7 @@ import { BN } from "@coral-xyz/anchor"; import type NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; import { Percentage } from "@orca-so/common-sdk"; import * as assert from "assert"; -import type { - WhirlpoolData, -} from "../../../../src"; +import type { WhirlpoolData } from "../../../../src"; import { PDAUtil, swapQuoteWithParams, @@ -15,7 +13,12 @@ import { WhirlpoolIx, } from "../../../../src"; import { IGNORE_CACHE } from "../../../../src/network/public/fetcher"; -import { getTokenBalance, sleep, TEST_TOKEN_2022_PROGRAM_ID, TickSpacing } from "../../../utils"; +import { + getTokenBalance, + sleep, + TEST_TOKEN_2022_PROGRAM_ID, + TickSpacing, +} from "../../../utils"; import { defaultConfirmOptions } from "../../../utils/const"; import { fundPositionsV2, @@ -24,7 +27,10 @@ import { import type { PublicKey } from "@solana/web3.js"; import { initTickArrayRange } from "../../../utils/init-utils"; import { TokenExtensionUtil } from "../../../../src/utils/public/token-extension-util"; -import { amountToUiAmount, updateRateInterestBearingMint } from "@solana/spl-token"; +import { + amountToUiAmount, + updateRateInterestBearingMint, +} from "@solana/spl-token"; describe("TokenExtension/InterestBearing", () => { const provider = anchor.AnchorProvider.local( @@ -37,8 +43,17 @@ describe("TokenExtension/InterestBearing", () => { const payer = (ctx.wallet as NodeWallet).payer; - async function rawAmountToUIAmount(mint: PublicKey, rawAmount: BN): Promise { - const result = await amountToUiAmount(ctx.connection, payer, mint, rawAmount.toNumber(), TEST_TOKEN_2022_PROGRAM_ID); + async function rawAmountToUIAmount( + mint: PublicKey, + rawAmount: BN, + ): Promise { + const result = await amountToUiAmount( + ctx.connection, + payer, + mint, + rawAmount.toNumber(), + TEST_TOKEN_2022_PROGRAM_ID, + ); if (typeof result === "string") { return result; } @@ -50,26 +65,44 @@ describe("TokenExtension/InterestBearing", () => { // |----------|-----*S*T*|****------| (*: liquidity, S: start, T: end) it("swap_v2 (covers both owner to vault and vault to owner transfer)", async () => { - const { - whirlpoolPda, - poolInitInfo, - tokenAccountA, - tokenAccountB, - } = await initTestPoolWithTokensV2( - ctx, - { isToken2022: true, hasInterestBearingExtension: true, interestBearingRate: 0 }, // 0% - { isToken2022: true, hasInterestBearingExtension: true, interestBearingRate: 0 }, // 0% - TickSpacing.Standard, + const { whirlpoolPda, poolInitInfo, tokenAccountA, tokenAccountB } = + await initTestPoolWithTokensV2( + ctx, + { + isToken2022: true, + hasInterestBearingExtension: true, + interestBearingRate: 0, + }, // 0% + { + isToken2022: true, + hasInterestBearingExtension: true, + interestBearingRate: 0, + }, // 0% + TickSpacing.Standard, + ); + + const initialRawBalanceA = new BN( + await getTokenBalance(provider, tokenAccountA), + ); + const initialRawBalanceB = new BN( + await getTokenBalance(provider, tokenAccountB), + ); + const initialUIBalanceA = await rawAmountToUIAmount( + poolInitInfo.tokenMintA, + initialRawBalanceA, + ); + const initialUIBalanceB = await rawAmountToUIAmount( + poolInitInfo.tokenMintB, + initialRawBalanceB, ); - - const initialRawBalanceA = new BN(await getTokenBalance(provider, tokenAccountA)); - const initialRawBalanceB = new BN(await getTokenBalance(provider, tokenAccountB)); - const initialUIBalanceA = await rawAmountToUIAmount(poolInitInfo.tokenMintA, initialRawBalanceA); - const initialUIBalanceB = await rawAmountToUIAmount(poolInitInfo.tokenMintB, initialRawBalanceB); // rate is 0%, so these values should be equal - assert.ok(initialRawBalanceA.eq(new BN(Number.parseInt(initialUIBalanceA)))); - assert.ok(initialRawBalanceB.eq(new BN(Number.parseInt(initialUIBalanceB)))); + assert.ok( + initialRawBalanceA.eq(new BN(Number.parseInt(initialUIBalanceA))), + ); + assert.ok( + initialRawBalanceB.eq(new BN(Number.parseInt(initialUIBalanceB))), + ); // set rate > 0% const sigA = await updateRateInterestBearingMint( @@ -92,9 +125,15 @@ describe("TokenExtension/InterestBearing", () => { ]); await sleep(10 * 1000); - - const newUIBalanceA = await rawAmountToUIAmount(poolInitInfo.tokenMintA, initialRawBalanceA); - const newUIBalanceB = await rawAmountToUIAmount(poolInitInfo.tokenMintB, initialRawBalanceB); + + const newUIBalanceA = await rawAmountToUIAmount( + poolInitInfo.tokenMintA, + initialRawBalanceA, + ); + const newUIBalanceB = await rawAmountToUIAmount( + poolInitInfo.tokenMintB, + initialRawBalanceB, + ); // rate is >0%, so these values should NOT be equal assert.ok(initialRawBalanceA.lt(new BN(Number.parseInt(newUIBalanceA)))); @@ -112,19 +151,13 @@ describe("TokenExtension/InterestBearing", () => { aToB, ); - await fundPositionsV2( - ctx, - poolInitInfo, - tokenAccountA, - tokenAccountB, - [ - { - liquidityAmount: new anchor.BN(10_000_000), - tickLowerIndex: 29440, - tickUpperIndex: 33536, - }, - ] - ); + await fundPositionsV2(ctx, poolInitInfo, tokenAccountA, tokenAccountB, [ + { + liquidityAmount: new anchor.BN(10_000_000), + tickLowerIndex: 29440, + tickUpperIndex: 33536, + }, + ]); const oraclePubkey = PDAUtil.getOracle( ctx.program.programId, @@ -155,12 +188,11 @@ describe("TokenExtension/InterestBearing", () => { fetcher, IGNORE_CACHE, ), - tokenExtensionCtx: - await TokenExtensionUtil.buildTokenExtensionContext( - fetcher, - whirlpoolData, - IGNORE_CACHE, - ), + tokenExtensionCtx: await TokenExtensionUtil.buildTokenExtensionContext( + fetcher, + whirlpoolData, + IGNORE_CACHE, + ), }, Percentage.fromFraction(0, 100), ); @@ -195,5 +227,4 @@ describe("TokenExtension/InterestBearing", () => { assert.ok(diffA.eq(quoteBToA.estimatedAmountOut)); assert.ok(diffB.eq(quoteBToA.estimatedAmountIn.neg())); }); - }); diff --git a/legacy-sdk/whirlpool/tests/integration/v2/token-extensions/transfer-hook.test.ts b/legacy-sdk/whirlpool/tests/integration/v2/token-extensions/transfer-hook.test.ts index 73bade2bf..241b34657 100644 --- a/legacy-sdk/whirlpool/tests/integration/v2/token-extensions/transfer-hook.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/v2/token-extensions/transfer-hook.test.ts @@ -323,8 +323,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); const feeBalanceA = await getTokenBalance(provider, feeAccountA); const feeBalanceB = await getTokenBalance(provider, feeAccountB); assert.ok(new BN(feeBalanceA).gtn(0)); @@ -387,8 +387,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: undefined, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); const feeBalanceA = await getTokenBalance(provider, feeAccountA); const feeBalanceB = await getTokenBalance(provider, feeAccountB); assert.ok(new BN(feeBalanceA).gtn(0)); @@ -441,8 +441,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -481,8 +481,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: undefined, // TransferHook (not provided) }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -525,8 +525,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), // Errors on tlv-account-resolution // https://github.com/solana-labs/solana-program-library/blob/dbf609206a60ed5698644f4840ddbd117d2c83d8/libraries/tlv-account-resolution/src/error.rs#L6 /0xa261c2c0/, // IncorrectAccount (2724315840) @@ -576,8 +576,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), // Errors on tlv-account-resolution // https://github.com/solana-labs/solana-program-library/blob/dbf609206a60ed5698644f4840ddbd117d2c83d8/libraries/tlv-account-resolution/src/error.rs#L6 /0xa261c2c0/, // IncorrectAccount (2724315840) @@ -627,8 +627,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), // Errors on transfer-hook-interface // https://github.com/solana-labs/solana-program-library/blob/dbf609206a60ed5698644f4840ddbd117d2c83d8/token/transfer-hook/interface/src/error.rs#L6 /0x7dc8348c/, // IncorrectAccount (2110272652) @@ -673,8 +673,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), // Errors on transfer-hook-interface // https://github.com/solana-labs/solana-program-library/blob/dbf609206a60ed5698644f4840ddbd117d2c83d8/token/transfer-hook/interface/src/error.rs#L6 /0x7dc8348c/, // IncorrectAccount (2110272652) @@ -737,8 +737,8 @@ describe("TokenExtension/TransferHook", () => { cleanupInstructions: [], signers: [], }) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a0/, // RemainingAccountsInvalidSlice ); }); @@ -799,8 +799,8 @@ describe("TokenExtension/TransferHook", () => { cleanupInstructions: [], signers: [], }) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a1/, // RemainingAccountsInsufficient ); }); @@ -861,8 +861,8 @@ describe("TokenExtension/TransferHook", () => { cleanupInstructions: [], signers: [], }) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a5/, // RemainingAccountsDuplicatedAccountsType ); }); @@ -923,8 +923,8 @@ describe("TokenExtension/TransferHook", () => { cleanupInstructions: [], signers: [], }) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a5/, // RemainingAccountsDuplicatedAccountsType ); }); @@ -1144,8 +1144,8 @@ describe("TokenExtension/TransferHook", () => { tickArrayUpper: positions[0].tickArrayUpper, }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); // Generate collect reward expectation const whirlpoolData = (await fetcher.getPool( @@ -1194,7 +1194,7 @@ describe("TokenExtension/TransferHook", () => { reward.rewardMint, reward.rewardVaultKeypair.publicKey, rewardAccounts[i], - whirlpoolPda.publicKey, + whirlpoolPda.publicKey, ); }), ); @@ -1228,8 +1228,8 @@ describe("TokenExtension/TransferHook", () => { rewardTransferHookAccounts: tokenTransferHookAccounts[i], // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); const rewardBalance = await getTokenBalance( provider, rewardAccounts[i], @@ -1270,8 +1270,8 @@ describe("TokenExtension/TransferHook", () => { rewardTransferHookAccounts: undefined, // TransferHook (not provided) }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); } @@ -1323,8 +1323,8 @@ describe("TokenExtension/TransferHook", () => { cleanupInstructions: [], signers: [], }) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a5/, // RemainingAccountsDuplicatedAccountsType ); } @@ -1428,8 +1428,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); const postVaultBalanceA = await getTokenBalance( provider, @@ -1520,8 +1520,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: undefined, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); const postVaultBalanceA = await getTokenBalance( provider, @@ -1584,8 +1584,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -1628,8 +1628,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: undefined, // TransferHook (not provided) }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -1676,8 +1676,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), // Errors on tlv-account-resolution // https://github.com/solana-labs/solana-program-library/blob/dbf609206a60ed5698644f4840ddbd117d2c83d8/libraries/tlv-account-resolution/src/error.rs#L6 /0xa261c2c0/, // IncorrectAccount (2724315840) @@ -1731,8 +1731,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), // Errors on tlv-account-resolution // https://github.com/solana-labs/solana-program-library/blob/dbf609206a60ed5698644f4840ddbd117d2c83d8/libraries/tlv-account-resolution/src/error.rs#L6 /0xa261c2c0/, // IncorrectAccount (2724315840) @@ -1786,8 +1786,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), // Errors on transfer-hook-interface // https://github.com/solana-labs/solana-program-library/blob/dbf609206a60ed5698644f4840ddbd117d2c83d8/token/transfer-hook/interface/src/error.rs#L6 /0x7dc8348c/, // IncorrectAccount (2110272652) @@ -1836,8 +1836,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), // Errors on transfer-hook-interface // https://github.com/solana-labs/solana-program-library/blob/dbf609206a60ed5698644f4840ddbd117d2c83d8/token/transfer-hook/interface/src/error.rs#L6 /0x7dc8348c/, // IncorrectAccount (2110272652) @@ -1961,8 +1961,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); const destBalanceA = await getTokenBalance(provider, destAccountA); const destBalanceB = await getTokenBalance(provider, destAccountB); assert.ok(new BN(destBalanceA).gtn(0)); @@ -2006,8 +2006,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -2038,8 +2038,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: undefined, // TransferHook (not provided) }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -2229,8 +2229,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: tokenTransferHookAccountsBForAToB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); const postCounterA = await getTestTransferHookCounter( provider, @@ -2273,8 +2273,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: tokenTransferHookAccountsBForBToA, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(); + .prependInstruction(useMaxCU()) + .buildAndExecute(); const postCounterA = await getTestTransferHookCounter( provider, @@ -2309,8 +2309,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: tokenTransferHookAccountsBForAToB, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -2336,8 +2336,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: undefined, // TransferHook (not provided) }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -2363,8 +2363,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: tokenTransferHookAccountsBForBToA, // TransferHook }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -2390,8 +2390,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB: undefined, // TransferHook (not provided) }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a2/, // NoExtraAccountsForTransferHook ); }); @@ -2814,8 +2814,8 @@ describe("TokenExtension/TransferHook", () => { cleanupInstructions: [], signers: [], }) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a5/, // RemainingAccountsDuplicatedAccountsType ); }); @@ -2866,8 +2866,8 @@ describe("TokenExtension/TransferHook", () => { cleanupInstructions: [], signers: [], }) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a5/, // RemainingAccountsDuplicatedAccountsType ); }); @@ -2918,8 +2918,8 @@ describe("TokenExtension/TransferHook", () => { cleanupInstructions: [], signers: [], }) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), /0x17a5/, // RemainingAccountsDuplicatedAccountsType ); }); @@ -3037,8 +3037,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), (err) => { // error code is 0x1770 from transfer hook program and it is ambiguous, so use message string return JSON.stringify(err).includes("AmountTooBig"); @@ -3158,8 +3158,8 @@ describe("TokenExtension/TransferHook", () => { tokenTransferHookAccountsB, }), ) - .prependInstruction(useMaxCU()) - .buildAndExecute(), + .prependInstruction(useMaxCU()) + .buildAndExecute(), (err) => { // error code is 0x1770 from transfer hook program and it is ambiguous, so use message string return JSON.stringify(err).includes("AmountTooBig"); diff --git a/legacy-sdk/whirlpool/tests/integration/v2/two_hop_swap_v2.test.ts b/legacy-sdk/whirlpool/tests/integration/v2/two_hop_swap_v2.test.ts index 16d1dd16a..4683e474b 100644 --- a/legacy-sdk/whirlpool/tests/integration/v2/two_hop_swap_v2.test.ts +++ b/legacy-sdk/whirlpool/tests/integration/v2/two_hop_swap_v2.test.ts @@ -45,7 +45,10 @@ import { asyncAssertOwnerProgram, createMintV2, } from "../../utils/v2/token-2022"; -import { NO_TOKEN_EXTENSION_CONTEXT, TokenExtensionUtil } from "../../../src/utils/public/token-extension-util"; +import { + NO_TOKEN_EXTENSION_CONTEXT, + TokenExtensionUtil, +} from "../../../src/utils/public/token-extension-util"; describe("two_hop_swap_v2", () => { const provider = anchor.AnchorProvider.local( @@ -2280,9 +2283,9 @@ describe("two_hop_swap_v2", () => { const aqConfig = { ...getDefaultAquariumV2(), initMintParams: [ - {tokenTrait: {isToken2022: true}}, - {tokenTrait: {isToken2022: true}}, - {tokenTrait: {isToken2022: true}}, + { tokenTrait: { isToken2022: true } }, + { tokenTrait: { isToken2022: true } }, + { tokenTrait: { isToken2022: true } }, ], initTokenAccParams: [ { mintIndex: 0 }, @@ -2290,46 +2293,76 @@ describe("two_hop_swap_v2", () => { { mintIndex: 2 }, ], initPoolParams: [ - { mintIndices: [0, 1] as [number, number], tickSpacing: 128, }, - { mintIndices: [1, 2] as [number, number], tickSpacing: 128, }, + { mintIndices: [0, 1] as [number, number], tickSpacing: 128 }, + { mintIndices: [1, 2] as [number, number], tickSpacing: 128 }, ], }; // Partial fill on second swap in ExactOut is allowed // |--***T**-S-| --> |--***T,limit**-S-| (where *: liquidity, S: start, T: end) it("ExactOut, partial fill on second swap", async () => { - const aquarium = (await buildTestAquariumsV2(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: aqConfig.initFeeTierParams, - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ + const aquarium = ( + await buildTestAquariumsV2(ctx, [ { - poolIndex: 0, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, - { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: aqConfig.initFeeTierParams, + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + ], }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -2350,7 +2383,7 @@ describe("two_hop_swap_v2", () => { ctx.program.programId, whirlpoolTwoKey, ctx.fetcher, - IGNORE_CACHE + IGNORE_CACHE, ), tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, whirlpoolData: whirlpoolOne.getData(), @@ -2358,20 +2391,34 @@ describe("two_hop_swap_v2", () => { }; // 906251 --> 1000000 (end tick: 1004) - const quoteSecondWithoutLimit = swapQuoteWithParams({ - ...quoteParams, - sqrtPriceLimit: MIN_SQRT_PRICE_BN, - }, Percentage.fromFraction(0, 100)); + const quoteSecondWithoutLimit = swapQuoteWithParams( + { + ...quoteParams, + sqrtPriceLimit: MIN_SQRT_PRICE_BN, + }, + Percentage.fromFraction(0, 100), + ); assert.ok(quoteSecondWithoutLimit.estimatedEndTickIndex < 1008); // 762627 --> 841645 (end tick: 1008) - const quoteSecondWithLimit = swapQuoteWithParams({ - ...quoteParams, - sqrtPriceLimit: PriceMath.tickIndexToSqrtPriceX64(1008), - }, Percentage.fromFraction(0, 100)); + const quoteSecondWithLimit = swapQuoteWithParams( + { + ...quoteParams, + sqrtPriceLimit: PriceMath.tickIndexToSqrtPriceX64(1008), + }, + Percentage.fromFraction(0, 100), + ); assert.ok(quoteSecondWithLimit.estimatedEndTickIndex == 1008); - assert.ok(quoteSecondWithLimit.estimatedAmountOut.lt(quoteSecondWithoutLimit.estimatedAmountOut)); - assert.ok(quoteSecondWithLimit.estimatedAmountIn.lt(quoteSecondWithoutLimit.estimatedAmountIn)); + assert.ok( + quoteSecondWithLimit.estimatedAmountOut.lt( + quoteSecondWithoutLimit.estimatedAmountOut, + ), + ); + assert.ok( + quoteSecondWithLimit.estimatedAmountIn.lt( + quoteSecondWithoutLimit.estimatedAmountIn, + ), + ); // 821218 --> 906251 const quoteFirstWithoutLimit = await swapQuoteByOutputToken( @@ -2396,7 +2443,10 @@ describe("two_hop_swap_v2", () => { ); // build without limit - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirstWithoutLimit, quoteSecondWithoutLimit); + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirstWithoutLimit, + quoteSecondWithoutLimit, + ); await assert.rejects( toTx( @@ -2408,11 +2458,15 @@ describe("two_hop_swap_v2", () => { sqrtPriceLimitTwo: PriceMath.tickIndexToSqrtPriceX64(1008), // partial fill is allowed // -1 to check input amount otherAmountThreshold: quoteFirstWithLimit.estimatedAmountIn.subn(1), - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x1795/, // AmountInAboveMaximum. + /0x1795/, // AmountInAboveMaximum. ); assert.ok(quoteSecondWithoutLimit.estimatedEndTickIndex > 999); @@ -2424,7 +2478,11 @@ describe("two_hop_swap_v2", () => { sqrtPriceLimitOne: new BN(0), // partial fill on second swap is NOT allowd sqrtPriceLimitTwo: PriceMath.tickIndexToSqrtPriceX64(1008), // partial fill is allowed otherAmountThreshold: quoteFirstWithLimit.estimatedAmountIn, - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(); @@ -2433,38 +2491,68 @@ describe("two_hop_swap_v2", () => { // Reject partial fill result // |--***T**-S-| --> |-min,T----**-S-| (where *: liquidity, S: start, T: end) it("fails ExactOut, partial fill on second swap, sqrt_price_limit_two == 0", async () => { - const aquarium = (await buildTestAquariumsV2(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: aqConfig.initFeeTierParams, - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1) }, - ], - initTickArrayRangeParams: [ + const aquarium = ( + await buildTestAquariumsV2(ctx, [ { - poolIndex: 0, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, - { - poolIndex: 1, - startTickIndex: -450560, - arrayCount: 1, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: aqConfig.initFeeTierParams, + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: -450560, + arrayCount: 1, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: -512, + tickUpperIndex: -128, + liquidityAmount: new BN(5_000_000_000_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: -439296 - 256, + tickUpperIndex: -439296 - 128, + liquidityAmount: new BN(1_000), + }, + ], + }, + ], }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: -512, tickUpperIndex: -128, liquidityAmount: new BN(5_000_000_000_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: -439296 - 256, tickUpperIndex: -439296 - 128, liquidityAmount: new BN(1_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -2473,7 +2561,7 @@ describe("two_hop_swap_v2", () => { let whirlpoolTwo = await client.getPool(whirlpoolTwoKey, IGNORE_CACHE); const [_inputToken, intermediaryToken, outputToken] = mintKeys; - + const quoteSecond = await swapQuoteByOutputToken( whirlpoolTwo, outputToken, @@ -2483,7 +2571,7 @@ describe("two_hop_swap_v2", () => { fetcher, IGNORE_CACHE, ); - + const quoteFirst = await swapQuoteByOutputToken( whirlpoolOne, intermediaryToken, @@ -2493,9 +2581,12 @@ describe("two_hop_swap_v2", () => { fetcher, IGNORE_CACHE, ); - - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirst, quoteSecond); - + + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirst, + quoteSecond, + ); + await assert.rejects( toTx( ctx, @@ -2503,11 +2594,15 @@ describe("two_hop_swap_v2", () => { ...twoHopQuote, sqrtPriceLimitOne: MIN_SQRT_PRICE_BN, // Partial fill is allowed sqrtPriceLimitTwo: new BN(0), // Partial fill is NOT allowed - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x17a9/, // PartialFillError + /0x17a9/, // PartialFillError ); await toTx( @@ -2516,7 +2611,11 @@ describe("two_hop_swap_v2", () => { ...twoHopQuote, sqrtPriceLimitOne: MIN_SQRT_PRICE_BN, // Partial fill is allowed sqrtPriceLimitTwo: MIN_SQRT_PRICE_BN, // Partial fill is allowed - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(); @@ -2525,38 +2624,68 @@ describe("two_hop_swap_v2", () => { // Reject partial fill on the first swap by sqrt_price_limit_one = 0 // |-min,T----**-S-| --> |--***T**-S-| (where *: liquidity, S: start, T: end) it("fails ExactOut, partial fill on first swap, sqrt_price_limit_one == 0", async () => { - const aquarium = (await buildTestAquariumsV2(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: [{tickSpacing: 128, feeRate: 0}], // to realize input = 1 on second swap - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ - { - poolIndex: 0, - startTickIndex: -450560, - arrayCount: 1, - aToB: true, - }, + const aquarium = ( + await buildTestAquariumsV2(ctx, [ { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: [{ tickSpacing: 128, feeRate: 0 }], // to realize input = 1 on second swap + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: -450560, + arrayCount: 1, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: -439296 - 256, + tickUpperIndex: -439296 - 128, + liquidityAmount: new BN(1_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(5_000_000_000_000), + }, + ], + }, + ], }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: -439296 - 256, tickUpperIndex: -439296 - 128, liquidityAmount: new BN(1_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(5_000_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -2565,7 +2694,7 @@ describe("two_hop_swap_v2", () => { let whirlpoolTwo = await client.getPool(whirlpoolTwoKey, IGNORE_CACHE); const [_inputToken, intermediaryToken, outputToken] = mintKeys; - + // 1 --> 1 const quoteSecond = await swapQuoteByOutputToken( whirlpoolTwo, @@ -2576,7 +2705,7 @@ describe("two_hop_swap_v2", () => { fetcher, IGNORE_CACHE, ); - + // 22337909818 --> 0 (not round up) const quoteFirst = await swapQuoteByOutputToken( whirlpoolOne, @@ -2587,9 +2716,12 @@ describe("two_hop_swap_v2", () => { fetcher, IGNORE_CACHE, ); - - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirst, quoteSecond); - + + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirst, + quoteSecond, + ); + await assert.rejects( toTx( ctx, @@ -2597,11 +2729,15 @@ describe("two_hop_swap_v2", () => { ...twoHopQuote, sqrtPriceLimitOne: new BN(0), // Partial fill is NOT allowed sqrtPriceLimitTwo: MIN_SQRT_PRICE_BN, // Partial fill is allowed - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x17a9/, // PartialFillError + /0x17a9/, // PartialFillError ); }); @@ -2609,38 +2745,68 @@ describe("two_hop_swap_v2", () => { // This case must be rejected due to vault to vault transfer // |-min,T----**-S-| --> |--***T**-S-| (where *: liquidity, S: start, T: end) it("fails ExactOut, partial fill on first swap, sqrt_price_limit_one != 0", async () => { - const aquarium = (await buildTestAquariumsV2(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: [{tickSpacing: 128, feeRate: 0}], // to realize input = 1 on second swap - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ + const aquarium = ( + await buildTestAquariumsV2(ctx, [ { - poolIndex: 0, - startTickIndex: -450560, - arrayCount: 1, - aToB: true, - }, - { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: [{ tickSpacing: 128, feeRate: 0 }], // to realize input = 1 on second swap + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(-439296 - 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: -450560, + arrayCount: 1, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: -439296 - 256, + tickUpperIndex: -439296 - 128, + liquidityAmount: new BN(1_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(5_000_000_000_000), + }, + ], + }, + ], }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: -439296 - 256, tickUpperIndex: -439296 - 128, liquidityAmount: new BN(1_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(5_000_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -2649,7 +2815,7 @@ describe("two_hop_swap_v2", () => { let whirlpoolTwo = await client.getPool(whirlpoolTwoKey, IGNORE_CACHE); const [_inputToken, intermediaryToken, outputToken] = mintKeys; - + // 1 --> 1 const quoteSecond = await swapQuoteByOutputToken( whirlpoolTwo, @@ -2660,7 +2826,7 @@ describe("two_hop_swap_v2", () => { fetcher, IGNORE_CACHE, ); - + // 22337909818 --> 0 (not round up) const quoteFirst = await swapQuoteByOutputToken( whirlpoolOne, @@ -2671,9 +2837,12 @@ describe("two_hop_swap_v2", () => { fetcher, IGNORE_CACHE, ); - - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirst, quoteSecond); - + + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirst, + quoteSecond, + ); + await assert.rejects( toTx( ctx, @@ -2681,49 +2850,83 @@ describe("two_hop_swap_v2", () => { ...twoHopQuote, sqrtPriceLimitOne: MIN_SQRT_PRICE_BN, // Partial fill is allowed sqrtPriceLimitTwo: MIN_SQRT_PRICE_BN, // Partial fill is allowed - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x17a3/, // IntermediateTokenAmountMismatch + /0x17a3/, // IntermediateTokenAmountMismatch ); }); // Partial fill on the first swap in ExactIn is allowed. // |--***T,limit**-S-| -> |--***T**-S--| (where *: liquidity, S: start, T: end) it("ExactIn, partial fill on first swap", async () => { - const aquarium = (await buildTestAquariumsV2(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: aqConfig.initFeeTierParams, - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ - { - poolIndex: 0, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, + const aquarium = ( + await buildTestAquariumsV2(ctx, [ { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: aqConfig.initFeeTierParams, + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + ], }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -2744,7 +2947,7 @@ describe("two_hop_swap_v2", () => { ctx.program.programId, whirlpoolOneKey, ctx.fetcher, - IGNORE_CACHE + IGNORE_CACHE, ), tokenExtensionCtx: NO_TOKEN_EXTENSION_CONTEXT, whirlpoolData: whirlpoolOne.getData(), @@ -2752,20 +2955,34 @@ describe("two_hop_swap_v2", () => { }; // 1000000 --> 1103339 - const quoteFirstWithoutLimit = swapQuoteWithParams({ - ...quoteParams, - sqrtPriceLimit: MIN_SQRT_PRICE_BN, - }, Percentage.fromFraction(0, 100)); + const quoteFirstWithoutLimit = swapQuoteWithParams( + { + ...quoteParams, + sqrtPriceLimit: MIN_SQRT_PRICE_BN, + }, + Percentage.fromFraction(0, 100), + ); assert.ok(quoteFirstWithoutLimit.estimatedEndTickIndex < 1010); // 667266 --> 736476 - const quoteFirstWithLimit = swapQuoteWithParams({ - ...quoteParams, - sqrtPriceLimit: PriceMath.tickIndexToSqrtPriceX64(1010), - }, Percentage.fromFraction(0, 100)); + const quoteFirstWithLimit = swapQuoteWithParams( + { + ...quoteParams, + sqrtPriceLimit: PriceMath.tickIndexToSqrtPriceX64(1010), + }, + Percentage.fromFraction(0, 100), + ); assert.ok(quoteFirstWithLimit.estimatedEndTickIndex == 1010); - assert.ok(quoteFirstWithLimit.estimatedAmountIn.lt(quoteFirstWithoutLimit.estimatedAmountIn)); - assert.ok(quoteFirstWithLimit.estimatedAmountOut.lt(quoteFirstWithoutLimit.estimatedAmountOut)); + assert.ok( + quoteFirstWithLimit.estimatedAmountIn.lt( + quoteFirstWithoutLimit.estimatedAmountIn, + ), + ); + assert.ok( + quoteFirstWithLimit.estimatedAmountOut.lt( + quoteFirstWithoutLimit.estimatedAmountOut, + ), + ); // 1103339 --> 1217224 const quoteSecondWithoutLimit = await swapQuoteByInputToken( @@ -2790,7 +3007,10 @@ describe("two_hop_swap_v2", () => { ); // build without limit - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirstWithoutLimit, quoteSecondWithoutLimit); + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirstWithoutLimit, + quoteSecondWithoutLimit, + ); await assert.rejects( toTx( @@ -2801,12 +3021,17 @@ describe("two_hop_swap_v2", () => { sqrtPriceLimitOne: PriceMath.tickIndexToSqrtPriceX64(1010), // partial fill is allowed sqrtPriceLimitTwo: new BN(0), // partial fill on second swap is NOT allowd // +1 to check output amount - otherAmountThreshold: quoteSecondWithLimit.estimatedAmountOut.addn(1), - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + otherAmountThreshold: + quoteSecondWithLimit.estimatedAmountOut.addn(1), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x1794/, // AmountOutBelowMinimum + /0x1794/, // AmountOutBelowMinimum ); assert.ok(quoteSecondWithoutLimit.estimatedEndTickIndex > 999); @@ -2818,9 +3043,13 @@ describe("two_hop_swap_v2", () => { sqrtPriceLimitOne: PriceMath.tickIndexToSqrtPriceX64(1010), // partial fill is allowed sqrtPriceLimitTwo: new BN(0), // partial fill on second swap is NOT allowd otherAmountThreshold: quoteSecondWithLimit.estimatedAmountOut, - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, - }), + }), ).buildAndExecute(); }); @@ -2828,38 +3057,68 @@ describe("two_hop_swap_v2", () => { // Pools and owner are safe, but owner will receive unconsumed intermediate tokens // |--***T**-S-| -> |--***T,limit**-S--| (where *: liquidity, S: start, T: end) it("fails ExactIn, partial fill on second swap", async () => { - const aquarium = (await buildTestAquariumsV2(ctx, [{ - configParams: aqConfig.configParams, - initFeeTierParams: aqConfig.initFeeTierParams, - initMintParams: aqConfig.initMintParams, - initTokenAccParams: [ - {mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000)}, - {mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000)}, - ], - initPoolParams: [ - { ...aqConfig.initPoolParams[0], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - { ...aqConfig.initPoolParams[1], tickSpacing: 128, initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1) }, - ], - initTickArrayRangeParams: [ + const aquarium = ( + await buildTestAquariumsV2(ctx, [ { - poolIndex: 0, - startTickIndex: 0, - arrayCount: 3, - aToB: true, - }, - { - poolIndex: 1, - startTickIndex: 0, - arrayCount: 3, - aToB: true, + configParams: aqConfig.configParams, + initFeeTierParams: aqConfig.initFeeTierParams, + initMintParams: aqConfig.initMintParams, + initTokenAccParams: [ + { mintIndex: 0, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 1, mintAmount: new BN(1_000_000_000_000_000) }, + { mintIndex: 2, mintAmount: new BN(1_000_000_000_000_000) }, + ], + initPoolParams: [ + { + ...aqConfig.initPoolParams[0], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + { + ...aqConfig.initPoolParams[1], + tickSpacing: 128, + initSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(1024 + 1), + }, + ], + initTickArrayRangeParams: [ + { + poolIndex: 0, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + { + poolIndex: 1, + startTickIndex: 0, + arrayCount: 3, + aToB: true, + }, + ], + initPositionParams: [ + { + poolIndex: 0, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + { + poolIndex: 1, + fundParams: [ + { + tickLowerIndex: 512, + tickUpperIndex: 1024, + liquidityAmount: new BN(1_000_000_000), + }, + ], + }, + ], }, - ], - initPositionParams: [ - {poolIndex: 0, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - {poolIndex: 1, fundParams: [{tickLowerIndex: 512, tickUpperIndex: 1024, liquidityAmount: new BN(1_000_000_000)}]}, - ], - }]))[0]; + ]) + )[0]; const { tokenAccounts, mintKeys, pools } = aquarium; const whirlpoolOneKey = pools[0].whirlpoolPda.publicKey; @@ -2868,7 +3127,7 @@ describe("two_hop_swap_v2", () => { let whirlpoolTwo = await client.getPool(whirlpoolTwoKey, IGNORE_CACHE); const [inputToken, intermediaryToken, _outputToken] = mintKeys; - + // 1000000 --> 1103339 const quoteFirst = await swapQuoteByInputToken( whirlpoolOne, @@ -2890,8 +3149,11 @@ describe("two_hop_swap_v2", () => { fetcher, IGNORE_CACHE, ); - - const twoHopQuote = twoHopSwapQuoteFromSwapQuotes(quoteFirst, quoteSecond); + + const twoHopQuote = twoHopSwapQuoteFromSwapQuotes( + quoteFirst, + quoteSecond, + ); assert.ok(quoteSecond.estimatedEndTickIndex < 1002); await assert.rejects( @@ -2901,11 +3163,15 @@ describe("two_hop_swap_v2", () => { ...twoHopQuote, sqrtPriceLimitOne: MIN_SQRT_PRICE_BN, // Partial fill is allowed sqrtPriceLimitTwo: PriceMath.tickIndexToSqrtPriceX64(1002), // Partial fill - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(), - /0x17a3/, // IntermediateTokenAmountMismatch + /0x17a3/, // IntermediateTokenAmountMismatch ); assert.ok(quoteSecond.estimatedEndTickIndex > 999); @@ -2914,11 +3180,14 @@ describe("two_hop_swap_v2", () => { WhirlpoolIx.twoHopSwapV2Ix(ctx.program, { ...twoHopQuote, sqrtPriceLimitTwo: PriceMath.tickIndexToSqrtPriceX64(999), - ...getParamsFromPools([pools[0], pools[1]], [true, true], tokenAccounts), + ...getParamsFromPools( + [pools[0], pools[1]], + [true, true], + tokenAccounts, + ), tokenAuthority: ctx.wallet.publicKey, }), ).buildAndExecute(); - }); }); diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl-collectFees.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl-collectFees.test.ts index 43cc392f9..6a3816be5 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl-collectFees.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl-collectFees.test.ts @@ -1,7 +1,10 @@ import * as anchor from "@coral-xyz/anchor"; import { BN } from "@coral-xyz/anchor"; import { MathUtil } from "@orca-so/common-sdk"; -import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; +import { + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; import * as assert from "assert"; import Decimal from "decimal.js"; import type { Whirlpool, WhirlpoolClient } from "../../../src"; @@ -14,7 +17,12 @@ import { toTx, } from "../../../src"; import { IGNORE_CACHE } from "../../../src/network/public/fetcher"; -import { MAX_U64, TEST_TOKEN_2022_PROGRAM_ID, TickSpacing, ZERO_BN } from "../../utils"; +import { + MAX_U64, + TEST_TOKEN_2022_PROGRAM_ID, + TickSpacing, + ZERO_BN, +} from "../../utils"; import { defaultConfirmOptions } from "../../utils/const"; import { WhirlpoolTestFixture } from "../../utils/fixture"; import { TokenExtensionUtil } from "../../../src/utils/public/token-extension-util"; @@ -253,18 +261,30 @@ describe("PositionImpl#collectFees()", () => { ); // open TokenExtensions based position - const positionWithTokenExtensions = await pool.openPosition(tickLowerIndex, tickUpperIndex, { - liquidityAmount, - tokenMaxA: MAX_U64, - tokenMaxB: MAX_U64, - }, undefined, undefined, undefined, TOKEN_2022_PROGRAM_ID); + const positionWithTokenExtensions = await pool.openPosition( + tickLowerIndex, + tickUpperIndex, + { + liquidityAmount, + tokenMaxA: MAX_U64, + tokenMaxB: MAX_U64, + }, + undefined, + undefined, + undefined, + TOKEN_2022_PROGRAM_ID, + ); await positionWithTokenExtensions.tx.buildAndExecute(); - const positionAddress = PDAUtil.getPosition(testCtx.whirlpoolCtx.program.programId, positionWithTokenExtensions.positionMint).publicKey; + const positionAddress = PDAUtil.getPosition( + testCtx.whirlpoolCtx.program.programId, + positionWithTokenExtensions.positionMint, + ).publicKey; - const position = await testCtx.whirlpoolClient.getPosition( - positionAddress + const position = + await testCtx.whirlpoolClient.getPosition(positionAddress); + assert.ok( + position.getPositionMintTokenProgramId().equals(TOKEN_2022_PROGRAM_ID), ); - assert.ok(position.getPositionMintTokenProgramId().equals(TOKEN_2022_PROGRAM_ID)); await accrueFees(fixture); @@ -503,8 +523,8 @@ describe("PositionImpl#collectFees()", () => { )), }), ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); // Accrue fees in token B await toTx( @@ -541,8 +561,8 @@ describe("PositionImpl#collectFees()", () => { )), }), ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); const poolData = await pool.refreshData(); const positionData = await position.refreshData(); diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl-collectRewards.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl-collectRewards.test.ts index aff6598cd..19ad96e78 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl-collectRewards.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl-collectRewards.test.ts @@ -1,6 +1,9 @@ import * as anchor from "@coral-xyz/anchor"; import { MathUtil } from "@orca-so/common-sdk"; -import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; +import { + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; import * as assert from "assert"; import BN from "bn.js"; import Decimal from "decimal.js"; @@ -13,7 +16,12 @@ import { collectRewardsQuote, } from "../../../src"; import { IGNORE_CACHE } from "../../../src/network/public/fetcher"; -import { MAX_U64, TEST_TOKEN_2022_PROGRAM_ID, TickSpacing, sleep } from "../../utils"; +import { + MAX_U64, + TEST_TOKEN_2022_PROGRAM_ID, + TickSpacing, + sleep, +} from "../../utils"; import { defaultConfirmOptions } from "../../utils/const"; import { WhirlpoolTestFixture } from "../../utils/fixture"; import { TokenExtensionUtil } from "../../../src/utils/public/token-extension-util"; @@ -175,18 +183,30 @@ describe("PositionImpl#collectRewards()", () => { ); // open TokenExtensions based position - const positionWithTokenExtensions = await pool.openPosition(tickLowerIndex, tickUpperIndex, { - liquidityAmount, - tokenMaxA: MAX_U64, - tokenMaxB: MAX_U64, - }, undefined, undefined, undefined, TOKEN_2022_PROGRAM_ID); + const positionWithTokenExtensions = await pool.openPosition( + tickLowerIndex, + tickUpperIndex, + { + liquidityAmount, + tokenMaxA: MAX_U64, + tokenMaxB: MAX_U64, + }, + undefined, + undefined, + undefined, + TOKEN_2022_PROGRAM_ID, + ); await positionWithTokenExtensions.tx.buildAndExecute(); - const positionAddress = PDAUtil.getPosition(testCtx.whirlpoolCtx.program.programId, positionWithTokenExtensions.positionMint).publicKey; - - const position = await testCtx.whirlpoolClient.getPosition( - positionAddress + const positionAddress = PDAUtil.getPosition( + testCtx.whirlpoolCtx.program.programId, + positionWithTokenExtensions.positionMint, + ).publicKey; + + const position = + await testCtx.whirlpoolClient.getPosition(positionAddress); + assert.ok( + position.getPositionMintTokenProgramId().equals(TOKEN_2022_PROGRAM_ID), ); - assert.ok(position.getPositionMintTokenProgramId().equals(TOKEN_2022_PROGRAM_ID)); const otherWallet = anchor.web3.Keypair.generate(); const preCollectPoolData = await pool.refreshData(); diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl.test.ts index 278f14059..ed7011a91 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/position-impl.test.ts @@ -1,6 +1,9 @@ import * as anchor from "@coral-xyz/anchor"; import { Percentage } from "@orca-so/common-sdk"; -import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; +import { + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; import * as assert from "assert"; import Decimal from "decimal.js"; import { @@ -141,8 +144,8 @@ describe("position-impl", () => { ctx.wallet.publicKey, ) ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); const postIncreaseData = await position.refreshData(); const expectedPostIncreaseLiquidity = preIncreaseData.liquidity.add( @@ -170,8 +173,8 @@ describe("position-impl", () => { await ( await position.decreaseLiquidity(decrease_quote, false) ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); const postWithdrawData = await position.refreshData(); const expectedPostWithdrawLiquidity = postIncreaseData.liquidity.sub( @@ -258,8 +261,8 @@ describe("position-impl", () => { await ( await position.increaseLiquidity(increase_quote, false) ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); const postIncreaseData = await position.refreshData(); const expectedPostIncreaseLiquidity = preIncreaseData.liquidity.add( @@ -337,7 +340,7 @@ describe("position-impl", () => { ) ) .addSigner(otherWallet) - .prependInstruction(useMaxCU()) // TransferHook require much CU + .prependInstruction(useMaxCU()) // TransferHook require much CU .buildAndExecute(); const postSecondIncreaseData = await position.refreshData(); @@ -353,7 +356,7 @@ describe("position-impl", () => { ) ) .addSigner(otherWallet) - .prependInstruction(useMaxCU()) // TransferHook require much CU + .prependInstruction(useMaxCU()) // TransferHook require much CU .buildAndExecute(); const postWithdrawData = await position.refreshData(); @@ -425,7 +428,10 @@ describe("position-impl", () => { ); // Verify position mint is owned by Token-2022 - const positionMint = await fetcher.getMintInfo(position.getData().positionMint, IGNORE_CACHE); + const positionMint = await fetcher.getMintInfo( + position.getData().positionMint, + IGNORE_CACHE, + ); assert.ok(positionMint?.tokenProgram.equals(TOKEN_2022_PROGRAM_ID)); const preIncreaseData = position.getData(); @@ -451,8 +457,8 @@ describe("position-impl", () => { ctx.wallet.publicKey, ) ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); const postIncreaseData = await position.refreshData(); const expectedPostIncreaseLiquidity = preIncreaseData.liquidity.add( @@ -480,8 +486,8 @@ describe("position-impl", () => { await ( await position.decreaseLiquidity(decrease_quote, false) ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); const postWithdrawData = await position.refreshData(); const expectedPostWithdrawLiquidity = postIncreaseData.liquidity.sub( diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/swap/swap-edge-case.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/swap/swap-edge-case.test.ts index 0d1151f4c..dfebc0f5b 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/swap/swap-edge-case.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/swap/swap-edge-case.test.ts @@ -7,7 +7,7 @@ import { WhirlpoolContext, buildWhirlpoolClient, swapQuoteByInputToken, - swapQuoteByOutputToken + swapQuoteByOutputToken, } from "../../../../src"; import { IGNORE_CACHE } from "../../../../src/network/public/fetcher"; import { defaultConfirmOptions } from "../../../utils/const"; @@ -35,23 +35,24 @@ describe("swap edge case test", () => { const tickLowerIndex = -3904; const liquidityAmount = new BN(100000000000); - return new WhirlpoolTestFixture(ctx).init( - { - tokenAIsNative: true, // build pool which is similar to SOL/mSOL - initialSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(tickInitialIndex), - tickSpacing, - positions: [ - { tickLowerIndex, tickUpperIndex, liquidityAmount }, // In range position - ], - }, - ); + return new WhirlpoolTestFixture(ctx).init({ + tokenAIsNative: true, // build pool which is similar to SOL/mSOL + initialSqrtPrice: PriceMath.tickIndexToSqrtPriceX64(tickInitialIndex), + tickSpacing, + positions: [ + { tickLowerIndex, tickUpperIndex, liquidityAmount }, // In range position + ], + }); } it("ExactIn, SOL is input token", async () => { const fixture = await buildTestFixture(); const poolInitInfo = fixture.getInfos().poolInitInfo; - const pool = await client.getPool(poolInitInfo.whirlpoolPda.publicKey, IGNORE_CACHE); + const pool = await client.getPool( + poolInitInfo.whirlpoolPda.publicKey, + IGNORE_CACHE, + ); assert.ok(pool.getData().tokenMintA.equals(NATIVE_MINT)); const quote = await swapQuoteByInputToken( @@ -88,7 +89,10 @@ describe("swap edge case test", () => { const fixture = await buildTestFixture(); const poolInitInfo = fixture.getInfos().poolInitInfo; - const pool = await client.getPool(poolInitInfo.whirlpoolPda.publicKey, IGNORE_CACHE); + const pool = await client.getPool( + poolInitInfo.whirlpoolPda.publicKey, + IGNORE_CACHE, + ); assert.ok(pool.getData().tokenMintA.equals(NATIVE_MINT)); const quote = await swapQuoteByOutputToken( @@ -115,7 +119,8 @@ describe("swap edge case test", () => { const createAccountIx = tx.compressIx(true).instructions[0]; const decoded = SystemInstruction.decodeCreateAccount(createAccountIx); const tokenAccountRent = await fetcher.getAccountRentExempt(true); - const lamportsExpected = quote.otherAmountThreshold.addn(tokenAccountRent); + const lamportsExpected = + quote.otherAmountThreshold.addn(tokenAccountRent); assert.ok(lamportsExpected.eq(new BN(decoded.lamports))); await tx.buildAndExecute(); @@ -125,7 +130,10 @@ describe("swap edge case test", () => { const fixture = await buildTestFixture(); const poolInitInfo = fixture.getInfos().poolInitInfo; - const pool = await client.getPool(poolInitInfo.whirlpoolPda.publicKey, IGNORE_CACHE); + const pool = await client.getPool( + poolInitInfo.whirlpoolPda.publicKey, + IGNORE_CACHE, + ); assert.ok(pool.getData().tokenMintA.equals(NATIVE_MINT)); const quote = await swapQuoteByOutputToken( @@ -150,9 +158,11 @@ describe("swap edge case test", () => { pool.swap({ ...quote, // use default otherAmountThreshold (U64_MAX) - otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold(quote.amountSpecifiedIsInput), + otherAmountThreshold: SwapUtils.getDefaultOtherAmountThreshold( + quote.amountSpecifiedIsInput, + ), }), - /Wrapping U64_MAX amount of SOL is not possible/ + /Wrapping U64_MAX amount of SOL is not possible/, ); }); }); diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/utils/fetcher-util.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/utils/fetcher-util.test.ts index 6d6ef1fea..06e523046 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/utils/fetcher-util.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/utils/fetcher-util.test.ts @@ -14,7 +14,10 @@ import { Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js"; import type { PublicKey } from "@solana/web3.js"; import { PDAUtil } from "../../../../dist/utils/public/pda-utils"; import { WhirlpoolIx } from "../../../../dist/ix"; -import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { + getAssociatedTokenAddressSync, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; import { PositionBundleUtil } from "../../../../dist/utils/public/position-bundle-util"; import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; @@ -29,7 +32,11 @@ describe("fetcher util tests", () => { // create isolated wallet because the wallet for globalCtx has many positions created by other test cases. const isolatedOwnerKeypair = Keypair.generate(); const isolatedWallet = new NodeWallet(isolatedOwnerKeypair); - const ctx = WhirlpoolContext.from(globalCtx.connection, isolatedWallet, globalCtx.program.programId); + const ctx = WhirlpoolContext.from( + globalCtx.connection, + isolatedWallet, + globalCtx.program.programId, + ); const fetcher = ctx.fetcher; beforeAll(async () => { await systemTransferTx( @@ -44,9 +51,15 @@ describe("fetcher util tests", () => { const tickSpacing = TickSpacing.Standard; const liquidityAmount = new BN(10_000_000); - async function initializePositionBundleWithPositions(whirlpool: PublicKey, bundleIndexes: number[]): Promise { + async function initializePositionBundleWithPositions( + whirlpool: PublicKey, + bundleIndexes: number[], + ): Promise { const positionBundleMintKeypair = Keypair.generate(); - const positionBundlePda = PDAUtil.getPositionBundle(ctx.program.programId, positionBundleMintKeypair.publicKey); + const positionBundlePda = PDAUtil.getPositionBundle( + ctx.program.programId, + positionBundleMintKeypair.publicKey, + ); const positionBundleTokenAccount = getAssociatedTokenAddressSync( positionBundleMintKeypair.publicKey, ctx.wallet.publicKey, @@ -54,20 +67,26 @@ describe("fetcher util tests", () => { TOKEN_PROGRAM_ID, ); - await toTx(ctx, WhirlpoolIx.initializePositionBundleIx( - ctx.program, { + await toTx( + ctx, + WhirlpoolIx.initializePositionBundleIx(ctx.program, { funder: ctx.wallet.publicKey, owner: ctx.wallet.publicKey, positionBundleMintKeypair, positionBundlePda, positionBundleTokenAccount, - } - )).buildAndExecute(); + }), + ).buildAndExecute(); for (const bundleIndex of bundleIndexes) { - const bundledPositionPda = PDAUtil.getBundledPosition(ctx.program.programId, positionBundleMintKeypair.publicKey, bundleIndex); - await toTx(ctx, WhirlpoolIx.openBundledPositionIx( - ctx.program, { + const bundledPositionPda = PDAUtil.getBundledPosition( + ctx.program.programId, + positionBundleMintKeypair.publicKey, + bundleIndex, + ); + await toTx( + ctx, + WhirlpoolIx.openBundledPositionIx(ctx.program, { bundledPositionPda, bundleIndex, tickLowerIndex, @@ -77,51 +96,118 @@ describe("fetcher util tests", () => { positionBundleTokenAccount, funder: ctx.wallet.publicKey, whirlpool, - } - )).buildAndExecute(); + }), + ).buildAndExecute(); } - const positionBundleData = await fetcher.getPositionBundle(positionBundlePda.publicKey, IGNORE_CACHE); + const positionBundleData = await fetcher.getPositionBundle( + positionBundlePda.publicKey, + IGNORE_CACHE, + ); assert.ok(!!positionBundleData); - const occupied = PositionBundleUtil.getOccupiedBundleIndexes(positionBundleData); + const occupied = + PositionBundleUtil.getOccupiedBundleIndexes(positionBundleData); assert.deepEqual(occupied, bundleIndexes); return positionBundlePda.publicKey; } it("getAllPositionAccountsByOwner", async () => { - const fixture = await new WhirlpoolTestFixture(ctx).init( - { - tickSpacing, - positions: [ - // 5 TokenProgram based positions - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: false }, - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: false }, - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: false }, - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: false }, - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: false }, - // 5 TokenExtensions based positions - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: true }, - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: true }, - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: true }, - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: true }, - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: true }, - ], - }, - ); + const fixture = await new WhirlpoolTestFixture(ctx).init({ + tickSpacing, + positions: [ + // 5 TokenProgram based positions + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: false, + }, + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: false, + }, + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: false, + }, + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: false, + }, + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: false, + }, + // 5 TokenExtensions based positions + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: true, + }, + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: true, + }, + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: true, + }, + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: true, + }, + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: true, + }, + ], + }); - const positionAddresses = new Set(fixture.getInfos().positions.slice(0, 5).map(p => p.publicKey.toBase58())); + const positionAddresses = new Set( + fixture + .getInfos() + .positions.slice(0, 5) + .map((p) => p.publicKey.toBase58()), + ); assert.ok(positionAddresses.size === 5); - const positionWithTokenExtensionsAddresses = new Set(fixture.getInfos().positions.slice(5).map(p => p.publicKey.toBase58())); + const positionWithTokenExtensionsAddresses = new Set( + fixture + .getInfos() + .positions.slice(5) + .map((p) => p.publicKey.toBase58()), + ); assert.ok(positionWithTokenExtensionsAddresses.size === 5); - // initialize 2 position bundles const whirlpool = fixture.getInfos().poolInitInfo.whirlpoolPda.publicKey; const positionBundle1BundleIndexes = [0, 128, 250]; - const positionBundle1Pubkey = await initializePositionBundleWithPositions(whirlpool, positionBundle1BundleIndexes); + const positionBundle1Pubkey = await initializePositionBundleWithPositions( + whirlpool, + positionBundle1BundleIndexes, + ); const positionBundle2BundleIndexes = [5, 30, 64, 135, 192, 255]; - const positionBundle2Pubkey = await initializePositionBundleWithPositions(whirlpool, positionBundle2BundleIndexes); + const positionBundle2Pubkey = await initializePositionBundleWithPositions( + whirlpool, + positionBundle2BundleIndexes, + ); const result = await getAllPositionAccountsByOwner({ ctx, @@ -132,43 +218,124 @@ describe("fetcher util tests", () => { }); assert.ok(result.positions.size === 5); - assert.ok(Array.from(result.positions.keys()).every(p => positionAddresses.has(p))); - assert.ok(Array.from(result.positions.values()).every(p => p.tickLowerIndex === tickLowerIndex && p.tickUpperIndex === tickUpperIndex)); + assert.ok( + Array.from(result.positions.keys()).every((p) => + positionAddresses.has(p), + ), + ); + assert.ok( + Array.from(result.positions.values()).every( + (p) => + p.tickLowerIndex === tickLowerIndex && + p.tickUpperIndex === tickUpperIndex, + ), + ); assert.ok(result.positionsWithTokenExtensions.size === 5); - assert.ok(Array.from(result.positionsWithTokenExtensions.keys()).every(p => positionWithTokenExtensionsAddresses.has(p))); - assert.ok(Array.from(result.positionsWithTokenExtensions.values()).every(p => p.tickLowerIndex === tickLowerIndex && p.tickUpperIndex === tickUpperIndex)); + assert.ok( + Array.from(result.positionsWithTokenExtensions.keys()).every((p) => + positionWithTokenExtensionsAddresses.has(p), + ), + ); + assert.ok( + Array.from(result.positionsWithTokenExtensions.values()).every( + (p) => + p.tickLowerIndex === tickLowerIndex && + p.tickUpperIndex === tickUpperIndex, + ), + ); assert.ok(result.positionBundles.length === 2); const pb0 = result.positionBundles[0]; const pb1 = result.positionBundles[1]; - const occupied0 = PositionBundleUtil.getOccupiedBundleIndexes(pb0.positionBundleData); - const occupied1 = PositionBundleUtil.getOccupiedBundleIndexes(pb1.positionBundleData); + const occupied0 = PositionBundleUtil.getOccupiedBundleIndexes( + pb0.positionBundleData, + ); + const occupied1 = PositionBundleUtil.getOccupiedBundleIndexes( + pb1.positionBundleData, + ); - if (pb0.positionBundleAddress.toString() === positionBundle1Pubkey.toString()) { - assert.ok(pb0.positionBundleAddress.toString() === positionBundle1Pubkey.toString()); + if ( + pb0.positionBundleAddress.toString() === positionBundle1Pubkey.toString() + ) { + assert.ok( + pb0.positionBundleAddress.toString() === + positionBundle1Pubkey.toString(), + ); assert.deepEqual(occupied0, positionBundle1BundleIndexes); - assert.ok(pb0.bundledPositions.size === positionBundle1BundleIndexes.length); - assert.deepEqual(Array.from(pb0.bundledPositions.keys()), positionBundle1BundleIndexes); - assert.ok(Array.from(pb0.bundledPositions.values()).every(p => p.tickLowerIndex === tickLowerIndex && p.tickUpperIndex === tickUpperIndex)); + assert.ok( + pb0.bundledPositions.size === positionBundle1BundleIndexes.length, + ); + assert.deepEqual( + Array.from(pb0.bundledPositions.keys()), + positionBundle1BundleIndexes, + ); + assert.ok( + Array.from(pb0.bundledPositions.values()).every( + (p) => + p.tickLowerIndex === tickLowerIndex && + p.tickUpperIndex === tickUpperIndex, + ), + ); - assert.ok(pb1.positionBundleAddress.toString() === positionBundle2Pubkey.toString()); + assert.ok( + pb1.positionBundleAddress.toString() === + positionBundle2Pubkey.toString(), + ); assert.deepEqual(occupied1, positionBundle2BundleIndexes); - assert.ok(pb1.bundledPositions.size === positionBundle2BundleIndexes.length); - assert.deepEqual(Array.from(pb1.bundledPositions.keys()), positionBundle2BundleIndexes); - assert.ok(Array.from(pb1.bundledPositions.values()).every(p => p.tickLowerIndex === tickLowerIndex && p.tickUpperIndex === tickUpperIndex)); - + assert.ok( + pb1.bundledPositions.size === positionBundle2BundleIndexes.length, + ); + assert.deepEqual( + Array.from(pb1.bundledPositions.keys()), + positionBundle2BundleIndexes, + ); + assert.ok( + Array.from(pb1.bundledPositions.values()).every( + (p) => + p.tickLowerIndex === tickLowerIndex && + p.tickUpperIndex === tickUpperIndex, + ), + ); } else { - assert.ok(pb0.positionBundleAddress.toString() === positionBundle2Pubkey.toString()); + assert.ok( + pb0.positionBundleAddress.toString() === + positionBundle2Pubkey.toString(), + ); assert.deepEqual(occupied0, positionBundle2BundleIndexes); - assert.ok(pb0.bundledPositions.size === positionBundle2BundleIndexes.length); - assert.deepEqual(Array.from(pb0.bundledPositions.keys()), positionBundle2BundleIndexes); - assert.ok(Array.from(pb0.bundledPositions.values()).every(p => p.tickLowerIndex === tickLowerIndex && p.tickUpperIndex === tickUpperIndex)); + assert.ok( + pb0.bundledPositions.size === positionBundle2BundleIndexes.length, + ); + assert.deepEqual( + Array.from(pb0.bundledPositions.keys()), + positionBundle2BundleIndexes, + ); + assert.ok( + Array.from(pb0.bundledPositions.values()).every( + (p) => + p.tickLowerIndex === tickLowerIndex && + p.tickUpperIndex === tickUpperIndex, + ), + ); - assert.ok(pb1.positionBundleAddress.toString() === positionBundle1Pubkey.toString()); + assert.ok( + pb1.positionBundleAddress.toString() === + positionBundle1Pubkey.toString(), + ); assert.deepEqual(occupied1, positionBundle1BundleIndexes); - assert.ok(pb1.bundledPositions.size === positionBundle1BundleIndexes.length); - assert.deepEqual(Array.from(pb1.bundledPositions.keys()), positionBundle1BundleIndexes); - assert.ok(Array.from(pb1.bundledPositions.values()).every(p => p.tickLowerIndex === tickLowerIndex && p.tickUpperIndex === tickUpperIndex)); + assert.ok( + pb1.bundledPositions.size === positionBundle1BundleIndexes.length, + ); + assert.deepEqual( + Array.from(pb1.bundledPositions.keys()), + positionBundle1BundleIndexes, + ); + assert.ok( + Array.from(pb1.bundledPositions.values()).every( + (p) => + p.tickLowerIndex === tickLowerIndex && + p.tickUpperIndex === tickUpperIndex, + ), + ); } const resultDefault = await getAllPositionAccountsByOwner({ @@ -193,5 +360,4 @@ describe("fetcher util tests", () => { assert.ok(resultAllFalse.positionsWithTokenExtensions.size === 0); assert.ok(resultAllFalse.positionBundles.length === 0); }); - }); diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-client-impl.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-client-impl.test.ts index 0d5f29a66..a91b301a0 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-client-impl.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-client-impl.test.ts @@ -19,8 +19,16 @@ import { import { defaultConfirmOptions } from "../../utils/const"; import { buildTestPoolParams, initTestPool } from "../../utils/init-utils"; import { buildTestPoolV2Params } from "../../utils/v2/init-utils-v2"; -import { getMint, getTransferFeeConfig, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { initPosition, mintTokensToTestAccount } from "../../utils/test-builders"; +import { + getMint, + getTransferFeeConfig, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { + initPosition, + mintTokensToTestAccount, +} from "../../utils/test-builders"; describe("whirlpool-client-impl", () => { const provider = anchor.AnchorProvider.local( @@ -860,7 +868,7 @@ describe("whirlpool-client-impl", () => { poolInitInfo.tokenMintB, 10_000_000_000, ); - + const pool = await client.getPool(poolInitInfo.whirlpoolPda.publicKey); const lowerTick = PriceMath.priceToTickIndex( new Decimal(89), @@ -900,10 +908,20 @@ describe("whirlpool-client-impl", () => { ); // check .getPosition - const position0 = await client.getPosition(positions[0].positionAddress.publicKey, IGNORE_CACHE); - assert.ok(position0.getPositionMintTokenProgramId().equals(TOKEN_2022_PROGRAM_ID)); - const position1 = await client.getPosition(positions[1].positionAddress.publicKey, IGNORE_CACHE); - assert.ok(position1.getPositionMintTokenProgramId().equals(TOKEN_PROGRAM_ID)); + const position0 = await client.getPosition( + positions[0].positionAddress.publicKey, + IGNORE_CACHE, + ); + assert.ok( + position0.getPositionMintTokenProgramId().equals(TOKEN_2022_PROGRAM_ID), + ); + const position1 = await client.getPosition( + positions[1].positionAddress.publicKey, + IGNORE_CACHE, + ); + assert.ok( + position1.getPositionMintTokenProgramId().equals(TOKEN_PROGRAM_ID), + ); // check .getPositions const positionsFetched = await client.getPositions( @@ -911,11 +929,16 @@ describe("whirlpool-client-impl", () => { IGNORE_CACHE, ); withTokenExtensions.forEach((withTokenExtension, i) => { - const position = positionsFetched[positions[i].positionAddress.publicKey.toBase58()]; + const position = + positionsFetched[positions[i].positionAddress.publicKey.toBase58()]; assert.ok(!!position); - assert.ok(position.getPositionMintTokenProgramId().equals( - withTokenExtension ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID, - )); + assert.ok( + position + .getPositionMintTokenProgramId() + .equals( + withTokenExtension ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID, + ), + ); }); }); }); diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl-closePosition.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl-closePosition.test.ts index f71f0aa72..e9a84f466 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl-closePosition.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl-closePosition.test.ts @@ -93,8 +93,8 @@ describe("WhirlpoolImpl#closePosition()", () => { tickArray2: tickArrayPda.publicKey, }) ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); // Accrue fees in token B await ( @@ -109,8 +109,8 @@ describe("WhirlpoolImpl#closePosition()", () => { tickArray2: tickArrayPda.publicKey, }) ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); // accrue rewards await sleep(2000); @@ -146,8 +146,8 @@ describe("WhirlpoolImpl#closePosition()", () => { const tx = await position.decreaseLiquidity(liquidityCollectedQuote); - // TransferHook require much CU - tx.prependInstruction(useMaxCU()); + // TransferHook require much CU + tx.prependInstruction(useMaxCU()); await tx.buildAndExecute(); } @@ -162,9 +162,11 @@ describe("WhirlpoolImpl#closePosition()", () => { IGNORE_CACHE, ); const hasL = !position.getData().liquidity.isZero(); - await (await position.collectFees(hasL)) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + await ( + await position.collectFees(hasL) + ) + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); } async function testClosePosition( diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl-collectFeesAndRewardsForPositions.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl-collectFeesAndRewardsForPositions.test.ts index a77bef29c..5887c4314 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl-collectFeesAndRewardsForPositions.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl-collectFeesAndRewardsForPositions.test.ts @@ -340,7 +340,11 @@ describe("WhirlpoolImpl#collectFeesAndRewardsForPositions()", () => { await tx.buildAndExecute(); } - async function baseTestSenario(tokenAIsNative: boolean, ataExists: boolean, includeTokenExtensionBasedPosition: boolean) { + async function baseTestSenario( + tokenAIsNative: boolean, + ataExists: boolean, + includeTokenExtensionBasedPosition: boolean, + ) { const fixtures: WhirlpoolTestFixture[] = []; const positions: FundedPositionInfo[] = []; const numOfPool = 3; @@ -352,9 +356,26 @@ describe("WhirlpoolImpl#collectFeesAndRewardsForPositions()", () => { tickSpacing, positions: [ // 3 Positions / pool - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: includeTokenExtensionBasedPosition }, // In range position - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: false }, // In range position - { tickLowerIndex, tickUpperIndex, liquidityAmount, isTokenExtensionsBasedPosition: includeTokenExtensionBasedPosition }, // In range position + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: + includeTokenExtensionBasedPosition, + }, // In range position + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: false, + }, // In range position + { + tickLowerIndex, + tickUpperIndex, + liquidityAmount, + isTokenExtensionsBasedPosition: + includeTokenExtensionBasedPosition, + }, // In range position ], rewards: [ { @@ -378,12 +399,35 @@ describe("WhirlpoolImpl#collectFeesAndRewardsForPositions()", () => { } const positionMints = positions.map((p) => p.mintKeypair.publicKey); - const positionMintInfos = await testCtx.whirlpoolCtx.fetcher.getMintInfos(positionMints, IGNORE_CACHE); + const positionMintInfos = await testCtx.whirlpoolCtx.fetcher.getMintInfos( + positionMints, + IGNORE_CACHE, + ); for (let i = 0; i < numOfPool; i++) { const base = i * 3; - assert.ok(positionMintInfos.get(positionMints[base+0].toBase58())?.tokenProgram.equals(includeTokenExtensionBasedPosition ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID)); - assert.ok(positionMintInfos.get(positionMints[base+1].toBase58())?.tokenProgram.equals(TOKEN_PROGRAM_ID)); - assert.ok(positionMintInfos.get(positionMints[base+2].toBase58())?.tokenProgram.equals(includeTokenExtensionBasedPosition ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID)); + assert.ok( + positionMintInfos + .get(positionMints[base + 0].toBase58()) + ?.tokenProgram.equals( + includeTokenExtensionBasedPosition + ? TOKEN_2022_PROGRAM_ID + : TOKEN_PROGRAM_ID, + ), + ); + assert.ok( + positionMintInfos + .get(positionMints[base + 1].toBase58()) + ?.tokenProgram.equals(TOKEN_PROGRAM_ID), + ); + assert.ok( + positionMintInfos + .get(positionMints[base + 2].toBase58()) + ?.tokenProgram.equals( + includeTokenExtensionBasedPosition + ? TOKEN_2022_PROGRAM_ID + : TOKEN_PROGRAM_ID, + ), + ); } await sleep(2); // accrueRewards @@ -629,8 +673,8 @@ describe("WhirlpoolImpl#collectFeesAndRewardsForPositions()", () => { )), }), ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); // Accrue fees in token B await toTx( @@ -667,8 +711,8 @@ describe("WhirlpoolImpl#collectFeesAndRewardsForPositions()", () => { )), }), ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); const poolData = await pool.refreshData(); const positionData = await position.refreshData(); diff --git a/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl.test.ts b/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl.test.ts index d8f8550fc..d4ef1018e 100644 --- a/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl.test.ts +++ b/legacy-sdk/whirlpool/tests/sdk/whirlpools/whirlpool-impl.test.ts @@ -912,8 +912,8 @@ describe("whirlpool-impl", () => { )), }), ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); // Accrue fees in token B await toTx( @@ -950,8 +950,8 @@ describe("whirlpool-impl", () => { )), }), ) - .prependInstruction(useMaxCU()) // TransferHook require much CU - .buildAndExecute(); + .prependInstruction(useMaxCU()) // TransferHook require much CU + .buildAndExecute(); // accrue rewards // closePosition does not attempt to create an ATA unless reward has accumulated. @@ -1109,159 +1109,172 @@ describe("whirlpool-impl", () => { const withMetadataVariations = [true, false]; withMetadataVariations.forEach((withMetadata) => { - it(withMetadata ? "openPositionWithMetadata" : "openPosition", async () => { - const funderKeypair = anchor.web3.Keypair.generate(); - await systemTransferTx( - provider, - funderKeypair.publicKey, - ONE_SOL, - ).buildAndExecute(); - - const { poolInitInfo } = await initTestPool( - ctx, - TickSpacing.Standard, - PriceMath.priceToSqrtPriceX64(new Decimal(100), 6, 6), - ); - const pool = await client.getPool(poolInitInfo.whirlpoolPda.publicKey); - - // Verify token mint info is correct - const tokenAInfo = pool.getTokenAInfo(); - const tokenBInfo = pool.getTokenBInfo(); - assert.ok(tokenAInfo.mint.equals(poolInitInfo.tokenMintA)); - assert.ok(tokenBInfo.mint.equals(poolInitInfo.tokenMintB)); + it( + withMetadata ? "openPositionWithMetadata" : "openPosition", + async () => { + const funderKeypair = anchor.web3.Keypair.generate(); + await systemTransferTx( + provider, + funderKeypair.publicKey, + ONE_SOL, + ).buildAndExecute(); - // Create and mint tokens in this wallet - const mintedTokenAmount = 150_000_000; - const [userTokenAAccount, userTokenBAccount] = - await mintTokensToTestAccount( - ctx.provider, - tokenAInfo.mint, - mintedTokenAmount, - tokenBInfo.mint, - mintedTokenAmount, + const { poolInitInfo } = await initTestPool( + ctx, + TickSpacing.Standard, + PriceMath.priceToSqrtPriceX64(new Decimal(100), 6, 6), + ); + const pool = await client.getPool( + poolInitInfo.whirlpoolPda.publicKey, ); - // Open a position with no tick arrays initialized. - const lowerPrice = new Decimal(96); - const upperPrice = new Decimal(101); - const poolData = pool.getData(); - const tokenADecimal = tokenAInfo.decimals; - const tokenBDecimal = tokenBInfo.decimals; - - const tickLower = TickUtil.getInitializableTickIndex( - PriceMath.priceToTickIndex(lowerPrice, tokenADecimal, tokenBDecimal), - poolData.tickSpacing, - ); - const tickUpper = TickUtil.getInitializableTickIndex( - PriceMath.priceToTickIndex(upperPrice, tokenADecimal, tokenBDecimal), - poolData.tickSpacing, - ); + // Verify token mint info is correct + const tokenAInfo = pool.getTokenAInfo(); + const tokenBInfo = pool.getTokenBInfo(); + assert.ok(tokenAInfo.mint.equals(poolInitInfo.tokenMintA)); + assert.ok(tokenBInfo.mint.equals(poolInitInfo.tokenMintB)); + + // Create and mint tokens in this wallet + const mintedTokenAmount = 150_000_000; + const [userTokenAAccount, userTokenBAccount] = + await mintTokensToTestAccount( + ctx.provider, + tokenAInfo.mint, + mintedTokenAmount, + tokenBInfo.mint, + mintedTokenAmount, + ); + + // Open a position with no tick arrays initialized. + const lowerPrice = new Decimal(96); + const upperPrice = new Decimal(101); + const poolData = pool.getData(); + const tokenADecimal = tokenAInfo.decimals; + const tokenBDecimal = tokenBInfo.decimals; + + const tickLower = TickUtil.getInitializableTickIndex( + PriceMath.priceToTickIndex( + lowerPrice, + tokenADecimal, + tokenBDecimal, + ), + poolData.tickSpacing, + ); + const tickUpper = TickUtil.getInitializableTickIndex( + PriceMath.priceToTickIndex( + upperPrice, + tokenADecimal, + tokenBDecimal, + ), + poolData.tickSpacing, + ); - const inputTokenMint = poolData.tokenMintA; - const quote = increaseLiquidityQuoteByInputToken( - inputTokenMint, - new Decimal(50), - tickLower, - tickUpper, - Percentage.fromFraction(1, 100), - pool, - await TokenExtensionUtil.buildTokenExtensionContext( - fetcher, - poolData, - IGNORE_CACHE, - ), - ); + const inputTokenMint = poolData.tokenMintA; + const quote = increaseLiquidityQuoteByInputToken( + inputTokenMint, + new Decimal(50), + tickLower, + tickUpper, + Percentage.fromFraction(1, 100), + pool, + await TokenExtensionUtil.buildTokenExtensionContext( + fetcher, + poolData, + IGNORE_CACHE, + ), + ); - // [Action] Initialize Tick Arrays - const initTickArrayTx = ( - await pool.initTickArrayForTicks( - [tickLower, tickUpper], + // [Action] Initialize Tick Arrays + const initTickArrayTx = ( + await pool.initTickArrayForTicks( + [tickLower, tickUpper], + funderKeypair.publicKey, + ) + )?.addSigner(funderKeypair); + + assert.ok(!!initTickArrayTx); + + // [Action] Open Position (and increase L) + const openMethod = withMetadata + ? pool.openPositionWithMetadata.bind(pool) + : pool.openPosition.bind(pool); + const { positionMint, tx: openIx } = await openMethod( + tickLower, + tickUpper, + quote, + ctx.wallet.publicKey, funderKeypair.publicKey, - ) - )?.addSigner(funderKeypair); - - assert.ok(!!initTickArrayTx); - - // [Action] Open Position (and increase L) - const openMethod = withMetadata - ? pool.openPositionWithMetadata.bind(pool) - : pool.openPosition.bind(pool); - const { positionMint, tx: openIx } = await openMethod( - tickLower, - tickUpper, - quote, - ctx.wallet.publicKey, - funderKeypair.publicKey, - undefined, - TOKEN_2022_PROGRAM_ID, - ); - openIx.addSigner(funderKeypair); - - await initTickArrayTx.buildAndExecute(); - await openIx.buildAndExecute(); - - // Verify position exists and numbers fit input parameters - const positionAddress = PDAUtil.getPosition( - ctx.program.programId, - positionMint, - ).publicKey; - const position = await client.getPosition( - positionAddress, - IGNORE_CACHE, - ); - const positionData = position.getData(); + undefined, + TOKEN_2022_PROGRAM_ID, + ); + openIx.addSigner(funderKeypair); + + await initTickArrayTx.buildAndExecute(); + await openIx.buildAndExecute(); + + // Verify position exists and numbers fit input parameters + const positionAddress = PDAUtil.getPosition( + ctx.program.programId, + positionMint, + ).publicKey; + const position = await client.getPosition( + positionAddress, + IGNORE_CACHE, + ); + const positionData = position.getData(); - const tickLowerIndex = TickUtil.getInitializableTickIndex( - PriceMath.priceToTickIndex( - lowerPrice, - tokenAInfo.decimals, - tokenBInfo.decimals, - ), - poolData.tickSpacing, - ); - const tickUpperIndex = TickUtil.getInitializableTickIndex( - PriceMath.priceToTickIndex( - upperPrice, - tokenAInfo.decimals, - tokenBInfo.decimals, - ), - poolData.tickSpacing, - ); - assert.ok(positionData.liquidity.eq(quote.liquidityAmount)); - assert.ok(positionData.tickLowerIndex === tickLowerIndex); - assert.ok(positionData.tickUpperIndex === tickUpperIndex); - assert.ok(positionData.positionMint.equals(positionMint)); - assert.ok( - positionData.whirlpool.equals(poolInitInfo.whirlpoolPda.publicKey), - ); + const tickLowerIndex = TickUtil.getInitializableTickIndex( + PriceMath.priceToTickIndex( + lowerPrice, + tokenAInfo.decimals, + tokenBInfo.decimals, + ), + poolData.tickSpacing, + ); + const tickUpperIndex = TickUtil.getInitializableTickIndex( + PriceMath.priceToTickIndex( + upperPrice, + tokenAInfo.decimals, + tokenBInfo.decimals, + ), + poolData.tickSpacing, + ); + assert.ok(positionData.liquidity.eq(quote.liquidityAmount)); + assert.ok(positionData.tickLowerIndex === tickLowerIndex); + assert.ok(positionData.tickUpperIndex === tickUpperIndex); + assert.ok(positionData.positionMint.equals(positionMint)); + assert.ok( + positionData.whirlpool.equals(poolInitInfo.whirlpoolPda.publicKey), + ); - // [Action] Close Position - const txs = await pool.closePosition( - positionAddress, - Percentage.fromFraction(1, 100), - ); + // [Action] Close Position + const txs = await pool.closePosition( + positionAddress, + Percentage.fromFraction(1, 100), + ); - for (const tx of txs) { - await tx.buildAndExecute(); - } + for (const tx of txs) { + await tx.buildAndExecute(); + } - // Verify position is closed and owner wallet has the tokens back - const postClosePosition = await fetcher.getPosition( - positionAddress, - IGNORE_CACHE, - ); - assert.ok(postClosePosition === null); + // Verify position is closed and owner wallet has the tokens back + const postClosePosition = await fetcher.getPosition( + positionAddress, + IGNORE_CACHE, + ); + assert.ok(postClosePosition === null); - // TODO: we are leaking 1 decimal place of token? - assert.equal( - await getTokenBalance(ctx.provider, userTokenAAccount), - mintedTokenAmount - 1, - ); - assert.equal( - await getTokenBalance(ctx.provider, userTokenBAccount), - mintedTokenAmount - 1, - ); - }); + // TODO: we are leaking 1 decimal place of token? + assert.equal( + await getTokenBalance(ctx.provider, userTokenAAccount), + mintedTokenAmount - 1, + ); + assert.equal( + await getTokenBalance(ctx.provider, userTokenBAccount), + mintedTokenAmount - 1, + ); + }, + ); }); }); diff --git a/legacy-sdk/whirlpool/tests/utils/init-utils.ts b/legacy-sdk/whirlpool/tests/utils/init-utils.ts index f43d6e724..b7dd14106 100644 --- a/legacy-sdk/whirlpool/tests/utils/init-utils.ts +++ b/legacy-sdk/whirlpool/tests/utils/init-utils.ts @@ -1,7 +1,12 @@ import * as anchor from "@coral-xyz/anchor"; import type { PDA } from "@orca-so/common-sdk"; import { AddressUtil, MathUtil } from "@orca-so/common-sdk"; -import { NATIVE_MINT, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { + NATIVE_MINT, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, + getAssociatedTokenAddressSync, +} from "@solana/spl-token"; import type { PublicKey } from "@solana/web3.js"; import { Keypair } from "@solana/web3.js"; import type BN from "bn.js"; @@ -544,8 +549,8 @@ export async function openPosition( positionMintAddress: result.params.positionMint, // add metadata metadataPda: PDAUtil.getPositionMetadata(result.params.positionMint), - } - } + }, + }; } return openPositionWithOptMetadata( @@ -615,16 +620,20 @@ async function openPositionWithTokenExtensions( owner: PublicKey = ctx.provider.wallet.publicKey, funder?: Keypair, ) { - const { params, mint } = await generateDefaultOpenPositionWithTokenExtensionsParams( + const { params, mint } = + await generateDefaultOpenPositionWithTokenExtensionsParams( + ctx, + whirlpool, + withMetadata, + tickLowerIndex, + tickUpperIndex, + owner, + funder?.publicKey || ctx.provider.wallet.publicKey, + ); + let tx = toTx( ctx, - whirlpool, - withMetadata, - tickLowerIndex, - tickUpperIndex, - owner, - funder?.publicKey || ctx.provider.wallet.publicKey, + WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params), ); - let tx = toTx(ctx, WhirlpoolIx.openPositionWithTokenExtensionsIx(ctx.program, params)); tx.addSigner(mint); if (funder) { tx.addSigner(funder); @@ -853,9 +862,10 @@ export async function fundPositionsWithClient( true, ); - const tokenProgramId = (param.isTokenExtensionsBasedPosition ?? false) - ? TOKEN_2022_PROGRAM_ID - : TOKEN_PROGRAM_ID; + const tokenProgramId = + (param.isTokenExtensionsBasedPosition ?? false) + ? TOKEN_2022_PROGRAM_ID + : TOKEN_PROGRAM_ID; const { tx } = await whirlpool.openPosition( param.tickLowerIndex, diff --git a/legacy-sdk/whirlpool/tests/utils/test-builders.ts b/legacy-sdk/whirlpool/tests/utils/test-builders.ts index b0a55420a..5b752434a 100644 --- a/legacy-sdk/whirlpool/tests/utils/test-builders.ts +++ b/legacy-sdk/whirlpool/tests/utils/test-builders.ts @@ -1,7 +1,11 @@ import type { AnchorProvider } from "@coral-xyz/anchor"; import type { PDA } from "@orca-so/common-sdk"; import { AddressUtil, MathUtil, Percentage } from "@orca-so/common-sdk"; -import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; import type { PublicKey } from "@solana/web3.js"; import { Keypair } from "@solana/web3.js"; import Decimal from "decimal.js"; diff --git a/legacy-sdk/whirlpool/tests/utils/v2/init-utils-v2.ts b/legacy-sdk/whirlpool/tests/utils/v2/init-utils-v2.ts index d7732b578..58102402c 100644 --- a/legacy-sdk/whirlpool/tests/utils/v2/init-utils-v2.ts +++ b/legacy-sdk/whirlpool/tests/utils/v2/init-utils-v2.ts @@ -794,7 +794,7 @@ export async function fundPositionsV2( poolInitInfo.tokenMintB, tokenAccountB, tokenVaultBKeypair.publicKey, - ctx.provider.wallet.publicKey, + ctx.provider.wallet.publicKey, ); await toTx( diff --git a/legacy-sdk/whirlpool/tests/utils/v2/token-2022.ts b/legacy-sdk/whirlpool/tests/utils/v2/token-2022.ts index ef5a1797a..91a126fbc 100644 --- a/legacy-sdk/whirlpool/tests/utils/v2/token-2022.ts +++ b/legacy-sdk/whirlpool/tests/utils/v2/token-2022.ts @@ -1074,8 +1074,8 @@ function getAccountLenForMintHack(mintData: Mint): number { return ( getAccountLen( extensionTypes - .filter((type) => type !== (16 as ExtensionType)) - .map(getAccountTypeOfMintType), + .filter((type) => type !== (16 as ExtensionType)) + .map(getAccountTypeOfMintType), ) + confidentialTransferFeeAmountLen ); } diff --git a/yarn.lock b/yarn.lock index d2ce5183e..5f0fe3ec6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1753,7 +1753,7 @@ __metadata: languageName: node linkType: hard -"@coral-xyz/anchor@npm:~0.29.0": +"@coral-xyz/anchor@npm:0.29.0, @coral-xyz/anchor@npm:~0.29.0": version: 0.29.0 resolution: "@coral-xyz/anchor@npm:0.29.0" dependencies: @@ -1775,18 +1775,6 @@ __metadata: languageName: node linkType: hard -"@coral-xyz/borsh@npm:^0.26.0": - version: 0.26.0 - resolution: "@coral-xyz/borsh@npm:0.26.0" - dependencies: - bn.js: "npm:^5.1.2" - buffer-layout: "npm:^1.2.0" - peerDependencies: - "@solana/web3.js": ^1.68.0 - checksum: 10c0/537510e181f0caff81125d55729e69361a20dd5f6417a9962c3ad7fa6b482b58df5867dbd6311bf240358d53737b92b8b9d0b1b76fc780a19d4ff3ee2227ef4a - languageName: node - linkType: hard - "@coral-xyz/borsh@npm:^0.29.0": version: 0.29.0 resolution: "@coral-xyz/borsh@npm:0.29.0" @@ -3405,6 +3393,30 @@ __metadata: languageName: node linkType: hard +"@orca-so/common-sdk@npm:^0.3.0": + version: 0.3.7 + resolution: "@orca-so/common-sdk@npm:0.3.7" + dependencies: + "@solana/spl-token": "npm:^0.3.8" + "@solana/web3.js": "npm:^1.75.0" + decimal.js: "npm:^10.3.1" + tiny-invariant: "npm:^1.2.0" + checksum: 10c0/881032932d3b98764d0d419eab478ca2945c6b079a097542c2249a57d6cd92ca48b6143ca3855d080107634242254169b958f8f1e5eda26f47fb4f27b6322e72 + languageName: node + linkType: hard + +"@orca-so/orca-sdk@npm:0.1.1": + version: 0.1.1 + resolution: "@orca-so/orca-sdk@npm:0.1.1" + dependencies: + "@orca-so/common-sdk": "npm:^0.3.0" + "@solana/web3.js": "npm:^1.74.0" + axios: "npm:^0.27.2" + decimal.js: "npm:^10.3.1" + checksum: 10c0/91338fb2f66f25c1dd4458efe3bde319f5cc823b6f248be3869ca29df41b24cbf1882261f684230fc0917338707181cb022a4d72ebb273e916ccdf8fa16fc67b + languageName: node + linkType: hard + "@orca-so/whirlpools-client@npm:*, @orca-so/whirlpools-client@workspace:ts-sdk/client": version: 0.0.0-use.local resolution: "@orca-so/whirlpools-client@workspace:ts-sdk/client" @@ -3571,16 +3583,24 @@ __metadata: languageName: unknown linkType: soft -"@orca-so/whirlpools-scripts@workspace:legacy-sdk/scripts": +"@orca-so/whirlpools-sdk-cli@workspace:legacy-sdk/cli": version: 0.0.0-use.local - resolution: "@orca-so/whirlpools-scripts@workspace:legacy-sdk/scripts" + resolution: "@orca-so/whirlpools-sdk-cli@workspace:legacy-sdk/cli" dependencies: - "@project-serum/anchor": "npm:~0.26.0" - "@solana/spl-token": "npm:^0.4.8" - "@solana/web3.js": "npm:^1.95.2" - "@types/mz": "npm:^2.7.3" - mz: "npm:^2.7.0" - typescript: "npm:^5.6.3" + "@coral-xyz/anchor": "npm:0.29.0" + "@orca-so/common-sdk": "npm:0.6.3" + "@orca-so/orca-sdk": "npm:0.1.1" + "@orca-so/whirlpools-sdk": "npm:*" + "@solana/spl-token": "npm:0.4.1" + "@solana/web3.js": "npm:^1.90.0" + "@types/bn.js": "npm:^5.1.0" + "@types/prompts": "npm:^2.4.9" + bs58: "npm:^5.0.0" + decimal.js: "npm:^10.4.3" + js-convert-case: "npm:^4.2.0" + prompts: "npm:^2.4.2" + tsx: "npm:^4.19.0" + typescript: "npm:^5.4.5" languageName: unknown linkType: soft @@ -3666,29 +3686,6 @@ __metadata: languageName: node linkType: hard -"@project-serum/anchor@npm:~0.26.0": - version: 0.26.0 - resolution: "@project-serum/anchor@npm:0.26.0" - dependencies: - "@coral-xyz/borsh": "npm:^0.26.0" - "@solana/web3.js": "npm:^1.68.0" - base64-js: "npm:^1.5.1" - bn.js: "npm:^5.1.2" - bs58: "npm:^4.0.1" - buffer-layout: "npm:^1.2.2" - camelcase: "npm:^6.3.0" - cross-fetch: "npm:^3.1.5" - crypto-hash: "npm:^1.3.0" - eventemitter3: "npm:^4.0.7" - js-sha256: "npm:^0.9.0" - pako: "npm:^2.0.3" - snake-case: "npm:^3.0.4" - superstruct: "npm:^0.15.4" - toml: "npm:^3.0.0" - checksum: 10c0/1d2dffd56eef215d8f8f9481c51c2de5db2869ddb21d54cda687f51a8c4ac7fd2662acab0edb5bce55c4527680a741c3c16631d60753aff9b2fe8295bad6c26b - languageName: node - linkType: hard - "@rollup/rollup-android-arm-eabi@npm:4.24.4": version: 4.24.4 resolution: "@rollup/rollup-android-arm-eabi@npm:4.24.4" @@ -4531,7 +4528,7 @@ __metadata: languageName: node linkType: hard -"@solana/spl-token-metadata@npm:^0.1.6": +"@solana/spl-token-metadata@npm:^0.1.2, @solana/spl-token-metadata@npm:^0.1.6": version: 0.1.6 resolution: "@solana/spl-token-metadata@npm:0.1.6" dependencies: @@ -4542,6 +4539,34 @@ __metadata: languageName: node linkType: hard +"@solana/spl-token@npm:0.4.1": + version: 0.4.1 + resolution: "@solana/spl-token@npm:0.4.1" + dependencies: + "@solana/buffer-layout": "npm:^4.0.0" + "@solana/buffer-layout-utils": "npm:^0.2.0" + "@solana/spl-token-metadata": "npm:^0.1.2" + buffer: "npm:^6.0.3" + peerDependencies: + "@solana/web3.js": ^1.90.0 + checksum: 10c0/be7866b4b090c1632ef61576911ac2e6044e88d25d8121877b3df4d3caac34dc883e77e6b6e61d36fe84e0351ad10ba7d56049431aa9d2e14fdf58d284707f44 + languageName: node + linkType: hard + +"@solana/spl-token@npm:^0.3.8": + version: 0.3.11 + resolution: "@solana/spl-token@npm:0.3.11" + dependencies: + "@solana/buffer-layout": "npm:^4.0.0" + "@solana/buffer-layout-utils": "npm:^0.2.0" + "@solana/spl-token-metadata": "npm:^0.1.2" + buffer: "npm:^6.0.3" + peerDependencies: + "@solana/web3.js": ^1.88.0 + checksum: 10c0/eae23e1ced21c8cc117a43d4c6e1b1f0f9aa412619e22f7fed96e15a833e350dddd6ec1b1498f5274a9542d8b0fdbd9ce93a5b79ed77863f7170951aeacb18fe + languageName: node + linkType: hard + "@solana/spl-token@npm:^0.4.8": version: 0.4.9 resolution: "@solana/spl-token@npm:0.4.9" @@ -4641,7 +4666,7 @@ __metadata: languageName: node linkType: hard -"@solana/web3.js@npm:^1.32.0, @solana/web3.js@npm:^1.68.0, @solana/web3.js@npm:^1.90.0, @solana/web3.js@npm:^1.95.2": +"@solana/web3.js@npm:^1.32.0, @solana/web3.js@npm:^1.68.0, @solana/web3.js@npm:^1.74.0, @solana/web3.js@npm:^1.75.0, @solana/web3.js@npm:^1.90.0": version: 1.95.4 resolution: "@solana/web3.js@npm:1.95.4" dependencies: @@ -4891,7 +4916,7 @@ __metadata: languageName: node linkType: hard -"@types/bn.js@npm:~5.1.6": +"@types/bn.js@npm:^5.1.0, @types/bn.js@npm:~5.1.6": version: 5.1.6 resolution: "@types/bn.js@npm:5.1.6" dependencies: @@ -5163,15 +5188,6 @@ __metadata: languageName: node linkType: hard -"@types/mz@npm:^2.7.3": - version: 2.7.8 - resolution: "@types/mz@npm:2.7.8" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/5d00871d016c9ebc09e32e6cfcc39bd5901d7731dbdb37af2c8b789d4a1212c418342723baa0fa0d66badcd7297ffdcab376af7ef52a2ce4c9197ea03686f8b6 - languageName: node - linkType: hard - "@types/node-forge@npm:^1.3.0": version: 1.3.11 resolution: "@types/node-forge@npm:1.3.11" @@ -5218,6 +5234,16 @@ __metadata: languageName: node linkType: hard +"@types/prompts@npm:^2.4.9": + version: 2.4.9 + resolution: "@types/prompts@npm:2.4.9" + dependencies: + "@types/node": "npm:*" + kleur: "npm:^3.0.3" + checksum: 10c0/22fe0da6807681c85e88ba283184f4be4c8a95c744ea12a638865c98c4e0c22e7f733542f6b0f1fbca02245cdc3fe84feacf9c9adf4ddd8bc98a337fd679d8d2 + languageName: node + linkType: hard + "@types/prop-types@npm:*": version: 15.7.13 resolution: "@types/prop-types@npm:15.7.13" @@ -6204,6 +6230,16 @@ __metadata: languageName: node linkType: hard +"axios@npm:^0.27.2": + version: 0.27.2 + resolution: "axios@npm:0.27.2" + dependencies: + follow-redirects: "npm:^1.14.9" + form-data: "npm:^4.0.0" + checksum: 10c0/76d673d2a90629944b44d6f345f01e58e9174690f635115d5ffd4aca495d99bcd8f95c590d5ccb473513f5ebc1d1a6e8934580d0c57cdd0498c3a101313ef771 + languageName: node + linkType: hard + "axios@npm:^1.7.4": version: 1.7.7 resolution: "axios@npm:1.7.7" @@ -6296,7 +6332,14 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": +"base-x@npm:^4.0.0": + version: 4.0.0 + resolution: "base-x@npm:4.0.0" + checksum: 10c0/0cb47c94535144ab138f70bb5aa7e6e03049ead88615316b62457f110fc204f2c3baff5c64a1c1b33aeb068d79a68092c08a765c7ccfa133eee1e70e4c6eb903 + languageName: node + linkType: hard + +"base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf @@ -6510,6 +6553,15 @@ __metadata: languageName: node linkType: hard +"bs58@npm:^5.0.0": + version: 5.0.0 + resolution: "bs58@npm:5.0.0" + dependencies: + base-x: "npm:^4.0.0" + checksum: 10c0/0d1b05630b11db48039421b5975cb2636ae0a42c62f770eec257b2e5c7d94cb5f015f440785f3ec50870a6e9b1132b35bd0a17c7223655b22229f24b2a3491d1 + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -7567,7 +7619,7 @@ __metadata: languageName: node linkType: hard -"decimal.js@npm:^10.4.3": +"decimal.js@npm:^10.3.1, decimal.js@npm:^10.4.3": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" checksum: 10c0/6d60206689ff0911f0ce968d40f163304a6c1bc739927758e6efc7921cfa630130388966f16bf6ef6b838cb33679fbe8e7a78a2f3c478afce841fd55ac8fb8ee @@ -9053,7 +9105,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.15.6": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.8, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.6": version: 1.15.9 resolution: "follow-redirects@npm:1.15.9" peerDependenciesMeta: @@ -10744,10 +10796,10 @@ __metadata: languageName: node linkType: hard -"js-sha256@npm:^0.9.0": - version: 0.9.0 - resolution: "js-sha256@npm:0.9.0" - checksum: 10c0/f20b9245f6ebe666f42ca05536f777301132fb1aa7fbc22f10578fa302717a6cca507344894efdeaf40a011256eb2f7d517b94ac7105bd5cf087fa61551ad634 +"js-convert-case@npm:^4.2.0": + version: 4.2.0 + resolution: "js-convert-case@npm:4.2.0" + checksum: 10c0/70a0355e887c899842d9c9776c5ef85f9541cb2c8fb34b71111be8cbe0ab140d069232abeea3855da95e62091b465936565769c7531883c22ba8b1bdbc4fa4f2 languageName: node linkType: hard @@ -15861,7 +15913,7 @@ __metadata: languageName: node linkType: hard -"tiny-invariant@npm:^1.0.2, tiny-invariant@npm:^1.3.1": +"tiny-invariant@npm:^1.0.2, tiny-invariant@npm:^1.2.0, tiny-invariant@npm:^1.3.1": version: 1.3.3 resolution: "tiny-invariant@npm:1.3.3" checksum: 10c0/65af4a07324b591a059b35269cd696aba21bef2107f29b9f5894d83cc143159a204b299553435b03874ebb5b94d019afa8b8eff241c8a4cfee95872c2e1c1c4a @@ -16176,7 +16228,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.5.4, typescript@npm:^5.6.3": +"typescript@npm:^5.4.5, typescript@npm:^5.5.4, typescript@npm:^5.6.3": version: 5.6.3 resolution: "typescript@npm:5.6.3" bin: @@ -16186,7 +16238,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.5.4#optional!builtin, typescript@patch:typescript@npm%3A^5.6.3#optional!builtin": +"typescript@patch:typescript@npm%3A^5.4.5#optional!builtin, typescript@patch:typescript@npm%3A^5.5.4#optional!builtin, typescript@patch:typescript@npm%3A^5.6.3#optional!builtin": version: 5.6.3 resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=8c6c40" bin: