UNPKG

@mercurial-finance/dynamic-amm-sdk

Version:

Mercurial Vaults SDK is a typescript library that allows you to interact with Mercurial v2's AMM.

946 lines (945 loc) 108 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const anchor_1 = require("@coral-xyz/anchor"); const web3_js_1 = require("@solana/web3.js"); const spl_token_1 = require("@solana/spl-token"); const vault_sdk_1 = __importStar(require("@mercurial-finance/vault-sdk")); const m3m3_1 = __importStar(require("@meteora-ag/m3m3")); const invariant_1 = __importDefault(require("invariant")); const types_1 = require("./types"); const constants_1 = require("./constants"); const curve_1 = require("./curve"); const constant_product_1 = require("./curve/constant-product"); const utils_1 = require("./utils"); const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes"); const bn_sqrt_1 = __importDefault(require("bn-sqrt")); const getAllPoolState = async (poolMints, program) => { const poolStates = (await (0, utils_1.chunkedFetchMultiplePoolAccount)(program, poolMints)); (0, invariant_1.default)(poolStates.length === poolMints.length, 'Some of the pool state not found'); const poolLpMints = poolStates.map((poolState) => poolState.lpMint); const lpMintAccounts = await (0, utils_1.chunkedGetMultipleAccountInfos)(program.provider.connection, poolLpMints); return poolStates.map((poolState, idx) => { const lpMintAccount = lpMintAccounts[idx]; (0, invariant_1.default)(lpMintAccount, constants_1.ERROR.INVALID_ACCOUNT); const lpSupply = new anchor_1.BN(spl_token_1.MintLayout.decode(lpMintAccount.data).supply.toString()); return { ...poolState, lpSupply }; }); }; const getPoolState = async (poolMint, program) => { const poolState = (await program.account.pool.fetchNullable(poolMint)); (0, invariant_1.default)(poolState, `Pool ${poolMint.toBase58()} not found`); const account = await program.provider.connection.getTokenSupply(poolState.lpMint); (0, invariant_1.default)(account.value.amount, constants_1.ERROR.INVALID_ACCOUNT); return { ...poolState, lpSupply: new anchor_1.BN(account.value.amount) }; }; const getFeeVaultState = async (publicKey, program) => { const feeVaultState = await program.account.feeVault.fetchNullable(publicKey); return feeVaultState; }; const decodeAccountTypeMapper = (type) => { const decoder = { [types_1.AccountType.VAULT_A_RESERVE]: (accountData) => new anchor_1.BN(spl_token_1.AccountLayout.decode(accountData).amount.toString()), [types_1.AccountType.VAULT_B_RESERVE]: (accountData) => new anchor_1.BN(spl_token_1.AccountLayout.decode(accountData).amount.toString()), [types_1.AccountType.VAULT_A_LP]: (accountData) => new anchor_1.BN(spl_token_1.MintLayout.decode(accountData).supply.toString()), [types_1.AccountType.VAULT_B_LP]: (accountData) => new anchor_1.BN(spl_token_1.MintLayout.decode(accountData).supply.toString()), [types_1.AccountType.POOL_VAULT_A_LP]: (accountData) => new anchor_1.BN(spl_token_1.AccountLayout.decode(accountData).amount.toString()), [types_1.AccountType.POOL_VAULT_B_LP]: (accountData) => new anchor_1.BN(spl_token_1.AccountLayout.decode(accountData).amount.toString()), [types_1.AccountType.POOL_LP_MINT]: (accountData) => new anchor_1.BN(spl_token_1.MintLayout.decode(accountData).supply.toString()), [types_1.AccountType.SYSVAR_CLOCK]: (accountData) => new anchor_1.BN(accountData.readBigInt64LE(32).toString()), }; return decoder[type]; }; const getAccountsBuffer = async (connection, accountsToFetch) => { const accounts = await (0, utils_1.chunkedGetMultipleAccountInfos)(connection, accountsToFetch.map((account) => account.pubkey)); return accountsToFetch.reduce((accMap, account, index) => { const accountInfo = accounts[index]; accMap.set(account.pubkey.toBase58(), { type: account.type, account: accountInfo, }); return accMap; }, new Map()); }; const deserializeAccountsBuffer = (accountInfoMap) => { return Array.from(accountInfoMap).reduce((accValue, [publicKey, { type, account }]) => { const decodedAccountInfo = decodeAccountTypeMapper(type); accValue.set(publicKey, decodedAccountInfo(account.data)); return accValue; }, new Map()); }; class AmmImpl { address; program; vaultProgram; tokenAMint; tokenBMint; poolState; poolInfo; vaultA; vaultB; accountsInfo; swapCurve; depegAccounts; opt = { cluster: 'mainnet-beta', }; constructor(address, program, vaultProgram, tokenAMint, tokenBMint, poolState, poolInfo, vaultA, vaultB, accountsInfo, swapCurve, depegAccounts, opt) { this.address = address; this.program = program; this.vaultProgram = vaultProgram; this.tokenAMint = tokenAMint; this.tokenBMint = tokenBMint; this.poolState = poolState; this.poolInfo = poolInfo; this.vaultA = vaultA; this.vaultB = vaultB; this.accountsInfo = accountsInfo; this.swapCurve = swapCurve; this.depegAccounts = depegAccounts; this.opt = { ...this.opt, ...opt, }; } static async createConfig(connection, payer, tradeFeeBps, protocolFeeBps, vaultConfigKey, activationDuration, poolCreatorAuthority, activationType, partnerFeeNumerator, opt) { const { ammProgram } = (0, utils_1.createProgram)(connection, opt?.programId); const configs = await this.getFeeConfigurations(connection, opt); let index = 0; while (true) { const configPda = (0, utils_1.deriveConfigPda)(new anchor_1.BN(index), ammProgram.programId); if (!configs.find((c) => c.publicKey.equals(configPda))) { const createConfigTx = await ammProgram.methods .createConfig({ // Default fee denominator is 100_000 tradeFeeNumerator: tradeFeeBps.mul(new anchor_1.BN(10)), protocolTradeFeeNumerator: protocolFeeBps.mul(new anchor_1.BN(10)), vaultConfigKey, activationDuration, poolCreatorAuthority, index: new anchor_1.BN(index), activationType, partnerFeeNumerator, }) .accounts({ config: configPda, systemProgram: web3_js_1.SystemProgram.programId, admin: payer, }) .transaction(); return new web3_js_1.Transaction({ feePayer: payer, ...(await ammProgram.provider.connection.getLatestBlockhash(ammProgram.provider.connection.commitment)), }).add(createConfigTx); } else { index++; } } } static async searchPoolsByToken(connection, tokenMint) { const { ammProgram } = (0, utils_1.createProgram)(connection); const [poolsForTokenAMint, poolsForTokenBMint] = await Promise.all([ ammProgram.account.pool.all([ { memcmp: { offset: 8 + 32, bytes: tokenMint.toBase58(), }, }, ]), ammProgram.account.pool.all([ { memcmp: { offset: 8 + 32 + 32, bytes: tokenMint.toBase58(), }, }, ]), ]); return [...poolsForTokenAMint, ...poolsForTokenBMint]; } async partnerClaimFees(partnerAddress, maxAmountA, maxAmountB) { let preInstructions = []; const [[partnerTokenA, createPartnerTokenAIx], [partnerTokenB, createPartnerTokenBIx]] = await this.createATAPreInstructions(partnerAddress, [this.poolState.tokenAMint, this.poolState.tokenBMint]); createPartnerTokenAIx && preInstructions.push(createPartnerTokenAIx); createPartnerTokenBIx && preInstructions.push(createPartnerTokenBIx); const claimTx = await this.program.methods .partnerClaimFee(maxAmountA, maxAmountB) .accounts({ pool: this.address, aVaultLp: this.poolState.aVaultLp, protocolTokenAFee: this.poolState.protocolTokenAFee, protocolTokenBFee: this.poolState.protocolTokenBFee, partnerTokenA, partnerTokenB, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, partnerAuthority: partnerAddress, }) .preInstructions(preInstructions) .transaction(); return new web3_js_1.Transaction({ feePayer: partnerAddress, ...(await this.program.provider.connection.getLatestBlockhash(this.program.provider.connection.commitment)), }).add(claimTx); } static async createCustomizablePermissionlessConstantProductPool(connection, payer, tokenAMint, tokenBMint, tokenAAmount, tokenBAmount, customizableParams, opt) { const { vaultProgram, ammProgram } = (0, utils_1.createProgram)(connection, opt?.programId); const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [(0, vault_sdk_1.getVaultPdas)(tokenAMint, vaultProgram.programId), (0, vault_sdk_1.getVaultPdas)(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = await Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; let preInstructions = []; if (!aVaultAccount) { const createVaultAIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, tokenAMint); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, tokenBMint); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = (0, utils_1.deriveCustomizablePermissionlessConstantProductPoolAddress)(tokenAMint, tokenBMint, ammProgram.programId); const [lpMint] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const [[aVaultLp], [bVaultLp]] = [ web3_js_1.PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = await Promise.all([ (0, utils_1.getOrCreateATAInstruction)(tokenAMint, payer, connection), (0, utils_1.getOrCreateATAInstruction)(tokenBMint, payer, connection), ]); createPayerTokenAIx && preInstructions.push(createPayerTokenAIx); createPayerTokenBIx && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const payerPoolLp = await (0, utils_1.getAssociatedTokenAccount)(lpMint, payer); if (tokenAMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenB, BigInt(tokenBAmount.toString()))); } const [mintMetadata, _mintMetadataBump] = (0, utils_1.deriveMintMetadata)(lpMint); const createPermissionlessPoolTx = await ammProgram.methods .initializeCustomizablePermissionlessConstantProductPool(tokenAAmount, tokenBAmount, customizableParams) .accounts({ pool: poolPubkey, tokenAMint, tokenBMint, aVault, bVault, aVaultLpMint, bVaultLpMint, aVaultLp, bVaultLp, lpMint, payerTokenA, payerTokenB, protocolTokenAFee, protocolTokenBFee, payerPoolLp, aTokenVault, bTokenVault, mintMetadata, metadataProgram: constants_1.METAPLEX_PROGRAM, payer, rent: web3_js_1.SYSVAR_RENT_PUBKEY, vaultProgram: vaultProgram.programId, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, systemProgram: web3_js_1.SystemProgram.programId, associatedTokenProgram: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, }) .preInstructions(preInstructions) .transaction(); const latestBlockHash = await ammProgram.provider.connection.getLatestBlockhash(ammProgram.provider.connection.commitment); return new web3_js_1.Transaction({ feePayer: payer, ...latestBlockHash, }).add(createPermissionlessPoolTx); } static async createPermissionlessConstantProductPoolWithConfig2(connection, payer, tokenAMint, tokenBMint, tokenAAmount, tokenBAmount, config, opt) { const { vaultProgram, ammProgram } = (0, utils_1.createProgram)(connection, opt?.programId); const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [(0, vault_sdk_1.getVaultPdas)(tokenAMint, vaultProgram.programId), (0, vault_sdk_1.getVaultPdas)(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = await Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; let preInstructions = []; if (!aVaultAccount) { const createVaultAIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, tokenAMint); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, tokenBMint); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = (0, utils_1.derivePoolAddressWithConfig)(tokenAMint, tokenBMint, config, ammProgram.programId); const [lpMint] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const [[aVaultLp], [bVaultLp]] = [ web3_js_1.PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = await Promise.all([ (0, utils_1.getOrCreateATAInstruction)(tokenAMint, payer, connection), (0, utils_1.getOrCreateATAInstruction)(tokenBMint, payer, connection), ]); createPayerTokenAIx && preInstructions.push(createPayerTokenAIx); createPayerTokenBIx && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const payerPoolLp = await (0, utils_1.getAssociatedTokenAccount)(lpMint, payer); if (tokenAMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenB, BigInt(tokenBAmount.toString()))); } const [mintMetadata, _mintMetadataBump] = (0, utils_1.deriveMintMetadata)(lpMint); const activationPoint = opt?.activationPoint || null; const lpAmount = (0, bn_sqrt_1.default)(tokenAAmount.mul(tokenBAmount)); const { userLockAmount, feeWrapperLockAmount } = (0, utils_1.calculateLockAmounts)(lpAmount, opt?.stakeLiquidity?.ratio); const createPoolPostInstructions = []; if (opt?.lockLiquidity && userLockAmount.gt(new anchor_1.BN(0))) { const [lockEscrowPK] = (0, utils_1.deriveLockEscrowPda)(poolPubkey, payer, ammProgram.programId); const createLockEscrowIx = await ammProgram.methods .createLockEscrow() .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, payer, systemProgram: web3_js_1.SystemProgram.programId, }) .instruction(); createPoolPostInstructions.push(createLockEscrowIx); const [escrowAta, createEscrowAtaIx] = await (0, utils_1.getOrCreateATAInstruction)(lpMint, lockEscrowPK, connection, payer); createEscrowAtaIx && createPoolPostInstructions.push(createEscrowAtaIx); const lockIx = await ammProgram.methods .lock(userLockAmount) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowAta, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .instruction(); createPoolPostInstructions.push(lockIx); } const createPermissionlessPoolTx = await ammProgram.methods .initializePermissionlessConstantProductPoolWithConfig2(tokenAAmount, tokenBAmount, activationPoint) .accounts({ pool: poolPubkey, tokenAMint, tokenBMint, aVault, bVault, aVaultLpMint, bVaultLpMint, aVaultLp, bVaultLp, lpMint, payerTokenA, payerTokenB, protocolTokenAFee, protocolTokenBFee, payerPoolLp, aTokenVault, bTokenVault, mintMetadata, metadataProgram: constants_1.METAPLEX_PROGRAM, payer, config, rent: web3_js_1.SYSVAR_RENT_PUBKEY, vaultProgram: vaultProgram.programId, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, systemProgram: web3_js_1.SystemProgram.programId, associatedTokenProgram: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, }) .postInstructions(createPoolPostInstructions) .transaction(); const ixs = []; if (preInstructions.length) { // https://explorer.solana.com/tx/3G95TWMmTLbZ7aAgyGmZd8JFk19ye8whVB5cXhYCCsUWSUsnuVMqPMXbPiY5WaF4zE2Sz7CG5e4jTj8NQbCnUG14?cluster=devnet // Create 2 dynamic vault consume around 190k CU. Each create ATA + Wrap SOL around 23k const setComputeUnitLimitIx = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000, }); ixs.push([setComputeUnitLimitIx, ...preInstructions]); } // https://explorer.solana.com/tx/4X37hBoUNwpmHKNGpQB3M72xDA7yhFKhbYrkBQUK4skBGD7P9LdWgb2WpvFGHcxYi13e9bdwhetsqmULeW7nUDbW?cluster=devnet // https://explorer.solana.com/tx/3xMGCQ9GAqSMZH1uhXXc1quZit61DXr1KsbVxdBN1zCkQy1GTXBGuWgxzWfMEdecdRi859m3mYQKuLyemvR18VHS?cluster=devnet // Create dynamic pool consume around 287k, create lock escrow + lock liquidity around 84k const setComputeUnitLimitIx = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 450_000, }); ixs.push([setComputeUnitLimitIx, createPermissionlessPoolTx]); if (feeWrapperLockAmount.gt(new anchor_1.BN(0))) { const preInstructions = []; const createFeeVaultIxs = await m3m3_1.default.createFeeVaultInstructions(connection, poolPubkey, payer, tokenAMint, tokenBMint); preInstructions.push(...createFeeVaultIxs); const vaultKey = (0, m3m3_1.deriveFeeVault)(poolPubkey, m3m3_1.STAKE_FOR_FEE_PROGRAM_ID); const [lockEscrowFeeVaultPK] = (0, utils_1.deriveLockEscrowPda)(poolPubkey, vaultKey, ammProgram.programId); const [escrowFeeVaultAta, createEscrowFeeVaultAtaIx] = await (0, utils_1.getOrCreateATAInstruction)(lpMint, lockEscrowFeeVaultPK, connection, payer); createEscrowFeeVaultAtaIx && preInstructions.push(createEscrowFeeVaultAtaIx); const lockTx = await ammProgram.methods .lock(feeWrapperLockAmount) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowFeeVaultPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowFeeVaultAta, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .preInstructions(preInstructions) .transaction(); ixs.push(lockTx); } if (opt?.swapLiquidity) { const protocolTokenFee = (0, utils_1.deriveProtocolTokenFee)(poolPubkey, tokenBMint, ammProgram.programId); const swapTx = await ammProgram.methods .swap(opt.swapLiquidity.inAmount, opt.swapLiquidity.minAmountOut) .accounts({ aTokenVault, bTokenVault, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, userSourceToken: payerTokenB, userDestinationToken: payerTokenA, user: payer, protocolTokenFee, pool: poolPubkey, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, vaultProgram: vaultProgram.programId, }) .transaction(); ixs.push(swapTx); } const resultTx = await (0, utils_1.createTransactions)(connection, ixs, payer); return resultTx; } static async createPermissionlessConstantProductPoolWithConfig(connection, payer, tokenAMint, tokenBMint, tokenAAmount, tokenBAmount, config, opt) { const { vaultProgram, ammProgram } = (0, utils_1.createProgram)(connection, opt?.programId); const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [(0, vault_sdk_1.getVaultPdas)(tokenAMint, vaultProgram.programId), (0, vault_sdk_1.getVaultPdas)(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = await Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; let preInstructions = []; if (!aVaultAccount) { const createVaultAIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, tokenAMint); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, tokenBMint); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = (0, utils_1.derivePoolAddressWithConfig)(tokenAMint, tokenBMint, config, ammProgram.programId); const [lpMint] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const [[aVaultLp], [bVaultLp]] = [ web3_js_1.PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = await Promise.all([ (0, utils_1.getOrCreateATAInstruction)(tokenAMint, payer, connection), (0, utils_1.getOrCreateATAInstruction)(tokenBMint, payer, connection), ]); createPayerTokenAIx && !opt?.skipAAta && preInstructions.push(createPayerTokenAIx); createPayerTokenBIx && !opt?.skipBAta && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const payerPoolLp = (0, utils_1.getAssociatedTokenAccount)(lpMint, payer); if (tokenAMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenB, BigInt(tokenBAmount.add(opt?.swapLiquidity?.inAmount ?? new anchor_1.BN(0)).toString()))); } const [mintMetadata, _mintMetadataBump] = (0, utils_1.deriveMintMetadata)(lpMint); const createPoolPostInstructions = []; if (opt?.lockLiquidity) { const [lockEscrowPK] = (0, utils_1.deriveLockEscrowPda)(poolPubkey, payer, ammProgram.programId); const createLockEscrowIx = await ammProgram.methods .createLockEscrow() .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, payer, systemProgram: web3_js_1.SystemProgram.programId, }) .instruction(); createPoolPostInstructions.push(createLockEscrowIx); const [escrowAta, createEscrowAtaIx] = await (0, utils_1.getOrCreateATAInstruction)(lpMint, lockEscrowPK, connection, payer); createEscrowAtaIx && createPoolPostInstructions.push(createEscrowAtaIx); const lockIx = await ammProgram.methods .lock(constants_1.U64_MAX) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowAta, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .instruction(); createPoolPostInstructions.push(lockIx); } const createPermissionlessPoolTx = await ammProgram.methods .initializePermissionlessConstantProductPoolWithConfig(tokenAAmount, tokenBAmount) .accounts({ pool: poolPubkey, tokenAMint, tokenBMint, aVault, bVault, aVaultLpMint, bVaultLpMint, aVaultLp, bVaultLp, lpMint, payerTokenA, payerTokenB, protocolTokenAFee, protocolTokenBFee, payerPoolLp, aTokenVault, bTokenVault, mintMetadata, metadataProgram: constants_1.METAPLEX_PROGRAM, payer, config, rent: web3_js_1.SYSVAR_RENT_PUBKEY, vaultProgram: vaultProgram.programId, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, systemProgram: web3_js_1.SystemProgram.programId, associatedTokenProgram: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, }) .postInstructions(createPoolPostInstructions) .transaction(); const ixs = []; if (preInstructions.length) { // Create 2 dynamic vault consume around 190k CU. Each create ATA + Wrap SOL around 23k const setComputeUnitLimitIx = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000, }); ixs.push([setComputeUnitLimitIx, ...preInstructions]); } // Create dynamic pool consume around 287k, create lock escrow + lock liquidity around 84k const setComputeUnitLimitIx = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 450_000, }); ixs.push([setComputeUnitLimitIx, createPermissionlessPoolTx]); if (opt?.swapLiquidity) { const protocolTokenFee = (0, utils_1.deriveProtocolTokenFee)(poolPubkey, tokenBMint, ammProgram.programId); const swapTx = await ammProgram.methods .swap(opt.swapLiquidity.inAmount, opt.swapLiquidity.minAmountOut) .accounts({ aTokenVault, bTokenVault, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, userSourceToken: payerTokenB, userDestinationToken: payerTokenA, user: payer, protocolTokenFee, pool: poolPubkey, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, vaultProgram: vaultProgram.programId, }) .transaction(); ixs.push(swapTx); } const resultTx = await (0, utils_1.createTransactions)(connection, ixs, payer); return resultTx; } static async createPermissionlessConstantProductMemecoinPoolWithConfig(connection, payer, tokenAMint, tokenBMint, tokenAAmount, tokenBAmount, config, memecoinInfo, opt) { const { vaultProgram, ammProgram } = (0, utils_1.createProgram)(connection, opt?.programId); const createTokenIxs = []; if (!memecoinInfo.isMinted) { if (!memecoinInfo.keypair || !memecoinInfo.payer || !memecoinInfo.mintAuthority || !memecoinInfo.mintAmount || !memecoinInfo.assetData || memecoinInfo.freezeAuthority === undefined) { throw new Error('Missing required fields for minting Memecoin.'); } const { tx: mintTx, mintAccount } = await (0, utils_1.createMint)(connection, memecoinInfo.keypair, memecoinInfo.payer, memecoinInfo.assetData, memecoinInfo.mintAuthority, memecoinInfo.freezeAuthority, memecoinInfo.decimals || 0, spl_token_1.TOKEN_PROGRAM_ID); createTokenIxs.push(...mintTx.instructions); const [ata, createAtaIx] = await (0, utils_1.getOrCreateATAInstruction)(mintAccount.publicKey, memecoinInfo.mintAuthority, connection, memecoinInfo.payer); createAtaIx && createTokenIxs.push(createAtaIx); const mintToIx = (0, spl_token_1.createMintToInstruction)(mintAccount.publicKey, ata, memecoinInfo.mintAuthority, BigInt(memecoinInfo.mintAmount.toString())); createTokenIxs.push(mintToIx); const revokeMintAuthorityIx = (0, spl_token_1.createSetAuthorityInstruction)(mintAccount.publicKey, memecoinInfo.mintAuthority, 0, null); createTokenIxs.push(revokeMintAuthorityIx); } let preInstructions = [...createTokenIxs]; const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [(0, vault_sdk_1.getVaultPdas)(tokenAMint, vaultProgram.programId), (0, vault_sdk_1.getVaultPdas)(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = await Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; if (!aVaultAccount) { const createVaultAIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, tokenAMint); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, tokenBMint); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = (0, utils_1.derivePoolAddressWithConfig)(tokenAMint, tokenBMint, config, ammProgram.programId); const [lpMint] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const [[aVaultLp], [bVaultLp]] = [ web3_js_1.PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = await Promise.all([ (0, utils_1.getOrCreateATAInstruction)(tokenAMint, payer, connection), (0, utils_1.getOrCreateATAInstruction)(tokenBMint, payer, connection), ]); createPayerTokenAIx && !opt?.skipAAta && preInstructions.push(createPayerTokenAIx); createPayerTokenBIx && !opt?.skipBAta && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const payerPoolLp = (0, utils_1.getAssociatedTokenAccount)(lpMint, payer); if (tokenAMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenB, BigInt(tokenBAmount.add(opt?.swapLiquidity?.inAmount ?? new anchor_1.BN(0)).toString()))); } const [mintMetadata, _mintMetadataBump] = (0, utils_1.deriveMintMetadata)(lpMint); const lpAmount = (0, bn_sqrt_1.default)(tokenAAmount.mul(tokenBAmount)); const { userLockAmount, feeWrapperLockAmount } = (0, utils_1.calculateLockAmounts)(lpAmount, opt?.stakeLiquidity?.ratio); const createPoolPostInstructions = []; if (opt?.lockLiquidity && userLockAmount.gt(new anchor_1.BN(0))) { const [lockEscrowPK] = (0, utils_1.deriveLockEscrowPda)(poolPubkey, payer, ammProgram.programId); const createLockEscrowIx = await ammProgram.methods .createLockEscrow() .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, payer, systemProgram: web3_js_1.SystemProgram.programId, }) .instruction(); createPoolPostInstructions.push(createLockEscrowIx); const [escrowAta, createEscrowAtaIx] = await (0, utils_1.getOrCreateATAInstruction)(lpMint, lockEscrowPK, connection, payer); createEscrowAtaIx && createPoolPostInstructions.push(createEscrowAtaIx); const lockIx = await ammProgram.methods .lock(userLockAmount) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowAta, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .instruction(); createPoolPostInstructions.push(lockIx); } const createPermissionlessPoolTx = await ammProgram.methods .initializePermissionlessConstantProductPoolWithConfig(tokenAAmount, tokenBAmount) .accounts({ pool: poolPubkey, tokenAMint, tokenBMint, aVault, bVault, aVaultLpMint, bVaultLpMint, aVaultLp, bVaultLp, lpMint, payerTokenA, payerTokenB, protocolTokenAFee, protocolTokenBFee, payerPoolLp, aTokenVault, bTokenVault, mintMetadata, metadataProgram: constants_1.METAPLEX_PROGRAM, payer, config, rent: web3_js_1.SYSVAR_RENT_PUBKEY, vaultProgram: vaultProgram.programId, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, systemProgram: web3_js_1.SystemProgram.programId, associatedTokenProgram: spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, }) .postInstructions(createPoolPostInstructions) .transaction(); const ixs = []; if (preInstructions.length) { // Create 2 dynamic vault consume around 190k CU. Each create ATA + Wrap SOL around 23k const setComputeUnitLimitIx = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000, }); ixs.push([setComputeUnitLimitIx, ...preInstructions]); } // Create dynamic pool consume around 287k, create lock escrow + lock liquidity around 84k const setComputeUnitLimitIx = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 450_000, }); ixs.push([setComputeUnitLimitIx, createPermissionlessPoolTx]); if (feeWrapperLockAmount.gt(new anchor_1.BN(0))) { const preInstructions = []; const initFeeVaultParams = opt?.feeVault ? { ...opt.feeVault, padding: new Array(64).fill(0) } : undefined; const createFeeVaultIxs = await m3m3_1.default.createFeeVaultInstructions(connection, poolPubkey, payer, tokenAMint, tokenBMint, initFeeVaultParams); preInstructions.push(...createFeeVaultIxs); const vaultKey = (0, m3m3_1.deriveFeeVault)(poolPubkey, m3m3_1.STAKE_FOR_FEE_PROGRAM_ID); const [lockEscrowFeeVaultPK] = (0, utils_1.deriveLockEscrowPda)(poolPubkey, vaultKey, ammProgram.programId); const [escrowFeeVaultAta, createEscrowFeeVaultAtaIx] = await (0, utils_1.getOrCreateATAInstruction)(lpMint, lockEscrowFeeVaultPK, connection, payer); createEscrowFeeVaultAtaIx && preInstructions.push(createEscrowFeeVaultAtaIx); const lockTx = await ammProgram.methods .lock(feeWrapperLockAmount) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowFeeVaultPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowFeeVaultAta, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .preInstructions(preInstructions) .transaction(); ixs.push(lockTx); } if (opt?.swapLiquidity) { const protocolTokenFee = (0, utils_1.deriveProtocolTokenFee)(poolPubkey, tokenBMint, ammProgram.programId); const swapTx = await ammProgram.methods .swap(opt.swapLiquidity.inAmount, opt.swapLiquidity.minAmountOut) .accounts({ aTokenVault, bTokenVault, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, userSourceToken: payerTokenB, userDestinationToken: payerTokenA, user: payer, protocolTokenFee, pool: poolPubkey, tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, vaultProgram: vaultProgram.programId, }) .transaction(); ixs.push(swapTx); } const resultTx = await (0, utils_1.createTransactions)(connection, ixs, payer); return resultTx; } static async createPermissionlessPool(connection, payer, tokenInfoA, tokenInfoB, tokenAAmount, tokenBAmount, isStable, tradeFeeBps, opt) { const { vaultProgram, ammProgram } = (0, utils_1.createProgram)(connection, opt?.programId); const curveType = (0, utils_1.generateCurveType)(tokenInfoA, tokenInfoB, isStable); const tokenAMint = new web3_js_1.PublicKey(tokenInfoA.address); const tokenBMint = new web3_js_1.PublicKey(tokenInfoB.address); const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [(0, vault_sdk_1.getVaultPdas)(tokenAMint, vaultProgram.programId), (0, vault_sdk_1.getVaultPdas)(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = await Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; let preInstructions = []; const setComputeUnitLimitIx = web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: 600_000, }); preInstructions.push(setComputeUnitLimitIx); if (!aVaultAccount) { const createVaultAIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, new web3_js_1.PublicKey(tokenInfoA.address)); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = await vault_sdk_1.default.createPermissionlessVaultInstruction(connection, payer, new web3_js_1.PublicKey(tokenInfoB.address)); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = (0, utils_1.derivePoolAddress)(connection, tokenInfoA, tokenInfoB, isStable, tradeFeeBps, { programId: opt?.programId, }); const [[aVaultLp], [bVaultLp]] = [ web3_js_1.PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = await Promise.all([ (0, utils_1.getOrCreateATAInstruction)(tokenAMint, payer, connection), (0, utils_1.getOrCreateATAInstruction)(tokenBMint, payer, connection), ]); if (!opt?.skipAta) { createPayerTokenAIx && preInstructions.push(createPayerTokenAIx); } createPayerTokenBIx && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [lpMint] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from(constants_1.SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const payerPoolLp = await (0, utils_1.getAssociatedTokenAccount)(lpMint, payer); if (tokenAMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(spl_token_1.NATIVE_MINT)) { preInstructions = preInstructions.concat((0, utils_1.wrapSOLInstruction)(payer, payerTokenB, BigInt(tokenBAmount.toString()))); } const [mintMetadata, _mintMetadataBump] = (0, utils_1.deriveMintMetadata)(lpMint); const createPermissionlessPoolTx = await ammProgram.methods .initializePermissionlessPoolWithFeeTier(curveType, tradeFeeBps, tokenAAmount, tokenBAmount) .accounts({ pool: poolPubkey, tokenAMint, tokenBMint, aVault, bVault, aVaultLpMint, bVaultLpMint, aVaultLp, bVaultLp, lpMint, payerTokenA, payerTokenB, protocolTokenAFee, protocolTokenBFee, payerPoolLp, aTokenVault, bTokenVault, mintMetadata, metadataProgram: constants_1.METAPLEX_PROGRAM, feeOwner: constants_1.FEE_OWNER, payer,