UNPKG

@mercurial-finance/dynamic-amm-sdk

Version:

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

866 lines 112 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { BN } from '@coral-xyz/anchor'; import { PublicKey, Transaction, SYSVAR_CLOCK_PUBKEY, SYSVAR_RENT_PUBKEY, SystemProgram, ComputeBudgetProgram, } from '@solana/web3.js'; import { AccountLayout, ASSOCIATED_TOKEN_PROGRAM_ID, createMintToInstruction, createSetAuthorityInstruction, MintLayout, NATIVE_MINT, TOKEN_PROGRAM_ID, } from '@solana/spl-token'; import VaultImpl, { calculateWithdrawableAmount, getVaultPdas } from '@mercurial-finance/vault-sdk'; import StakeForFee, { deriveFeeVault, STAKE_FOR_FEE_PROGRAM_ID } from '@meteora-ag/m3m3'; import invariant from 'invariant'; import { AccountType, ClockLayout, } from './types'; import { ERROR, SEEDS, UNLOCK_AMOUNT_BUFFER, FEE_OWNER, METAPLEX_PROGRAM, U64_MAX } from './constants'; import { StableSwap, TradeDirection } from './curve'; import { ConstantProductSwap } from './curve/constant-product'; import { calculateMaxSwapOutAmount, calculateSwapQuote, computeActualDepositAmount, calculatePoolInfo, getMaxAmountWithSlippage, getMinAmountWithSlippage, getOrCreateATAInstruction, unwrapSOLInstruction, wrapSOLInstruction, getDepegAccounts, createProgram, getAssociatedTokenAccount, deserializeAccount, chunkedGetMultipleAccountInfos, generateCurveType, derivePoolAddress, chunkedFetchMultiplePoolAccount, deriveMintMetadata, deriveLockEscrowPda, calculateUnclaimedLockEscrowFee, derivePoolAddressWithConfig as deriveConstantProductPoolAddressWithConfig, deriveConfigPda, deriveProtocolTokenFee, createMint, calculateLockAmounts, createTransactions, deriveCustomizablePermissionlessConstantProductPoolAddress, } from './utils'; import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes'; import sqrt from 'bn-sqrt'; const getAllPoolState = (poolMints, program) => __awaiter(void 0, void 0, void 0, function* () { const poolStates = (yield chunkedFetchMultiplePoolAccount(program, poolMints)); invariant(poolStates.length === poolMints.length, 'Some of the pool state not found'); const poolLpMints = poolStates.map((poolState) => poolState.lpMint); const lpMintAccounts = yield chunkedGetMultipleAccountInfos(program.provider.connection, poolLpMints); return poolStates.map((poolState, idx) => { const lpMintAccount = lpMintAccounts[idx]; invariant(lpMintAccount, ERROR.INVALID_ACCOUNT); const lpSupply = new BN(MintLayout.decode(lpMintAccount.data).supply.toString()); return Object.assign(Object.assign({}, poolState), { lpSupply }); }); }); const getPoolState = (poolMint, program) => __awaiter(void 0, void 0, void 0, function* () { const poolState = (yield program.account.pool.fetchNullable(poolMint)); invariant(poolState, `Pool ${poolMint.toBase58()} not found`); const account = yield program.provider.connection.getTokenSupply(poolState.lpMint); invariant(account.value.amount, ERROR.INVALID_ACCOUNT); return Object.assign(Object.assign({}, poolState), { lpSupply: new BN(account.value.amount) }); }); const getFeeVaultState = (publicKey, program) => __awaiter(void 0, void 0, void 0, function* () { const feeVaultState = yield program.account.feeVault.fetchNullable(publicKey); return feeVaultState; }); const decodeAccountTypeMapper = (type) => { const decoder = { [AccountType.VAULT_A_RESERVE]: (accountData) => new BN(AccountLayout.decode(accountData).amount.toString()), [AccountType.VAULT_B_RESERVE]: (accountData) => new BN(AccountLayout.decode(accountData).amount.toString()), [AccountType.VAULT_A_LP]: (accountData) => new BN(MintLayout.decode(accountData).supply.toString()), [AccountType.VAULT_B_LP]: (accountData) => new BN(MintLayout.decode(accountData).supply.toString()), [AccountType.POOL_VAULT_A_LP]: (accountData) => new BN(AccountLayout.decode(accountData).amount.toString()), [AccountType.POOL_VAULT_B_LP]: (accountData) => new BN(AccountLayout.decode(accountData).amount.toString()), [AccountType.POOL_LP_MINT]: (accountData) => new BN(MintLayout.decode(accountData).supply.toString()), [AccountType.SYSVAR_CLOCK]: (accountData) => new BN(accountData.readBigInt64LE(32).toString()), }; return decoder[type]; }; const getAccountsBuffer = (connection, accountsToFetch) => __awaiter(void 0, void 0, void 0, function* () { const accounts = yield 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()); }; export default class AmmImpl { 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 = { cluster: 'mainnet-beta', }; this.opt = Object.assign(Object.assign({}, this.opt), opt); } static createConfig(connection, payer, tradeFeeBps, protocolFeeBps, vaultConfigKey, activationDuration, poolCreatorAuthority, activationType, partnerFeeNumerator, opt) { return __awaiter(this, void 0, void 0, function* () { const { ammProgram } = createProgram(connection, opt === null || opt === void 0 ? void 0 : opt.programId); const configs = yield this.getFeeConfigurations(connection, opt); let index = 0; while (true) { const configPda = deriveConfigPda(new BN(index), ammProgram.programId); if (!configs.find((c) => c.publicKey.equals(configPda))) { const createConfigTx = yield ammProgram.methods .createConfig({ // Default fee denominator is 100_000 tradeFeeNumerator: tradeFeeBps.mul(new BN(10)), protocolTradeFeeNumerator: protocolFeeBps.mul(new BN(10)), vaultConfigKey, activationDuration, poolCreatorAuthority, index: new BN(index), activationType, partnerFeeNumerator, }) .accounts({ config: configPda, systemProgram: SystemProgram.programId, admin: payer, }) .transaction(); return new Transaction(Object.assign({ feePayer: payer }, (yield ammProgram.provider.connection.getLatestBlockhash(ammProgram.provider.connection.commitment)))).add(createConfigTx); } else { index++; } } }); } static searchPoolsByToken(connection, tokenMint) { return __awaiter(this, void 0, void 0, function* () { const { ammProgram } = createProgram(connection); const [poolsForTokenAMint, poolsForTokenBMint] = yield 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]; }); } partnerClaimFees(partnerAddress, maxAmountA, maxAmountB) { return __awaiter(this, void 0, void 0, function* () { let preInstructions = []; const [[partnerTokenA, createPartnerTokenAIx], [partnerTokenB, createPartnerTokenBIx]] = yield this.createATAPreInstructions(partnerAddress, [this.poolState.tokenAMint, this.poolState.tokenBMint]); createPartnerTokenAIx && preInstructions.push(createPartnerTokenAIx); createPartnerTokenBIx && preInstructions.push(createPartnerTokenBIx); const claimTx = yield 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: TOKEN_PROGRAM_ID, partnerAuthority: partnerAddress, }) .preInstructions(preInstructions) .transaction(); return new Transaction(Object.assign({ feePayer: partnerAddress }, (yield this.program.provider.connection.getLatestBlockhash(this.program.provider.connection.commitment)))).add(claimTx); }); } static createCustomizablePermissionlessConstantProductPool(connection, payer, tokenAMint, tokenBMint, tokenAAmount, tokenBAmount, customizableParams, opt) { return __awaiter(this, void 0, void 0, function* () { const { vaultProgram, ammProgram } = createProgram(connection, opt === null || opt === void 0 ? void 0 : opt.programId); const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [getVaultPdas(tokenAMint, vaultProgram.programId), getVaultPdas(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = yield Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; let preInstructions = []; if (!aVaultAccount) { const createVaultAIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, tokenAMint); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, tokenBMint); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = deriveCustomizablePermissionlessConstantProductPoolAddress(tokenAMint, tokenBMint, ammProgram.programId); const [lpMint] = PublicKey.findProgramAddressSync([Buffer.from(SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const [[aVaultLp], [bVaultLp]] = [ PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = yield Promise.all([ getOrCreateATAInstruction(tokenAMint, payer, connection), getOrCreateATAInstruction(tokenBMint, payer, connection), ]); createPayerTokenAIx && preInstructions.push(createPayerTokenAIx); createPayerTokenBIx && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ PublicKey.findProgramAddressSync([Buffer.from(SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([Buffer.from(SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const payerPoolLp = yield getAssociatedTokenAccount(lpMint, payer); if (tokenAMint.equals(NATIVE_MINT)) { preInstructions = preInstructions.concat(wrapSOLInstruction(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(NATIVE_MINT)) { preInstructions = preInstructions.concat(wrapSOLInstruction(payer, payerTokenB, BigInt(tokenBAmount.toString()))); } const [mintMetadata, _mintMetadataBump] = deriveMintMetadata(lpMint); const createPermissionlessPoolTx = yield 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: METAPLEX_PROGRAM, payer, rent: SYSVAR_RENT_PUBKEY, vaultProgram: vaultProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, }) .preInstructions(preInstructions) .transaction(); const latestBlockHash = yield ammProgram.provider.connection.getLatestBlockhash(ammProgram.provider.connection.commitment); return new Transaction(Object.assign({ feePayer: payer }, latestBlockHash)).add(createPermissionlessPoolTx); }); } static createPermissionlessConstantProductPoolWithConfig2(connection, payer, tokenAMint, tokenBMint, tokenAAmount, tokenBAmount, config, opt) { return __awaiter(this, void 0, void 0, function* () { var _a; const { vaultProgram, ammProgram } = createProgram(connection, opt === null || opt === void 0 ? void 0 : opt.programId); const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [getVaultPdas(tokenAMint, vaultProgram.programId), getVaultPdas(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = yield Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; let preInstructions = []; if (!aVaultAccount) { const createVaultAIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, tokenAMint); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, tokenBMint); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = deriveConstantProductPoolAddressWithConfig(tokenAMint, tokenBMint, config, ammProgram.programId); const [lpMint] = PublicKey.findProgramAddressSync([Buffer.from(SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const [[aVaultLp], [bVaultLp]] = [ PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = yield Promise.all([ getOrCreateATAInstruction(tokenAMint, payer, connection), getOrCreateATAInstruction(tokenBMint, payer, connection), ]); createPayerTokenAIx && preInstructions.push(createPayerTokenAIx); createPayerTokenBIx && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ PublicKey.findProgramAddressSync([Buffer.from(SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([Buffer.from(SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const payerPoolLp = yield getAssociatedTokenAccount(lpMint, payer); if (tokenAMint.equals(NATIVE_MINT)) { preInstructions = preInstructions.concat(wrapSOLInstruction(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(NATIVE_MINT)) { preInstructions = preInstructions.concat(wrapSOLInstruction(payer, payerTokenB, BigInt(tokenBAmount.toString()))); } const [mintMetadata, _mintMetadataBump] = deriveMintMetadata(lpMint); const activationPoint = (opt === null || opt === void 0 ? void 0 : opt.activationPoint) || null; const lpAmount = sqrt(tokenAAmount.mul(tokenBAmount)); const { userLockAmount, feeWrapperLockAmount } = calculateLockAmounts(lpAmount, (_a = opt === null || opt === void 0 ? void 0 : opt.stakeLiquidity) === null || _a === void 0 ? void 0 : _a.ratio); const createPoolPostInstructions = []; if ((opt === null || opt === void 0 ? void 0 : opt.lockLiquidity) && userLockAmount.gt(new BN(0))) { const [lockEscrowPK] = deriveLockEscrowPda(poolPubkey, payer, ammProgram.programId); const createLockEscrowIx = yield ammProgram.methods .createLockEscrow() .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, payer, systemProgram: SystemProgram.programId, }) .instruction(); createPoolPostInstructions.push(createLockEscrowIx); const [escrowAta, createEscrowAtaIx] = yield getOrCreateATAInstruction(lpMint, lockEscrowPK, connection, payer); createEscrowAtaIx && createPoolPostInstructions.push(createEscrowAtaIx); const lockIx = yield ammProgram.methods .lock(userLockAmount) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowAta, tokenProgram: TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .instruction(); createPoolPostInstructions.push(lockIx); } const createPermissionlessPoolTx = yield 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: METAPLEX_PROGRAM, payer, config, rent: SYSVAR_RENT_PUBKEY, vaultProgram: vaultProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, associatedTokenProgram: 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 = ComputeBudgetProgram.setComputeUnitLimit({ units: 300000, }); 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 = ComputeBudgetProgram.setComputeUnitLimit({ units: 450000, }); ixs.push([setComputeUnitLimitIx, createPermissionlessPoolTx]); if (feeWrapperLockAmount.gt(new BN(0))) { const preInstructions = []; const createFeeVaultIxs = yield StakeForFee.createFeeVaultInstructions(connection, poolPubkey, payer, tokenAMint, tokenBMint); preInstructions.push(...createFeeVaultIxs); const vaultKey = deriveFeeVault(poolPubkey, STAKE_FOR_FEE_PROGRAM_ID); const [lockEscrowFeeVaultPK] = deriveLockEscrowPda(poolPubkey, vaultKey, ammProgram.programId); const [escrowFeeVaultAta, createEscrowFeeVaultAtaIx] = yield getOrCreateATAInstruction(lpMint, lockEscrowFeeVaultPK, connection, payer); createEscrowFeeVaultAtaIx && preInstructions.push(createEscrowFeeVaultAtaIx); const lockTx = yield ammProgram.methods .lock(feeWrapperLockAmount) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowFeeVaultPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowFeeVaultAta, tokenProgram: TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .preInstructions(preInstructions) .transaction(); ixs.push(lockTx); } if (opt === null || opt === void 0 ? void 0 : opt.swapLiquidity) { const protocolTokenFee = deriveProtocolTokenFee(poolPubkey, tokenBMint, ammProgram.programId); const swapTx = yield 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: TOKEN_PROGRAM_ID, vaultProgram: vaultProgram.programId, }) .transaction(); ixs.push(swapTx); } const resultTx = yield createTransactions(connection, ixs, payer); return resultTx; }); } static createPermissionlessConstantProductPoolWithConfig(connection, payer, tokenAMint, tokenBMint, tokenAAmount, tokenBAmount, config, opt) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; const { vaultProgram, ammProgram } = createProgram(connection, opt === null || opt === void 0 ? void 0 : opt.programId); const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [getVaultPdas(tokenAMint, vaultProgram.programId), getVaultPdas(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = yield Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; let preInstructions = []; if (!aVaultAccount) { const createVaultAIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, tokenAMint); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, tokenBMint); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = deriveConstantProductPoolAddressWithConfig(tokenAMint, tokenBMint, config, ammProgram.programId); const [lpMint] = PublicKey.findProgramAddressSync([Buffer.from(SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const [[aVaultLp], [bVaultLp]] = [ PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = yield Promise.all([ getOrCreateATAInstruction(tokenAMint, payer, connection), getOrCreateATAInstruction(tokenBMint, payer, connection), ]); createPayerTokenAIx && !(opt === null || opt === void 0 ? void 0 : opt.skipAAta) && preInstructions.push(createPayerTokenAIx); createPayerTokenBIx && !(opt === null || opt === void 0 ? void 0 : opt.skipBAta) && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ PublicKey.findProgramAddressSync([Buffer.from(SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([Buffer.from(SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const payerPoolLp = getAssociatedTokenAccount(lpMint, payer); if (tokenAMint.equals(NATIVE_MINT)) { preInstructions = preInstructions.concat(wrapSOLInstruction(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(NATIVE_MINT)) { preInstructions = preInstructions.concat(wrapSOLInstruction(payer, payerTokenB, BigInt(tokenBAmount.add((_b = (_a = opt === null || opt === void 0 ? void 0 : opt.swapLiquidity) === null || _a === void 0 ? void 0 : _a.inAmount) !== null && _b !== void 0 ? _b : new BN(0)).toString()))); } const [mintMetadata, _mintMetadataBump] = deriveMintMetadata(lpMint); const createPoolPostInstructions = []; if (opt === null || opt === void 0 ? void 0 : opt.lockLiquidity) { const [lockEscrowPK] = deriveLockEscrowPda(poolPubkey, payer, ammProgram.programId); const createLockEscrowIx = yield ammProgram.methods .createLockEscrow() .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, payer, systemProgram: SystemProgram.programId, }) .instruction(); createPoolPostInstructions.push(createLockEscrowIx); const [escrowAta, createEscrowAtaIx] = yield getOrCreateATAInstruction(lpMint, lockEscrowPK, connection, payer); createEscrowAtaIx && createPoolPostInstructions.push(createEscrowAtaIx); const lockIx = yield ammProgram.methods .lock(U64_MAX) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowAta, tokenProgram: TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .instruction(); createPoolPostInstructions.push(lockIx); } const createPermissionlessPoolTx = yield 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: METAPLEX_PROGRAM, payer, config, rent: SYSVAR_RENT_PUBKEY, vaultProgram: vaultProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, associatedTokenProgram: 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 = ComputeBudgetProgram.setComputeUnitLimit({ units: 300000, }); ixs.push([setComputeUnitLimitIx, ...preInstructions]); } // Create dynamic pool consume around 287k, create lock escrow + lock liquidity around 84k const setComputeUnitLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 450000, }); ixs.push([setComputeUnitLimitIx, createPermissionlessPoolTx]); if (opt === null || opt === void 0 ? void 0 : opt.swapLiquidity) { const protocolTokenFee = deriveProtocolTokenFee(poolPubkey, tokenBMint, ammProgram.programId); const swapTx = yield 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: TOKEN_PROGRAM_ID, vaultProgram: vaultProgram.programId, }) .transaction(); ixs.push(swapTx); } const resultTx = yield createTransactions(connection, ixs, payer); return resultTx; }); } static createPermissionlessConstantProductMemecoinPoolWithConfig(connection, payer, tokenAMint, tokenBMint, tokenAAmount, tokenBAmount, config, memecoinInfo, opt) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const { vaultProgram, ammProgram } = createProgram(connection, opt === null || opt === void 0 ? void 0 : 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 } = yield createMint(connection, memecoinInfo.keypair, memecoinInfo.payer, memecoinInfo.assetData, memecoinInfo.mintAuthority, memecoinInfo.freezeAuthority, memecoinInfo.decimals || 0, TOKEN_PROGRAM_ID); createTokenIxs.push(...mintTx.instructions); const [ata, createAtaIx] = yield getOrCreateATAInstruction(mintAccount.publicKey, memecoinInfo.mintAuthority, connection, memecoinInfo.payer); createAtaIx && createTokenIxs.push(createAtaIx); const mintToIx = createMintToInstruction(mintAccount.publicKey, ata, memecoinInfo.mintAuthority, BigInt(memecoinInfo.mintAmount.toString())); createTokenIxs.push(mintToIx); const revokeMintAuthorityIx = 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 },] = [getVaultPdas(tokenAMint, vaultProgram.programId), getVaultPdas(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = yield Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; if (!aVaultAccount) { const createVaultAIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, tokenAMint); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, tokenBMint); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = deriveConstantProductPoolAddressWithConfig(tokenAMint, tokenBMint, config, ammProgram.programId); const [lpMint] = PublicKey.findProgramAddressSync([Buffer.from(SEEDS.LP_MINT), poolPubkey.toBuffer()], ammProgram.programId); const [[aVaultLp], [bVaultLp]] = [ PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB, createPayerTokenBIx]] = yield Promise.all([ getOrCreateATAInstruction(tokenAMint, payer, connection), getOrCreateATAInstruction(tokenBMint, payer, connection), ]); createPayerTokenAIx && !(opt === null || opt === void 0 ? void 0 : opt.skipAAta) && preInstructions.push(createPayerTokenAIx); createPayerTokenBIx && !(opt === null || opt === void 0 ? void 0 : opt.skipBAta) && preInstructions.push(createPayerTokenBIx); const [[protocolTokenAFee], [protocolTokenBFee]] = [ PublicKey.findProgramAddressSync([Buffer.from(SEEDS.FEE), tokenAMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([Buffer.from(SEEDS.FEE), tokenBMint.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const payerPoolLp = getAssociatedTokenAccount(lpMint, payer); if (tokenAMint.equals(NATIVE_MINT)) { preInstructions = preInstructions.concat(wrapSOLInstruction(payer, payerTokenA, BigInt(tokenAAmount.toString()))); } if (tokenBMint.equals(NATIVE_MINT)) { preInstructions = preInstructions.concat(wrapSOLInstruction(payer, payerTokenB, BigInt(tokenBAmount.add((_b = (_a = opt === null || opt === void 0 ? void 0 : opt.swapLiquidity) === null || _a === void 0 ? void 0 : _a.inAmount) !== null && _b !== void 0 ? _b : new BN(0)).toString()))); } const [mintMetadata, _mintMetadataBump] = deriveMintMetadata(lpMint); const lpAmount = sqrt(tokenAAmount.mul(tokenBAmount)); const { userLockAmount, feeWrapperLockAmount } = calculateLockAmounts(lpAmount, (_c = opt === null || opt === void 0 ? void 0 : opt.stakeLiquidity) === null || _c === void 0 ? void 0 : _c.ratio); const createPoolPostInstructions = []; if ((opt === null || opt === void 0 ? void 0 : opt.lockLiquidity) && userLockAmount.gt(new BN(0))) { const [lockEscrowPK] = deriveLockEscrowPda(poolPubkey, payer, ammProgram.programId); const createLockEscrowIx = yield ammProgram.methods .createLockEscrow() .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, payer, systemProgram: SystemProgram.programId, }) .instruction(); createPoolPostInstructions.push(createLockEscrowIx); const [escrowAta, createEscrowAtaIx] = yield getOrCreateATAInstruction(lpMint, lockEscrowPK, connection, payer); createEscrowAtaIx && createPoolPostInstructions.push(createEscrowAtaIx); const lockIx = yield ammProgram.methods .lock(userLockAmount) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowAta, tokenProgram: TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .instruction(); createPoolPostInstructions.push(lockIx); } const createPermissionlessPoolTx = yield 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: METAPLEX_PROGRAM, payer, config, rent: SYSVAR_RENT_PUBKEY, vaultProgram: vaultProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, associatedTokenProgram: 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 = ComputeBudgetProgram.setComputeUnitLimit({ units: 300000, }); ixs.push([setComputeUnitLimitIx, ...preInstructions]); } // Create dynamic pool consume around 287k, create lock escrow + lock liquidity around 84k const setComputeUnitLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 450000, }); ixs.push([setComputeUnitLimitIx, createPermissionlessPoolTx]); if (feeWrapperLockAmount.gt(new BN(0))) { const preInstructions = []; const initFeeVaultParams = (opt === null || opt === void 0 ? void 0 : opt.feeVault) ? Object.assign(Object.assign({}, opt.feeVault), { padding: new Array(64).fill(0) }) : undefined; const createFeeVaultIxs = yield StakeForFee.createFeeVaultInstructions(connection, poolPubkey, payer, tokenAMint, tokenBMint, initFeeVaultParams); preInstructions.push(...createFeeVaultIxs); const vaultKey = deriveFeeVault(poolPubkey, STAKE_FOR_FEE_PROGRAM_ID); const [lockEscrowFeeVaultPK] = deriveLockEscrowPda(poolPubkey, vaultKey, ammProgram.programId); const [escrowFeeVaultAta, createEscrowFeeVaultAtaIx] = yield getOrCreateATAInstruction(lpMint, lockEscrowFeeVaultPK, connection, payer); createEscrowFeeVaultAtaIx && preInstructions.push(createEscrowFeeVaultAtaIx); const lockTx = yield ammProgram.methods .lock(feeWrapperLockAmount) .accounts({ pool: poolPubkey, lockEscrow: lockEscrowFeeVaultPK, owner: payer, lpMint, sourceTokens: payerPoolLp, escrowVault: escrowFeeVaultAta, tokenProgram: TOKEN_PROGRAM_ID, aVault, bVault, aVaultLp, bVaultLp, aVaultLpMint, bVaultLpMint, }) .preInstructions(preInstructions) .transaction(); ixs.push(lockTx); } if (opt === null || opt === void 0 ? void 0 : opt.swapLiquidity) { const protocolTokenFee = deriveProtocolTokenFee(poolPubkey, tokenBMint, ammProgram.programId); const swapTx = yield 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: TOKEN_PROGRAM_ID, vaultProgram: vaultProgram.programId, }) .transaction(); ixs.push(swapTx); } const resultTx = yield createTransactions(connection, ixs, payer); return resultTx; }); } static createPermissionlessPool(connection, payer, tokenInfoA, tokenInfoB, tokenAAmount, tokenBAmount, isStable, tradeFeeBps, opt) { return __awaiter(this, void 0, void 0, function* () { const { vaultProgram, ammProgram } = createProgram(connection, opt === null || opt === void 0 ? void 0 : opt.programId); const curveType = generateCurveType(tokenInfoA, tokenInfoB, isStable); const tokenAMint = new PublicKey(tokenInfoA.address); const tokenBMint = new PublicKey(tokenInfoB.address); const [{ vaultPda: aVault, tokenVaultPda: aTokenVault, lpMintPda: aLpMintPda }, { vaultPda: bVault, tokenVaultPda: bTokenVault, lpMintPda: bLpMintPda },] = [getVaultPdas(tokenAMint, vaultProgram.programId), getVaultPdas(tokenBMint, vaultProgram.programId)]; const [aVaultAccount, bVaultAccount] = yield Promise.all([ vaultProgram.account.vault.fetchNullable(aVault), vaultProgram.account.vault.fetchNullable(bVault), ]); let aVaultLpMint = aLpMintPda; let bVaultLpMint = bLpMintPda; let preInstructions = []; const setComputeUnitLimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 600000, }); preInstructions.push(setComputeUnitLimitIx); if (!aVaultAccount) { const createVaultAIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, new PublicKey(tokenInfoA.address)); createVaultAIx && preInstructions.push(createVaultAIx); } else { aVaultLpMint = aVaultAccount.lpMint; // Old vault doesn't have lp mint pda } if (!bVaultAccount) { const createVaultBIx = yield VaultImpl.createPermissionlessVaultInstruction(connection, payer, new PublicKey(tokenInfoB.address)); createVaultBIx && preInstructions.push(createVaultBIx); } else { bVaultLpMint = bVaultAccount.lpMint; // Old vault doesn't have lp mint pda } const poolPubkey = derivePoolAddress(connection, tokenInfoA, tokenInfoB, isStable, tradeFeeBps, { programId: opt === null || opt === void 0 ? void 0 : opt.programId, }); const [[aVaultLp], [bVaultLp]] = [ PublicKey.findProgramAddressSync([aVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), PublicKey.findProgramAddressSync([bVault.toBuffer(), poolPubkey.toBuffer()], ammProgram.programId), ]; const [[payerTokenA, createPayerTokenAIx], [payerTokenB