diff --git a/.github/actions/setup-anchor/action.yaml b/.github/actions/setup-anchor/action.yaml new file mode 100644 index 0000000..3d07935 --- /dev/null +++ b/.github/actions/setup-anchor/action.yaml @@ -0,0 +1,10 @@ +name: "Setup anchor-cli" +description: "Setup node js and anchor cli" +runs: + using: "composite" + steps: + - uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + - run: npm install -g @coral-xyz/anchor-cli@${{ env.ANCHOR_CLI_VERSION }} yarn + shell: bash diff --git a/.github/actions/setup-dep/action.yaml b/.github/actions/setup-dep/action.yaml new file mode 100644 index 0000000..fa998c5 --- /dev/null +++ b/.github/actions/setup-dep/action.yaml @@ -0,0 +1,7 @@ +name: "Setup" +description: "Setup program dependencies" +runs: + using: "composite" + steps: + - run: sudo apt-get update && sudo apt-get install -y pkg-config build-essential libudev-dev + shell: bash diff --git a/.github/actions/setup-solana/action.yaml b/.github/actions/setup-solana/action.yaml new file mode 100644 index 0000000..a0247ec --- /dev/null +++ b/.github/actions/setup-solana/action.yaml @@ -0,0 +1,21 @@ +name: "Setup Solana" +description: "Setup Solana" +runs: + using: "composite" + steps: + - uses: actions/cache@v2 + name: Cache Solana Tool Suite + id: cache-solana + with: + path: | + ~/.cache/solana/ + ~/.local/share/solana/ + key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_CLI_VERSION }} + - run: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)" + shell: bash + - run: echo "$HOME/.local/share/solana/install/active_release/bin/" >> $GITHUB_PATH + shell: bash + - run: solana-keygen new --no-bip39-passphrase + shell: bash + - run: solana config set --url localhost + shell: bash diff --git a/.github/workflows/ci_ts.yaml b/.github/workflows/ci_ts.yaml new file mode 100644 index 0000000..5cfc5c4 --- /dev/null +++ b/.github/workflows/ci_ts.yaml @@ -0,0 +1,49 @@ +name: Typescript SDK CI + +on: + pull_request: + paths-ignore: + - "config/**" + - "README.md" + - "LICENSE" + - ".editorconfig" + branches: + - main + +env: + SOLANA_CLI_VERSION: 1.16.3 + NODE_VERSION: 18.14.2 + ANCHOR_CLI_VERSION: 0.28.0 + +jobs: + sdk_changed_files: + runs-on: ubuntu-latest + outputs: + sdk: ${{steps.changed-files-specific.outputs.any_changed}} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Get specific changed files + id: changed-files-specific + uses: tj-actions/changed-files@v18.6 + with: + files: | + src + + sdk_test: + runs-on: ubuntu-latest + needs: sdk_changed_files + if: needs.sdk_changed_files.outputs.sdk == 'true' + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-solana + - uses: ./.github/actions/setup-dep + - uses: oven-sh/setup-bun@v2 + # This much more faster than anchor localnet + - run: bun run start-test-validator & sleep 2 + shell: bash + - run: bun install + shell: bash + - run: bun test + shell: bash \ No newline at end of file diff --git a/README.md b/README.md index a40188e..f4580bf 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,14 @@ Also we need to provide the keypair for the payer wallet in `keypair.json` file. - `escrowFee`: Fee to create stake escrow account. - `whitelistMode`: `permissionless` or `permission_with_merkle_proof` or `permission_with_authority`. +## Testings +First, run the localnet +```bash +bun run start-test-validator +``` + +Then run the test: `bun test` + ## Run the scripts Run the script with config file specified in the CLI, some examples: diff --git a/bun.lockb b/bun.lockb index 4d73b19..65cabfc 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/config/lfg_seed_liquidity.json b/config/seed_liquidity_lfg.json similarity index 53% rename from config/lfg_seed_liquidity.json rename to config/seed_liquidity_lfg.json index 828b4d3..1767664 100644 --- a/config/lfg_seed_liquidity.json +++ b/config/seed_liquidity_lfg.json @@ -8,10 +8,12 @@ "lfgSeedLiquidity": { "minPrice": 0.003393, "maxPrice": 0.004393, - "binStep": 100, "curvature": 0.6, - "seedAmount": "1", - "basePositionKey": "ETp2wTykSe3iGYzPCn6upP8NVB7a1pe6WTZidGNJH7KC", - "basePositionKeypairFilepath": "keypair.json" + "basePositionKeypairFilepath": "keypair.json", + "operatorKeypairFilepath": "keypair.json", + "positionOwner": "2vqFyczcehkR7hTeZjEkiN5jvYnFXuFRinovqR2gakSb", + "feeOwner": "2vqFyczcehkR7hTeZjEkiN5jvYnFXuFRinovqR2gakSb", + "lockReleasePoint": 0, + "seedTokenXToPositionOwner": true } } \ No newline at end of file diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..efbb547 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +const TIMEOUT_SEC = 1000; + +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + transform: { + '^.+\\.ts?$': 'ts-jest', + }, + transformIgnorePatterns: ['/node_modules/'], + testTimeout: TIMEOUT_SEC * 90, +}; \ No newline at end of file diff --git a/package.json b/package.json index b6e4ad7..2abb304 100644 --- a/package.json +++ b/package.json @@ -4,18 +4,20 @@ "main": "index.js", "scripts": { "format": "bun prettier ./src --write", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "bun test", "create-dynamic-amm-pool": "bun run src/create_pool.ts --config ./config/create_dynamic_amm_pool.json", - "create-dlmm-pool": "bun run src/create_pool.ts --config ./config/create_dlmm_pool.json" + "create-dlmm-pool": "bun run src/create_pool.ts --config ./config/create_dlmm_pool.json", + "start-test-validator": "solana-test-validator --bind-address 0.0.0.0 --account-dir ./src/tests/artifacts/accounts --bpf-program LbVRzDTvBDEcrthxfZ4RL6yiq3uZw8bS6MwtdY6UhFQ ./src/tests/artifacts/lb_clmm.so --bpf-program Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB ./src/tests/artifacts/dynamic_amm.so --bpf-program 24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi ./src/tests/artifacts/dynamic_vault.so --bpf-program metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s ./src/tests/artifacts/metaplex.so --mint bossj3JvwiNK7pvjr149DqdtJxf2gdygbcmEPTkb2F1 --reset" }, "dependencies": { "@coral-xyz/anchor": "^0.28.0", "@mercurial-finance/dynamic-amm-sdk": "^1.1.19", "@meteora-ag/alpha-vault": "^1.1.6", - "@meteora-ag/dlmm": "^1.3.3", + "@meteora-ag/dlmm": "^1.3.5", "@solana/spl-token": "^0.4.9", "@solana/spl-token-registry": "^0.2.4574", "@solana/web3.js": "^1.95.8", + "@types/jest": "^29.5.14", "ajv": "^8.17.1", "bn.js": "^5.2.1", "decimal.js": "^10.4.3" @@ -25,6 +27,9 @@ "description": "", "devDependencies": { "@types/bun": "^1.1.14", - "prettier": "3.4.2" + "babar": "^0.2.3", + "jest": "^29.5.0", + "prettier": "3.4.2", + "ts-jest": "^29.1.1" } } \ No newline at end of file diff --git a/src/create_pool.ts b/src/create_pool.ts index 30681e1..82b0363 100644 --- a/src/create_pool.ts +++ b/src/create_pool.ts @@ -1,36 +1,17 @@ -import { - Connection, - PublicKey, - sendAndConfirmTransaction, -} from "@solana/web3.js"; +import { Connection, PublicKey } from "@solana/web3.js"; import { DEFAULT_COMMITMENT_LEVEL, MeteoraConfig, - getAmountInLamports, getQuoteMint, - getQuoteDecimals, safeParseKeypairFromFile, - runSimulateTransaction, - getDynamicAmmActivationType, - getDlmmActivationType, parseConfigFromCli, - modifyComputeUnitPriceIx, } from "."; -import { AmmImpl } from "@mercurial-finance/dynamic-amm-sdk"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; -import { BN } from "bn.js"; -import DLMM, { - LBCLMM_PROGRAM_IDS, - deriveCustomizablePermissionlessLbPair, -} from "@meteora-ag/dlmm"; -import Decimal from "decimal.js"; import { createTokenMint } from "./libs/create_token_mint"; -import { CustomizableParams } from "@mercurial-finance/dynamic-amm-sdk/dist/cjs/src/amm/types"; import { - deriveCustomizablePermissionlessConstantProductPoolAddress, - createProgram, -} from "@mercurial-finance/dynamic-amm-sdk/dist/cjs/src/amm/utils"; -import { getMint } from "@solana/spl-token"; + createPermissionlessDlmmPool, + createPermissionlessDynamicPool, +} from "./libs/create_pool_utils"; async function main() { let config: MeteoraConfig = parseConfigFromCli(); @@ -100,197 +81,4 @@ async function main() { } } -async function createPermissionlessDynamicPool( - config: MeteoraConfig, - connection: Connection, - wallet: Wallet, - baseMint: PublicKey, - quoteMint: PublicKey, -) { - if (!config.dynamicAmm) { - throw new Error("Missing dynamic amm configuration"); - } - console.log("\n> Initializing Permissionless Dynamic AMM pool..."); - - const quoteDecimals = getQuoteDecimals(config.quoteSymbol); - const baseMintAccount = await getMint(connection, baseMint); - const baseDecimals = baseMintAccount.decimals; - - const baseAmount = getAmountInLamports( - config.dynamicAmm.baseAmount, - baseDecimals, - ); - const quoteAmount = getAmountInLamports( - config.dynamicAmm.quoteAmount, - quoteDecimals, - ); - - console.log( - `- Using token A amount ${config.dynamicAmm.baseAmount}, in lamports = ${baseAmount}`, - ); - console.log( - `- Using token B amount ${config.dynamicAmm.quoteAmount}, in lamports = ${quoteAmount}`, - ); - - const activationType = getDynamicAmmActivationType( - config.dynamicAmm.activationType, - ); - - const customizeParam: CustomizableParams = { - tradeFeeNumerator: config.dynamicAmm.tradeFeeNumerator, - activationType: activationType, - activationPoint: config.dynamicAmm.activationPoint - ? new BN(config.dynamicAmm.activationPoint) - : null, - hasAlphaVault: config.dynamicAmm.hasAlphaVault, - padding: Array(90).fill(0), - }; - console.log( - `- Using tradeFeeNumerator = ${customizeParam.tradeFeeNumerator}`, - ); - console.log(`- Using activationType = ${config.dynamicAmm.activationType}`); - console.log(`- Using activationPoint = ${customizeParam.activationPoint}`); - console.log(`- Using hasAlphaVault = ${customizeParam.hasAlphaVault}`); - - const initPoolTx = - await AmmImpl.createCustomizablePermissionlessConstantProductPool( - connection, - wallet.publicKey, - baseMint, - quoteMint, - baseAmount, - quoteAmount, - customizeParam, - ); - modifyComputeUnitPriceIx(initPoolTx, config.computeUnitPriceMicroLamports); - const poolKey = deriveCustomizablePermissionlessConstantProductPoolAddress( - baseMint, - quoteMint, - createProgram(connection).ammProgram.programId, - ); - - console.log(`\n> Pool address: ${poolKey}`); - - if (config.dryRun) { - console.log(`> Simulating init pool tx...`); - await runSimulateTransaction(connection, [wallet.payer], wallet.publicKey, [ - initPoolTx, - ]); - } else { - console.log(`>> Sending init pool transaction...`); - const initPoolTxHash = await sendAndConfirmTransaction( - connection, - initPoolTx, - [wallet.payer], - ).catch((err) => { - console.error(err); - throw err; - }); - console.log( - `>>> Pool initialized successfully with tx hash: ${initPoolTxHash}`, - ); - } -} - -async function createPermissionlessDlmmPool( - config: MeteoraConfig, - connection: Connection, - wallet: Wallet, - baseMint: PublicKey, - quoteMint: PublicKey, -) { - if (!config.dlmm) { - throw new Error("Missing DLMM configuration"); - } - console.log("\n> Initializing Permissionless DLMM pool..."); - - const binStep = config.dlmm.binStep; - const feeBps = config.dlmm.feeBps; - const hasAlphaVault = config.dlmm.hasAlphaVault; - const activationPoint = config.dlmm.activationPoint - ? new BN(config.dlmm.activationPoint) - : null; - - const activationType = getDlmmActivationType(config.dlmm.activationType); - - console.log(`- Using binStep = ${binStep}`); - console.log(`- Using feeBps = ${feeBps}`); - console.log(`- Using initialPrice = ${config.dlmm.initialPrice}`); - console.log(`- Using activationType = ${config.dlmm.activationType}`); - console.log(`- Using activationPoint = ${activationPoint}`); - console.log(`- Using hasAlphaVault = ${hasAlphaVault}`); - - const quoteDecimals = getQuoteDecimals(config.quoteSymbol); - const baseMintAccount = await getMint(connection, baseMint); - const baseDecimals = baseMintAccount.decimals; - - const initPrice = DLMM.getPricePerLamport( - baseDecimals, - quoteDecimals, - config.dlmm.initialPrice, - ); - let selectiveRounding = false; - if (config.dlmm.priceRounding == "up") { - selectiveRounding = false; - } else if (config.dlmm.priceRounding == "down") { - selectiveRounding = true; - } else { - throw new Error( - `Unknown price rounding: ${config.dlmm.priceRounding}. Should be 'up' or 'down'`, - ); - } - - const activateBinId = DLMM.getBinIdFromPrice( - initPrice, - binStep, - selectiveRounding, - ); - - const initPoolTx = await DLMM.createCustomizablePermissionlessLbPair( - connection, - new BN(binStep), - baseMint, - quoteMint, - new BN(activateBinId.toString()), - new BN(feeBps), - activationType, - hasAlphaVault, - wallet.publicKey, - activationPoint, - { - cluster: "mainnet-beta", - }, - ); - modifyComputeUnitPriceIx(initPoolTx, config.computeUnitPriceMicroLamports); - - let poolKey: PublicKey; - [poolKey] = deriveCustomizablePermissionlessLbPair( - baseMint, - quoteMint, - new PublicKey(LBCLMM_PROGRAM_IDS["mainnet-beta"]), - ); - - console.log(`\n> Pool address: ${poolKey}`); - - if (config.dryRun) { - console.log(`\n> Simulating init pool tx...`); - await runSimulateTransaction(connection, [wallet.payer], wallet.publicKey, [ - initPoolTx, - ]); - } else { - console.log(`>> Sending init pool transaction...`); - let initPoolTxHash = await sendAndConfirmTransaction( - connection, - initPoolTx, - [wallet.payer], - ).catch((e) => { - console.error(e); - throw e; - }); - console.log( - `>>> Pool initialized successfully with tx hash: ${initPoolTxHash}`, - ); - } -} - main(); diff --git a/src/index.ts b/src/index.ts index 9fa2afc..9254308 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,6 @@ export * from "./libs/constants"; export * from "./libs/config"; export * from "./libs/utils"; export * from "./libs/create_token_mint"; +export * from "./libs/math"; +export * from "./libs/create_pool_utils"; +export * from "./libs/seed_liquidity_utils"; diff --git a/src/lfg_seed_liquidity.ts b/src/lfg_seed_liquidity.ts deleted file mode 100644 index 257abcb..0000000 --- a/src/lfg_seed_liquidity.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { - Connection, - Keypair, - PublicKey, - Transaction, - sendAndConfirmTransaction, -} from "@solana/web3.js"; -import { - DEFAULT_COMMITMENT_LEVEL, - MeteoraConfig, - getAmountInLamports, - getQuoteMint, - getQuoteDecimals, - safeParseKeypairFromFile, - runSimulateTransaction, - parseConfigFromCli, -} from "."; -import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; -import { BN } from "bn.js"; -import DLMM, { - LBCLMM_PROGRAM_IDS, - deriveCustomizablePermissionlessLbPair, - getBinArrayLowerUpperBinId, - getPriceOfBinByBinId, -} from "@meteora-ag/dlmm"; -import Decimal from "decimal.js"; -import { getMint } from "@solana/spl-token"; - -async function main() { - let config: MeteoraConfig = parseConfigFromCli(); - - console.log(`> Using keypair file path ${config.keypairFilePath}`); - let keypair = safeParseKeypairFromFile(config.keypairFilePath); - - console.log("\n> Initializing with general configuration..."); - console.log(`- Using RPC URL ${config.rpcUrl}`); - console.log(`- Dry run = ${config.dryRun}`); - console.log(`- Using payer ${keypair.publicKey} to execute commands`); - - const connection = new Connection(config.rpcUrl, DEFAULT_COMMITMENT_LEVEL); - const wallet = new Wallet(keypair); - const provider = new AnchorProvider(connection, wallet, { - commitment: connection.commitment, - }); - - if (!config.baseMint) { - throw new Error("Missing baseMint in configuration"); - } - const baseMint = new PublicKey(config.baseMint); - const baseMintAccount = await getMint(connection, baseMint); - const baseDecimals = baseMintAccount.decimals; - - let quoteMint = getQuoteMint(config.quoteSymbol); - const quoteDecimals = getQuoteDecimals(config.quoteSymbol); - - console.log(`- Using base token mint ${baseMint.toString()}`); - console.log(`- Using quote token mint ${quoteMint.toString()}`); - - let poolKey: PublicKey; - [poolKey] = deriveCustomizablePermissionlessLbPair( - baseMint, - quoteMint, - new PublicKey(LBCLMM_PROGRAM_IDS["mainnet-beta"]), - ); - console.log(`- Using pool key ${poolKey.toString()}`); - - if (!config.lfgSeedLiquidity) { - throw new Error(`Missing DLMM LFG seed liquidity in configuration`); - } - - const pair = await DLMM.create(connection, poolKey, { - cluster: "mainnet-beta", - }); - - const seedAmount = getAmountInLamports( - config.lfgSeedLiquidity.seedAmount, - baseDecimals, - ); - const curvature = config.lfgSeedLiquidity.curvature; - const minPrice = config.lfgSeedLiquidity.minPrice; - const maxPrice = config.lfgSeedLiquidity.maxPrice; - const basePositionKey = new PublicKey( - config.lfgSeedLiquidity.basePositionKey, - ); - const baseKeypair = safeParseKeypairFromFile( - config.lfgSeedLiquidity.basePositionKeypairFilepath, - ); - - const { initializeBinArraysAndPositionIxs, addLiquidityIxs } = - await pair.seedLiquidity( - wallet.publicKey, - seedAmount, - curvature, - minPrice, - maxPrice, - basePositionKey, - ); - - // Initialize all bin array and position, transaction order can be in sequence or not - { - const { blockhash, lastValidBlockHeight } = - await connection.getLatestBlockhash("confirmed"); - const transactions: Array> = []; - - for (const groupIx of initializeBinArraysAndPositionIxs) { - const tx = new Transaction({ - feePayer: keypair.publicKey, - blockhash, - lastValidBlockHeight, - }).add(...groupIx); - - const signers = [keypair, baseKeypair]; - - if (config.dryRun) { - console.log(`\n> Simulating initializeBinArraysAndPositionIxs tx...`); - await runSimulateTransaction( - connection, - [wallet.payer], - wallet.publicKey, - [tx], - ); - } else { - transactions.push(sendAndConfirmTransaction(connection, tx, signers)); - } - } - - await Promise.all(transactions) - .then((txs) => { - txs.map(console.log); - }) - .catch((e) => { - console.error(e); - throw e; - }); - } - - const beforeTokenXBalance = await connection - .getTokenAccountBalance(wallet.publicKey) - .then((i) => new BN(i.value.amount)); - - { - const { blockhash, lastValidBlockHeight } = - await connection.getLatestBlockhash("confirmed"); - - const transactions: Array> = []; - - // Deposit to positions created in above step. The add liquidity order can be in sequence or not. - for (const groupIx of addLiquidityIxs) { - const tx = new Transaction({ - feePayer: keypair.publicKey, - blockhash, - lastValidBlockHeight, - }).add(...groupIx); - - const signers = [keypair]; - - if (config.dryRun) { - console.log(`\n> Simulating addLiquidityIxs tx...`); - await runSimulateTransaction( - connection, - [wallet.payer], - wallet.publicKey, - [tx], - ); - } else { - transactions.push(sendAndConfirmTransaction(connection, tx, signers)); - } - } - - await Promise.all(transactions) - .then((txs) => { - txs.map(console.log); - }) - .catch((e) => { - console.error(e); - throw e; - }); - } - - const afterTokenXBalance = await connection - .getTokenAccountBalance(wallet.publicKey) - .then((i) => new BN(i.value.amount)); - - const actualDepositedAmount = beforeTokenXBalance.sub(afterTokenXBalance); - if (actualDepositedAmount.toString() != seedAmount.toString()) { - throw new Error( - `actual deposited amount ${actualDepositedAmount} is not equal to seed amount ${seedAmount}`, - ); - } - - let binArrays = await pair.getBinArrays(); - binArrays = binArrays.sort((a, b) => a.account.index.cmp(b.account.index)); - - const binLiquidities = binArrays - .map((ba) => { - const [lowerBinId, upperBinId] = getBinArrayLowerUpperBinId( - ba.account.index, - ); - const binWithLiquidity: [number, number][] = []; - for (let i = lowerBinId.toNumber(); i <= upperBinId.toNumber(); i++) { - const binAmountX = ba.account.bins[i - lowerBinId.toNumber()].amountX; - const binPrice = getPriceOfBinByBinId(i, pair.lbPair.binStep); - const liquidity = new Decimal(binAmountX.toString()) - .mul(binPrice) - .floor() - .toNumber(); - binWithLiquidity.push([i, liquidity]); - } - return binWithLiquidity; - }) - .flat(); - - console.log(binLiquidities.filter((b) => b[1] > 0).reverse()); - console.log(binLiquidities.filter((b) => b[1] > 0)); - - // use babar to print chart in console if needed - // console.log(babar(binLiquidities)); -} - -main(); diff --git a/src/libs/config.ts b/src/libs/config.ts index 59f4ece..63d5b96 100644 --- a/src/libs/config.ts +++ b/src/libs/config.ts @@ -41,9 +41,6 @@ const CONFIG_SCHEMA: JSONSchemaType = { quoteSymbol: { type: "string", }, - baseDecimals: { - type: "number", - }, dynamicAmm: { type: "object", nullable: true, @@ -169,20 +166,26 @@ const CONFIG_SCHEMA: JSONSchemaType = { type: "number", }, maxPrice: { type: "number" }, - binStep: { type: "number" }, curvature: { type: "number" }, seedAmount: { type: "string" }, - basePositionKey: { type: "string" }, basePositionKeypairFilepath: { type: "string" }, + operatorKeypairFilepath: { type: "string" }, + positionOwner: { type: "string" }, + feeOwner: { type: "string" }, + lockReleasePoint: { type: "number" }, + seedTokenXToPositionOwner: { type: "boolean" }, }, required: [ "minPrice", "maxPrice", - "binStep", "curvature", "seedAmount", - "basePositionKey", "basePositionKeypairFilepath", + "operatorKeypairFilepath", + "positionOwner", + "feeOwner", + "lockReleasePoint", + "seedTokenXToPositionOwner", ], }, singleBinSeedLiquidity: { @@ -197,7 +200,7 @@ const CONFIG_SCHEMA: JSONSchemaType = { positionOwner: { type: "string" }, feeOwner: { type: "string" }, lockReleasePoint: { type: "number" }, - seedTokenXToPositionOwner: { type: "boolean" } + seedTokenXToPositionOwner: { type: "boolean" }, }, required: [ "price", @@ -208,7 +211,7 @@ const CONFIG_SCHEMA: JSONSchemaType = { "positionOwner", "feeOwner", "lockReleasePoint", - "seedTokenXToPositionOwner" + "seedTokenXToPositionOwner", ], }, }, @@ -310,11 +313,14 @@ export interface LockLiquidityAllocation { export interface LfgSeedLiquidityConfig { minPrice: number; maxPrice: number; - binStep: number; curvature: number; seedAmount: string; - basePositionKey: string; basePositionKeypairFilepath: string; + operatorKeypairFilepath: string; + positionOwner: string; + feeOwner: string; + lockReleasePoint: number; + seedTokenXToPositionOwner: boolean; } export interface SingleBinSeedLiquidityConfig { diff --git a/src/libs/constants.ts b/src/libs/constants.ts index 1d26a9b..66e1d74 100644 --- a/src/libs/constants.ts +++ b/src/libs/constants.ts @@ -3,3 +3,15 @@ export const SOL_TOKEN_MINT = "So11111111111111111111111111111111111111112"; export const USDC_TOKEN_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"; export const SOL_TOKEN_DECIMALS = 9; export const USDC_TOKEN_DECIMALS = 6; + +export const DLMM_PROGRAM_IDS = { + devnet: "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo", + localhost: "LbVRzDTvBDEcrthxfZ4RL6yiq3uZw8bS6MwtdY6UhFQ", + "mainnet-beta": "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo", +}; + +export const DYNAMIC_AMM_PROGRAM_IDS = { + devnet: "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB", + localhost: "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB", + "mainnet-beta": "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB", +}; diff --git a/src/libs/create_pool_utils.ts b/src/libs/create_pool_utils.ts new file mode 100644 index 0000000..1ae172c --- /dev/null +++ b/src/libs/create_pool_utils.ts @@ -0,0 +1,236 @@ +import { + Cluster, + Connection, + PublicKey, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { + MeteoraConfig, + getAmountInLamports, + getQuoteDecimals, + runSimulateTransaction, + getDynamicAmmActivationType, + getDlmmActivationType, + modifyComputeUnitPriceIx, + DLMM_PROGRAM_IDS, +} from "../"; +import { AmmImpl } from "@mercurial-finance/dynamic-amm-sdk"; +import { Wallet } from "@coral-xyz/anchor"; +import { BN } from "bn.js"; +import DLMM, { + LBCLMM_PROGRAM_IDS, + deriveCustomizablePermissionlessLbPair, +} from "@meteora-ag/dlmm"; +import { CustomizableParams } from "@mercurial-finance/dynamic-amm-sdk/dist/cjs/src/amm/types"; +import { + deriveCustomizablePermissionlessConstantProductPoolAddress, + createProgram, +} from "@mercurial-finance/dynamic-amm-sdk/dist/cjs/src/amm/utils"; +import { getMint } from "@solana/spl-token"; + +export async function createPermissionlessDynamicPool( + config: MeteoraConfig, + connection: Connection, + wallet: Wallet, + baseMint: PublicKey, + quoteMint: PublicKey, + opts?: { + cluster?: Cluster; + programId?: PublicKey; + }, +) { + if (!config.dynamicAmm) { + throw new Error("Missing dynamic amm configuration"); + } + console.log("\n> Initializing Permissionless Dynamic AMM pool..."); + + const quoteDecimals = getQuoteDecimals(config.quoteSymbol); + const baseMintAccount = await getMint(connection, baseMint); + const baseDecimals = baseMintAccount.decimals; + + const baseAmount = getAmountInLamports( + config.dynamicAmm.baseAmount, + baseDecimals, + ); + const quoteAmount = getAmountInLamports( + config.dynamicAmm.quoteAmount, + quoteDecimals, + ); + + console.log( + `- Using token A amount ${config.dynamicAmm.baseAmount}, in lamports = ${baseAmount}`, + ); + console.log( + `- Using token B amount ${config.dynamicAmm.quoteAmount}, in lamports = ${quoteAmount}`, + ); + + const activationType = getDynamicAmmActivationType( + config.dynamicAmm.activationType, + ); + + const customizeParam: CustomizableParams = { + tradeFeeNumerator: config.dynamicAmm.tradeFeeNumerator, + activationType: activationType, + activationPoint: config.dynamicAmm.activationPoint + ? new BN(config.dynamicAmm.activationPoint) + : null, + hasAlphaVault: config.dynamicAmm.hasAlphaVault, + padding: Array(90).fill(0), + }; + console.log( + `- Using tradeFeeNumerator = ${customizeParam.tradeFeeNumerator}`, + ); + console.log(`- Using activationType = ${config.dynamicAmm.activationType}`); + console.log(`- Using activationPoint = ${customizeParam.activationPoint}`); + console.log(`- Using hasAlphaVault = ${customizeParam.hasAlphaVault}`); + + const initPoolTx = + await AmmImpl.createCustomizablePermissionlessConstantProductPool( + connection, + wallet.publicKey, + baseMint, + quoteMint, + baseAmount, + quoteAmount, + customizeParam, + { + cluster: opts?.cluster, + programId: opts?.programId.toString(), + }, + ); + modifyComputeUnitPriceIx(initPoolTx, config.computeUnitPriceMicroLamports); + const poolKey = deriveCustomizablePermissionlessConstantProductPoolAddress( + baseMint, + quoteMint, + createProgram(connection).ammProgram.programId, + ); + + console.log(`\n> Pool address: ${poolKey}`); + + if (config.dryRun) { + console.log(`> Simulating init pool tx...`); + await runSimulateTransaction(connection, [wallet.payer], wallet.publicKey, [ + initPoolTx, + ]); + } else { + console.log(`>> Sending init pool transaction...`); + const initPoolTxHash = await sendAndConfirmTransaction( + connection, + initPoolTx, + [wallet.payer], + ).catch((err) => { + console.error(err); + throw err; + }); + console.log( + `>>> Pool initialized successfully with tx hash: ${initPoolTxHash}`, + ); + } +} + +export async function createPermissionlessDlmmPool( + config: MeteoraConfig, + connection: Connection, + wallet: Wallet, + baseMint: PublicKey, + quoteMint: PublicKey, + opts?: { + cluster?: Cluster | "localhost"; + programId?: PublicKey; + }, +) { + if (!config.dlmm) { + throw new Error("Missing DLMM configuration"); + } + console.log("\n> Initializing Permissionless DLMM pool..."); + + const binStep = config.dlmm.binStep; + const feeBps = config.dlmm.feeBps; + const hasAlphaVault = config.dlmm.hasAlphaVault; + const activationPoint = config.dlmm.activationPoint + ? new BN(config.dlmm.activationPoint) + : null; + + const activationType = getDlmmActivationType(config.dlmm.activationType); + + console.log(`- Using binStep = ${binStep}`); + console.log(`- Using feeBps = ${feeBps}`); + console.log(`- Using initialPrice = ${config.dlmm.initialPrice}`); + console.log(`- Using activationType = ${config.dlmm.activationType}`); + console.log(`- Using activationPoint = ${activationPoint}`); + console.log(`- Using hasAlphaVault = ${hasAlphaVault}`); + + const quoteDecimals = getQuoteDecimals(config.quoteSymbol); + const baseMintAccount = await getMint(connection, baseMint); + const baseDecimals = baseMintAccount.decimals; + + const initPrice = DLMM.getPricePerLamport( + baseDecimals, + quoteDecimals, + config.dlmm.initialPrice, + ); + let selectiveRounding = false; + if (config.dlmm.priceRounding == "up") { + selectiveRounding = false; + } else if (config.dlmm.priceRounding == "down") { + selectiveRounding = true; + } else { + throw new Error( + `Unknown price rounding: ${config.dlmm.priceRounding}. Should be 'up' or 'down'`, + ); + } + + const activateBinId = DLMM.getBinIdFromPrice( + initPrice, + binStep, + selectiveRounding, + ); + + const initPoolTx = await DLMM.createCustomizablePermissionlessLbPair( + connection, + new BN(binStep), + baseMint, + quoteMint, + new BN(activateBinId.toString()), + new BN(feeBps), + activationType, + hasAlphaVault, + wallet.publicKey, + activationPoint, + opts, + ); + modifyComputeUnitPriceIx(initPoolTx, config.computeUnitPriceMicroLamports); + + const cluster = opts?.cluster || "mainnet-beta"; + const dlmmProgramId = + opts?.programId ?? new PublicKey(DLMM_PROGRAM_IDS[cluster]); + + let poolKey: PublicKey; + [poolKey] = deriveCustomizablePermissionlessLbPair( + baseMint, + quoteMint, + dlmmProgramId, + ); + + console.log(`\n> Pool address: ${poolKey}`); + + if (config.dryRun) { + console.log(`\n> Simulating init pool tx...`); + await runSimulateTransaction(connection, [wallet.payer], wallet.publicKey, [ + initPoolTx, + ]); + } else { + console.log(`>> Sending init pool transaction...`); + let initPoolTxHash = await sendAndConfirmTransaction( + connection, + initPoolTx, + [wallet.payer], + ).catch((e) => { + console.error(e); + throw e; + }); + console.log( + `>>> Pool initialized successfully with tx hash: ${initPoolTxHash}`, + ); + } +} diff --git a/src/libs/math.ts b/src/libs/math.ts new file mode 100644 index 0000000..d9c8dea --- /dev/null +++ b/src/libs/math.ts @@ -0,0 +1,173 @@ +import { BN } from "bn.js"; +import Decimal from "decimal.js"; +import { MAX_BIN_PER_POSITION, getPriceOfBinByBinId } from "@meteora-ag/dlmm"; + +export function generateAmountForBinRange( + amount: BN, + binStep: number, + tokenXDecimal: number, + tokenYDecimal: number, + minBinId: BN, + maxBinId: BN, + k: number, +): Map { + const toTokenMultiplier = new Decimal(10 ** (tokenXDecimal - tokenYDecimal)); + const minPrice = getPriceOfBinByBinId(minBinId.toNumber(), binStep).mul( + toTokenMultiplier, + ); + const maxPrice = getPriceOfBinByBinId(maxBinId.toNumber(), binStep).mul( + toTokenMultiplier, + ); + const binAmounts = new Map(); + + for (let i = minBinId.toNumber(); i < maxBinId.toNumber(); i++) { + const binAmount = generateBinAmount( + amount, + binStep, + new BN(i), + tokenXDecimal, + tokenYDecimal, + minPrice, + maxPrice, + k, + ); + + binAmounts.set(i, binAmount); + } + + return binAmounts; +} + +export function generateBinAmount( + amount: BN, + binStep: number, + binId: BN, + tokenXDecimal: number, + tokenYDecimal: number, + minPrice: Decimal, + maxPrice: Decimal, + k: number, +) { + const c1 = getC( + amount, + binStep, + binId.add(new BN(1)), + tokenXDecimal, + tokenYDecimal, + minPrice, + maxPrice, + k, + ); + + const c0 = getC( + amount, + binStep, + binId, + tokenXDecimal, + tokenYDecimal, + minPrice, + maxPrice, + k, + ); + + return new BN(c1.sub(c0).floor().toString()); +} + +export function getC( + amount: BN, + binStep: number, + binId: BN, + baseTokenDecimal: number, + quoteTokenDecimal: number, + minPrice: Decimal, + maxPrice: Decimal, + k: number, +) { + const currentPricePerLamport = new Decimal(1 + binStep / 10000).pow( + binId.toNumber(), + ); + const currentPricePerToken = currentPricePerLamport.mul( + new Decimal(10 ** (baseTokenDecimal - quoteTokenDecimal)), + ); + const priceRange = maxPrice.sub(minPrice); + const currentPriceDeltaFromMin = currentPricePerToken.sub( + new Decimal(minPrice), + ); + + const c = new Decimal(amount.toString()).mul( + currentPriceDeltaFromMin.div(priceRange).pow(k), + ); + + return c.floor(); +} + +export function compressBinAmount(binAmount: Map, multiplier: BN) { + const compressedBinAmount = new Map(); + + let totalAmount = new BN(0); + let compressionLoss = new BN(0); + + for (const [binId, amount] of binAmount) { + totalAmount = totalAmount.add(amount); + const compressedAmount = amount.div(multiplier); + + compressedBinAmount.set(binId, compressedAmount); + let loss = amount.sub(compressedAmount.mul(multiplier)); + compressionLoss = compressionLoss.add(loss); + } + + return { + compressedBinAmount, + compressionLoss, + }; +} + +export function distributeAmountToCompressedBinsByRatio( + compressedBinAmount: Map, + uncompressedAmount: BN, + multiplier: BN, + binCapAmount: BN, +) { + const newCompressedBinAmount = new Map(); + let totalCompressedAmount = new BN(0); + + for (const compressedAmount of compressedBinAmount.values()) { + totalCompressedAmount = totalCompressedAmount.add(compressedAmount); + } + + let totalDepositedAmount = new BN(0); + + for (const [binId, compressedAmount] of compressedBinAmount.entries()) { + const depositAmount = compressedAmount + .mul(uncompressedAmount) + .div(totalCompressedAmount); + + let compressedDepositAmount = depositAmount.div(multiplier); + + let newCompressedAmount = compressedAmount.add(compressedDepositAmount); + if (newCompressedAmount.gt(binCapAmount)) { + compressedDepositAmount = compressedDepositAmount.sub( + newCompressedAmount.sub(binCapAmount), + ); + newCompressedAmount = binCapAmount; + } + newCompressedBinAmount.set(binId, newCompressedAmount); + + totalDepositedAmount = totalDepositedAmount.add( + compressedDepositAmount.mul(multiplier), + ); + } + + const loss = uncompressedAmount.sub(totalDepositedAmount); + + return { + newCompressedBinAmount, + loss, + }; +} + +export function getPositionCount(minBinId: BN, maxBinId: BN) { + const binDelta = maxBinId.sub(minBinId); + const positionCount = binDelta.div(MAX_BIN_PER_POSITION); + return positionCount.add(new BN(1)); +} diff --git a/src/libs/seed_liquidity_utils.ts b/src/libs/seed_liquidity_utils.ts new file mode 100644 index 0000000..86a3f09 --- /dev/null +++ b/src/libs/seed_liquidity_utils.ts @@ -0,0 +1,896 @@ +import { + Cluster, + ComputeBudgetProgram, + Connection, + Keypair, + PublicKey, + Transaction, + TransactionInstruction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { DEFAULT_ADD_LIQUIDITY_CU, runSimulateTransaction } from "./utils"; +import { BN } from "bn.js"; +import DLMM, { + BASIS_POINT_MAX, + BinLiquidityDistribution, + CompressedBinDepositAmounts, + LiquidityParameter, + MAX_BIN_PER_POSITION, + PositionV2, + binIdToBinArrayIndex, + deriveBinArray, + deriveBinArrayBitmapExtension, + deriveCustomizablePermissionlessLbPair, + derivePosition, + getEstimatedComputeUnitIxWithBuffer, + getOrCreateATAInstruction, + isOverflowDefaultBinArrayBitmap, +} from "@meteora-ag/dlmm"; +import { + compressBinAmount, + distributeAmountToCompressedBinsByRatio, + generateAmountForBinRange, + getPositionCount, +} from "./math"; +import { + getAssociatedTokenAddressSync, + AccountLayout, + createTransferInstruction, + createAssociatedTokenAccountInstruction, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { DLMM_PROGRAM_IDS } from "./constants"; + +export async function seedLiquiditySingleBin( + connection: Connection, + payerKeypair: Keypair, + baseKeypair: Keypair, + operatorKeypair: Keypair, + positionOwner: PublicKey, + feeOwner: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + seedAmount: BN, + price: number, + priceRounding: string, + lockReleasePoint: BN, + seedTokenXToPositionOwner: boolean, + dryRun: boolean, + computeUnitPriceMicroLamports: number | bigint, + opts?: { + cluster?: Cluster | "localhost"; + programId?: PublicKey; + }, +) { + if (priceRounding != "up" && priceRounding != "down") { + throw new Error("Invalid selective rounding value. Must be 'up' or 'down'"); + } + + const cluster = opts?.cluster || "mainnet-beta"; + const dlmmProgramId = + opts?.programId ?? new PublicKey(DLMM_PROGRAM_IDS[cluster]); + + let poolKey: PublicKey; + [poolKey] = deriveCustomizablePermissionlessLbPair( + baseMint, + quoteMint, + dlmmProgramId, + ); + console.log(`- Using pool key ${poolKey.toString()}`); + + console.log(`- Using seedAmount in lamports = ${seedAmount}`); + console.log(`- Using priceRounding = ${priceRounding}`); + console.log(`- Using price ${price}`); + console.log(`- Using operator ${operatorKeypair.publicKey}`); + console.log(`- Using positionOwner ${positionOwner}`); + console.log(`- Using feeOwner ${feeOwner}`); + console.log(`- Using lockReleasePoint ${lockReleasePoint}`); + console.log(`- Using seedTokenXToPositionOwner ${seedTokenXToPositionOwner}`); + + if (!seedTokenXToPositionOwner) { + console.log( + `WARNING: You selected seedTokenXToPositionOwner = false, you should manually send 1 lamport of token X to the position owner account to prove ownership.`, + ); + } + + const { preInstructions, addLiquidityInstructions } = + await createSeedLiquiditySingleBinInstructions( + connection, + poolKey, + payerKeypair.publicKey, + baseKeypair.publicKey, + seedAmount, + price, + priceRounding == "up", + positionOwner, + feeOwner, + operatorKeypair.publicKey, + lockReleasePoint, + seedTokenXToPositionOwner, + opts, + ); + const seedLiquidityIxs = [...preInstructions, ...addLiquidityInstructions]; + + const setCUPriceIx = ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: computeUnitPriceMicroLamports, + }); + + const { blockhash, lastValidBlockHeight } = + await connection.getLatestBlockhash("confirmed"); + + const tx = new Transaction({ + feePayer: payerKeypair.publicKey, + blockhash, + lastValidBlockHeight, + }) + .add(setCUPriceIx) + .add(...seedLiquidityIxs); + + if (dryRun) { + console.log(`\n> Simulating seedLiquiditySingleBin transaction...`); + await runSimulateTransaction( + connection, + [payerKeypair, baseKeypair, operatorKeypair], + payerKeypair.publicKey, + [tx], + ); + } else { + console.log(`>> Sending seedLiquiditySingleBin transaction...`); + const txHash = await sendAndConfirmTransaction(connection, tx, [ + payerKeypair, + baseKeypair, + operatorKeypair, + ]).catch((err) => { + console.error(err); + throw err; + }); + console.log( + `>>> SeedLiquiditySingleBin successfully with tx hash: ${txHash}`, + ); + } +} + +export async function seedLiquidityLfg( + connection: Connection, + payerKeypair: Keypair, + baseKeypair: Keypair, + operatorKeypair: Keypair, + positionOwner: PublicKey, + feeOwner: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + seedAmount: BN, + curvature: number, + minPricePerLamport: BN, + maxPricePerLamport: BN, + lockReleasePoint: BN, + seedTokenXToPositionOwner: boolean, + dryRun: boolean, + computeUnitPriceMicroLamports: number | bigint, + opts?: { + cluster?: Cluster | "localhost"; + programId?: PublicKey; + }, +) { + const cluster = opts?.cluster || "mainnet-beta"; + const dlmmProgramId = + opts?.programId ?? new PublicKey(DLMM_PROGRAM_IDS[cluster]); + + let poolKey: PublicKey; + [poolKey] = deriveCustomizablePermissionlessLbPair( + baseMint, + quoteMint, + dlmmProgramId, + ); + console.log(`- Using pool key ${poolKey.toString()}`); + + console.log(`- Using seedAmount in lamports = ${seedAmount}`); + console.log(`- Using curvature = ${curvature}`); + console.log(`- Using minPrice per lamport ${minPricePerLamport}`); + console.log(`- Using maxPrice per lamport ${maxPricePerLamport}`); + console.log(`- Using operator ${operatorKeypair.publicKey}`); + console.log(`- Using positionOwner ${positionOwner}`); + console.log(`- Using feeOwner ${feeOwner}`); + console.log(`- Using lockReleasePoint ${lockReleasePoint}`); + console.log(`- Using seedTokenXToPositionOwner ${seedTokenXToPositionOwner}`); + + if (!seedTokenXToPositionOwner) { + console.log( + `WARNING: You selected seedTokenXToPositionOwner = false, you should manually send 1 lamport of token X to the position owner account to prove ownership.`, + ); + } + + const { + preInstructions, + initializeBinArraysAndPositionInstructions, + addLiquidityInstructions, + } = await createSeedLiquidityLfgInstructions( + connection, + poolKey, + payerKeypair.publicKey, + baseKeypair.publicKey, + lockReleasePoint, + seedAmount, + curvature, + minPricePerLamport, + maxPricePerLamport, + positionOwner, + feeOwner, + operatorKeypair.publicKey, + opts, + ); + + // run preflight ixs + const { blockhash, lastValidBlockHeight } = + await connection.getLatestBlockhash("confirmed"); + const setCUPriceIx = ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: computeUnitPriceMicroLamports, + }); + const tx = new Transaction({ + feePayer: payerKeypair.publicKey, + blockhash, + lastValidBlockHeight, + }) + .add(setCUPriceIx) + .add(...preInstructions); + + const signers = [payerKeypair]; + + if (dryRun) { + throw new Error( + "dryRun is not supported for this script, please set dryRun config to false", + ); + } + + console.log(`>> Running preflight instructions...`); + try { + console.log(`>> Sending preflight transaction...`); + const txHash = await sendAndConfirmTransaction(connection, tx, signers); + console.log(`>>> Preflight successfully with tx hash: ${txHash}`); + } catch (err) { + console.error(err); + throw new Error(err); + } + + console.log(`>> Running initializeBinArraysAndPosition instructions...`); + // Initialize all bin array and position, transaction order can be in sequence or not + { + const { blockhash, lastValidBlockHeight } = + await connection.getLatestBlockhash("confirmed"); + + const transactions: Array> = []; + + for (const groupIx of initializeBinArraysAndPositionInstructions) { + const tx = new Transaction({ + feePayer: payerKeypair.publicKey, + blockhash, + lastValidBlockHeight, + }).add(...groupIx); + + const signers = [payerKeypair, baseKeypair, operatorKeypair]; + + transactions.push(sendAndConfirmTransaction(connection, tx, signers)); + } + + await Promise.all(transactions) + .then((txs) => { + txs.map(console.log); + }) + .catch((e) => { + console.error(e); + throw e; + }); + } + console.log(`>>> Finished initializeBinArraysAndPosition instructions!`); + + console.log(`>> Running addLiquidity instructions...`); + { + const { blockhash, lastValidBlockHeight } = + await connection.getLatestBlockhash("confirmed"); + + const transactions: Array> = []; + + // Deposit to positions created in above step. The add liquidity order can be in sequence or not. + for (const groupIx of addLiquidityInstructions) { + const tx = new Transaction({ + feePayer: payerKeypair.publicKey, + blockhash, + lastValidBlockHeight, + }).add(...groupIx); + + const signers = [payerKeypair, operatorKeypair]; + + transactions.push(sendAndConfirmTransaction(connection, tx, signers)); + } + + await Promise.all(transactions) + .then((txs) => { + txs.map(console.log); + }) + .catch((e) => { + console.error(e); + throw e; + }); + } + console.log(`>>> Finished addLiquidity instructions!`); +} + +export async function createSeedLiquiditySingleBinInstructions( + connection: Connection, + poolAddress: PublicKey, + payer: PublicKey, + base: PublicKey, + seedAmount: BN, + price: number, + roundingUp: boolean, + positionOwner: PublicKey, + feeOwner: PublicKey, + operator: PublicKey, + lockReleasePoint: BN, + shouldSeedPositionOwner: boolean = false, + opts?: { + cluster?: Cluster | "localhost"; + programId?: PublicKey; + }, +): Promise { + const pair = await DLMM.create(connection, poolAddress, opts); + + const pricePerLamport = DLMM.getPricePerLamport( + pair.tokenX.decimal, + pair.tokenY.decimal, + price, + ); + const binIdNumber = DLMM.getBinIdFromPrice( + pricePerLamport, + pair.lbPair.binStep, + !roundingUp, + ); + + const binId = new BN(binIdNumber); + const lowerBinArrayIndex = binIdToBinArrayIndex(binId); + const upperBinArrayIndex = lowerBinArrayIndex.add(new BN(1)); + + const [lowerBinArray] = deriveBinArray( + pair.pubkey, + lowerBinArrayIndex, + pair.program.programId, + ); + const [upperBinArray] = deriveBinArray( + pair.pubkey, + upperBinArrayIndex, + pair.program.programId, + ); + const [positionPda] = derivePosition( + pair.pubkey, + base, + binId, + new BN(1), + pair.program.programId, + ); + + const preInstructions = []; + + const [ + { ataPubKey: userTokenX, ix: createPayerTokenXIx }, + { ataPubKey: userTokenY, ix: createPayerTokenYIx }, + ] = await Promise.all([ + getOrCreateATAInstruction( + connection, + pair.tokenX.publicKey, + operator, + payer, + ), + getOrCreateATAInstruction( + connection, + pair.tokenY.publicKey, + operator, + payer, + ), + ]); + + // create userTokenX and userTokenY accounts + createPayerTokenXIx && preInstructions.push(createPayerTokenXIx); + createPayerTokenYIx && preInstructions.push(createPayerTokenYIx); + + let [binArrayBitmapExtension] = deriveBinArrayBitmapExtension( + pair.pubkey, + pair.program.programId, + ); + const accounts = await connection.getMultipleAccountsInfo([ + lowerBinArray, + upperBinArray, + positionPda, + binArrayBitmapExtension, + ]); + + if (isOverflowDefaultBinArrayBitmap(lowerBinArrayIndex)) { + const bitmapExtensionAccount = accounts[3]; + if (!bitmapExtensionAccount) { + preInstructions.push( + await pair.program.methods + .initializeBinArrayBitmapExtension() + .accounts({ + binArrayBitmapExtension, + funder: payer, + lbPair: pair.pubkey, + }) + .instruction(), + ); + } + } else { + binArrayBitmapExtension = pair.program.programId; + } + + const positionOwnerTokenX = getAssociatedTokenAddressSync( + pair.lbPair.tokenXMint, + positionOwner, + true, + ); + + if (shouldSeedPositionOwner) { + const positionOwnerTokenXAccount = + await connection.getAccountInfo(positionOwnerTokenX); + if (positionOwnerTokenXAccount) { + const account = AccountLayout.decode(positionOwnerTokenXAccount.data); + if (account.amount == BigInt(0)) { + // send 1 lamport to position owner token X to prove ownership + const transferIx = createTransferInstruction( + userTokenX, + positionOwnerTokenX, + payer, + 1, + ); + preInstructions.push(transferIx); + } + } else { + const createPositionOwnerTokenXIx = + createAssociatedTokenAccountInstruction( + payer, + positionOwnerTokenX, + positionOwner, + pair.lbPair.tokenXMint, + ); + preInstructions.push(createPositionOwnerTokenXIx); + + // send 1 lamport to position owner token X to prove ownership + const transferIx = createTransferInstruction( + userTokenX, + positionOwnerTokenX, + payer, + 1, + ); + preInstructions.push(transferIx); + } + } + + const lowerBinArrayAccount = accounts[0]; + const upperBinArrayAccount = accounts[1]; + const positionAccount = accounts[2]; + + if (!lowerBinArrayAccount) { + preInstructions.push( + await pair.program.methods + .initializeBinArray(lowerBinArrayIndex) + .accounts({ + binArray: lowerBinArray, + funder: payer, + lbPair: pair.pubkey, + }) + .instruction(), + ); + } + + if (!upperBinArrayAccount) { + preInstructions.push( + await pair.program.methods + .initializeBinArray(upperBinArrayIndex) + .accounts({ + binArray: upperBinArray, + funder: payer, + lbPair: pair.pubkey, + }) + .instruction(), + ); + } + + if (!positionAccount) { + preInstructions.push( + await pair.program.methods + .initializePositionByOperator( + binId.toNumber(), + 1, + feeOwner, + lockReleasePoint, + ) + .accounts({ + payer, + base, + position: positionPda, + lbPair: pair.pubkey, + owner: positionOwner, + operator, + operatorTokenX: userTokenX, + ownerTokenX: positionOwnerTokenX, + }) + .instruction(), + ); + } + + const binLiquidityDist: BinLiquidityDistribution = { + binId: binIdNumber, + distributionX: BASIS_POINT_MAX, + distributionY: 0, + }; + + const addLiquidityParams: LiquidityParameter = { + amountX: seedAmount, + amountY: new BN(0), + binLiquidityDist: [binLiquidityDist], + }; + + const depositLiquidityIx = await pair.program.methods + .addLiquidity(addLiquidityParams) + .accounts({ + position: positionPda, + lbPair: pair.pubkey, + binArrayBitmapExtension, + userTokenX, + userTokenY, + reserveX: pair.lbPair.reserveX, + reserveY: pair.lbPair.reserveY, + tokenXMint: pair.lbPair.tokenXMint, + tokenYMint: pair.lbPair.tokenYMint, + binArrayLower: lowerBinArray, + binArrayUpper: upperBinArray, + sender: operator, + tokenXProgram: TOKEN_PROGRAM_ID, + tokenYProgram: TOKEN_PROGRAM_ID, + }) + .instruction(); + + return { + preInstructions, + addLiquidityInstructions: [depositLiquidityIx], + }; +} + +export async function createSeedLiquidityLfgInstructions( + connection: Connection, + poolAddress: PublicKey, + payer: PublicKey, + base: PublicKey, + lockReleasePoint: PublicKey, + seedAmount: BN, + curvature: number, + minPricePerLamport: BN, + maxPricePerLamport: BN, + positionOwner: PublicKey, + feeOwner: PublicKey, + operator: PublicKey, + opts?: { + cluster?: Cluster | "localhost"; + programId?: PublicKey; + }, +): Promise { + const pair = await DLMM.create(connection, poolAddress, opts); + + const minBinId = new BN( + DLMM.getBinIdFromPrice(minPricePerLamport, pair.lbPair.binStep, false), + ); + const maxBinId = new BN( + DLMM.getBinIdFromPrice(maxPricePerLamport, pair.lbPair.binStep, true), + ); + if (minBinId.toNumber() < pair.lbPair.activeId) { + throw new Error("minPrice < current pair price"); + } + if (minBinId.toNumber() > maxBinId.toNumber()) { + throw new Error("Price range too small"); + } + const k = 1.0 / curvature; + + const binDepositAmount = generateAmountForBinRange( + seedAmount, + pair.lbPair.binStep, + pair.tokenX.decimal, + pair.tokenY.decimal, + minBinId, + maxBinId, + k, + ); + + const decompressMultiplier = new BN(10 ** pair.tokenX.decimal); + + let { compressedBinAmount, compressionLoss } = compressBinAmount( + binDepositAmount, + decompressMultiplier, + ); + + // Distribute loss after compression back to bins based on bin ratio with total deposited amount + let { newCompressedBinAmount: compressedBinDepositAmount, loss: finalLoss } = + distributeAmountToCompressedBinsByRatio( + compressedBinAmount, + compressionLoss, + decompressMultiplier, + new BN(2 ** 32 - 1), // u32 + ); + + // This amount will be deposited to the last bin without compression + const positionCount = getPositionCount(minBinId, maxBinId.sub(new BN(1))); + + const preInstructions: Array = []; + const initializeBinArraysAndPositionIxs: Array< + Array + > = []; + const addLiquidityIxs: Array> = []; + const appendedInitBinArrayIx = new Set(); + + const { ataPubKey: userTokenX, ix: createPayerTokenXIx } = + await getOrCreateATAInstruction( + connection, + pair.lbPair.tokenXMint, + operator, + payer, + ); + + // create userTokenX account + createPayerTokenXIx && preInstructions.push(createPayerTokenXIx); + + const positionOwnerTokenX = getAssociatedTokenAddressSync( + pair.lbPair.tokenXMint, + positionOwner, + true, + ); + + const positionOwnerTokenXAccount = + await connection.getAccountInfo(positionOwnerTokenX); + if (positionOwnerTokenXAccount) { + const account = AccountLayout.decode(positionOwnerTokenXAccount.data); + if (account.amount == BigInt(0)) { + // send 1 lamport to position owner token X to prove ownership + const transferIx = createTransferInstruction( + userTokenX, + positionOwnerTokenX, + payer, + 1, + ); + preInstructions.push(transferIx); + } + } else { + const createPositionOwnerTokenXIx = createAssociatedTokenAccountInstruction( + payer, + positionOwnerTokenX, + positionOwner, + pair.lbPair.tokenXMint, + ); + preInstructions.push(createPositionOwnerTokenXIx); + + // send 1 lamport to position owner token X to prove ownership + const transferIx = createTransferInstruction( + userTokenX, + positionOwnerTokenX, + payer, + 1, + ); + preInstructions.push(transferIx); + } + + for (let i = 0; i < positionCount.toNumber(); i++) { + const lowerBinId = minBinId.add(MAX_BIN_PER_POSITION.mul(new BN(i))); + const upperBinId = lowerBinId.add(MAX_BIN_PER_POSITION).sub(new BN(1)); + + const lowerBinArrayIndex = binIdToBinArrayIndex(lowerBinId); + const upperBinArrayIndex = binIdToBinArrayIndex(upperBinId); + + const [positionPda, _bump] = derivePosition( + pair.pubkey, + base, + lowerBinId, + MAX_BIN_PER_POSITION, + pair.program.programId, + ); + + const [lowerBinArray] = deriveBinArray( + pair.pubkey, + lowerBinArrayIndex, + pair.program.programId, + ); + + const [upperBinArray] = deriveBinArray( + pair.pubkey, + upperBinArrayIndex, + pair.program.programId, + ); + + const accounts = await connection.getMultipleAccountsInfo([ + lowerBinArray, + upperBinArray, + positionPda, + ]); + + let instructions: TransactionInstruction[] = []; + + const lowerBinArrayAccount = accounts[0]; + if ( + !lowerBinArrayAccount && + !appendedInitBinArrayIx.has(lowerBinArray.toBase58()) + ) { + instructions.push( + await pair.program.methods + .initializeBinArray(lowerBinArrayIndex) + .accounts({ + lbPair: pair.pubkey, + binArray: lowerBinArray, + funder: payer, + }) + .instruction(), + ); + + appendedInitBinArrayIx.add(lowerBinArray.toBase58()); + } + + const upperBinArrayAccount = accounts[1]; + if ( + !upperBinArrayAccount && + !appendedInitBinArrayIx.has(upperBinArray.toBase58()) + ) { + instructions.push( + await pair.program.methods + .initializeBinArray(upperBinArrayIndex) + .accounts({ + lbPair: pair.pubkey, + binArray: upperBinArray, + funder: payer, + }) + .instruction(), + ); + + appendedInitBinArrayIx.add(upperBinArray.toBase58()); + } + + const positionAccount = accounts[2]; + if (!positionAccount) { + instructions.push( + await pair.program.methods + .initializePositionByOperator( + lowerBinId.toNumber(), + MAX_BIN_PER_POSITION.toNumber(), + feeOwner, + lockReleasePoint, + ) + .accounts({ + payer: payer, + base, + position: positionPda, + lbPair: pair.pubkey, + owner: positionOwner, + operator, + operatorTokenX: userTokenX, + ownerTokenX: positionOwnerTokenX, + }) + .instruction(), + ); + } + + let cluster = opts?.cluster ?? "mainnet-beta"; + // Initialize bin arrays and initialize position account in 1 tx + if (instructions.length > 1) { + instructions.push( + await getEstimatedComputeUnitIxWithBuffer( + connection, + instructions, + payer, + ), + ); + + initializeBinArraysAndPositionIxs.push(instructions); + instructions = []; + } + + const positionDeposited = + positionAccount && + pair.program.coder.accounts + .decode( + pair.program.account.positionV2.idlAccount.name, + positionAccount.data, + ) + .liquidityShares.reduce((total, cur) => total.add(cur), new BN(0)) + .gt(new BN(0)); + + if (!positionDeposited) { + const cappedUpperBinId = Math.min( + upperBinId.toNumber(), + maxBinId.toNumber() - 1, + ); + + const bins: CompressedBinDepositAmounts = []; + + for (let i = lowerBinId.toNumber(); i <= cappedUpperBinId; i++) { + bins.push({ + binId: i, + amount: compressedBinDepositAmount.get(i).toNumber(), + }); + } + + instructions.push( + await pair.program.methods + .addLiquidityOneSidePrecise({ + bins, + decompressMultiplier, + }) + .accounts({ + position: positionPda, + lbPair: pair.pubkey, + binArrayBitmapExtension: pair.binArrayBitmapExtension + ? pair.binArrayBitmapExtension.publicKey + : pair.program.programId, + userToken: userTokenX, + reserve: pair.lbPair.reserveX, + tokenMint: pair.lbPair.tokenXMint, + binArrayLower: lowerBinArray, + binArrayUpper: upperBinArray, + sender: operator, + }) + .instruction(), + ); + + // Last position + if (i + 1 >= positionCount.toNumber() && !finalLoss.isZero()) { + instructions.push( + await pair.program.methods + .addLiquidityOneSide({ + amount: finalLoss, + activeId: pair.lbPair.activeId, + maxActiveBinSlippage: 0, + binLiquidityDist: [ + { + binId: cappedUpperBinId, + weight: 1, + }, + ], + }) + .accounts({ + position: positionPda, + lbPair: pair.pubkey, + binArrayBitmapExtension: pair.binArrayBitmapExtension + ? pair.binArrayBitmapExtension.publicKey + : pair.program.programId, + userToken: userTokenX, + reserve: pair.lbPair.reserveX, + tokenMint: pair.lbPair.tokenXMint, + binArrayLower: lowerBinArray, + binArrayUpper: upperBinArray, + sender: operator, + }) + .instruction(), + ); + } + + addLiquidityIxs.push([ + ComputeBudgetProgram.setComputeUnitLimit({ + units: DEFAULT_ADD_LIQUIDITY_CU, + }), + ...instructions, + ]); + } + } + + return { + preInstructions, + addLiquidityInstructions: addLiquidityIxs, + initializeBinArraysAndPositionInstructions: + initializeBinArraysAndPositionIxs, + }; +} + +export interface SeedLiquiditySingleBinInstructionSet { + preInstructions: Array; + addLiquidityInstructions: Array; +} + +export interface SeedLiquidityLfgInstructionSet { + preInstructions: Array; + initializeBinArraysAndPositionInstructions: Array< + Array + >; + addLiquidityInstructions: Array>; +} diff --git a/src/libs/utils.ts b/src/libs/utils.ts index d62196a..1421b7a 100644 --- a/src/libs/utils.ts +++ b/src/libs/utils.ts @@ -20,7 +20,10 @@ import { import { Wallet } from "@coral-xyz/anchor"; import { simulateTransaction } from "@coral-xyz/anchor/dist/cjs/utils/rpc"; import { ActivationType as DynamicAmmActivationType } from "@mercurial-finance/dynamic-amm-sdk/dist/cjs/src/amm/types"; -import { ActivationType as DlmmActivationType } from "@meteora-ag/dlmm"; +import { + ActivationType as DlmmActivationType, + getPriceOfBinByBinId, +} from "@meteora-ag/dlmm"; import { PermissionWithAuthority, PermissionWithMerkleProof, @@ -30,6 +33,8 @@ import { } from "@meteora-ag/alpha-vault"; import { MeteoraConfig } from ".."; +export const DEFAULT_ADD_LIQUIDITY_CU = 800_000; + export function validate_config(config: MeteoraConfig) { if (!config.keypairFilePath) { throw new Error("Missing keypairFilePath in config file."); diff --git a/src/seed_liquidity_lfg.ts b/src/seed_liquidity_lfg.ts new file mode 100644 index 0000000..bde4ccf --- /dev/null +++ b/src/seed_liquidity_lfg.ts @@ -0,0 +1,166 @@ +import { + ComputeBudgetProgram, + Connection, + Keypair, + PublicKey, + Transaction, + TransactionInstruction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { + DEFAULT_COMMITMENT_LEVEL, + MeteoraConfig, + getAmountInLamports, + getQuoteMint, + getQuoteDecimals, + safeParseKeypairFromFile, + runSimulateTransaction, + parseConfigFromCli, + generateAmountForBinRange, + compressBinAmount, + distributeAmountToCompressedBinsByRatio, + getPositionCount, + DEFAULT_ADD_LIQUIDITY_CU, + seedLiquidityLfg, +} from "."; +import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; +import { BN } from "bn.js"; +import DLMM, { + CompressedBinDepositAmounts, + LBCLMM_PROGRAM_IDS, + MAX_BIN_PER_POSITION, + PositionV2, + binIdToBinArrayIndex, + deriveBinArray, + deriveCustomizablePermissionlessLbPair, + derivePosition, + getBinArrayLowerUpperBinId, + getEstimatedComputeUnitIxWithBuffer, + getOrCreateATAInstruction, + getPriceOfBinByBinId, +} from "@meteora-ag/dlmm"; +import Decimal from "decimal.js"; +import { + AccountLayout, + createAssociatedTokenAccountInstruction, + createTransferInstruction, + getAssociatedTokenAddressSync, + getMint, +} from "@solana/spl-token"; + +async function main() { + let config: MeteoraConfig = parseConfigFromCli(); + + console.log(`> Using keypair file path ${config.keypairFilePath}`); + let keypair = safeParseKeypairFromFile(config.keypairFilePath); + + console.log("\n> Initializing with general configuration..."); + console.log(`- Using RPC URL ${config.rpcUrl}`); + console.log(`- Dry run = ${config.dryRun}`); + console.log(`- Using payer ${keypair.publicKey} to execute commands`); + + const connection = new Connection(config.rpcUrl, DEFAULT_COMMITMENT_LEVEL); + const wallet = new Wallet(keypair); + const provider = new AnchorProvider(connection, wallet, { + commitment: connection.commitment, + }); + const DLMM_PROGRAM_ID = new PublicKey(LBCLMM_PROGRAM_IDS["mainnet-beta"]); + + if (!config.baseMint) { + throw new Error("Missing baseMint in configuration"); + } + const baseMint = new PublicKey(config.baseMint); + const baseMintAccount = await getMint(connection, baseMint); + const baseDecimals = baseMintAccount.decimals; + + let quoteMint = getQuoteMint(config.quoteSymbol); + const quoteDecimals = getQuoteDecimals(config.quoteSymbol); + + console.log(`- Using base token mint ${baseMint.toString()}`); + console.log(`- Using quote token mint ${quoteMint.toString()}`); + + let poolKey: PublicKey; + [poolKey] = deriveCustomizablePermissionlessLbPair( + baseMint, + quoteMint, + new PublicKey(LBCLMM_PROGRAM_IDS["mainnet-beta"]), + ); + console.log(`- Using pool key ${poolKey.toString()}`); + + if (!config.lfgSeedLiquidity) { + throw new Error(`Missing DLMM LFG seed liquidity in configuration`); + } + + const pair = await DLMM.create(connection, poolKey, { + cluster: "mainnet-beta", + }); + await pair.refetchStates(); + + const seedAmount = getAmountInLamports( + config.lfgSeedLiquidity.seedAmount, + baseDecimals, + ); + const curvature = config.lfgSeedLiquidity.curvature; + const minPrice = config.lfgSeedLiquidity.minPrice; + const maxPrice = config.lfgSeedLiquidity.maxPrice; + const baseKeypair = safeParseKeypairFromFile( + config.lfgSeedLiquidity.basePositionKeypairFilepath, + ); + const operatorKeypair = safeParseKeypairFromFile( + config.lfgSeedLiquidity.operatorKeypairFilepath, + ); + const positionOwner = new PublicKey(config.lfgSeedLiquidity.positionOwner); + const feeOwner = new PublicKey(config.lfgSeedLiquidity.feeOwner); + const operator = operatorKeypair.publicKey; + const lockReleasePoint = new BN(config.lfgSeedLiquidity.lockReleasePoint); + const seedTokenXToPositionOwner = + config.lfgSeedLiquidity.seedTokenXToPositionOwner; + + console.log(`- Using seedAmount in lamports = ${seedAmount}`); + console.log(`- Using curvature = ${curvature}`); + console.log(`- Using minPrice ${minPrice}`); + console.log(`- Using maxPrice ${maxPrice}`); + console.log(`- Using operator ${operator}`); + console.log(`- Using positionOwner ${positionOwner}`); + console.log(`- Using feeOwner ${feeOwner}`); + console.log(`- Using lockReleasePoint ${lockReleasePoint}`); + console.log(`- Using seedTokenXToPositionOwner ${seedTokenXToPositionOwner}`); + + if (!seedTokenXToPositionOwner) { + console.log( + `WARNING: You selected seedTokenXToPositionOwner = false, you should manually send 1 lamport of token X to the position owner account to prove ownership.`, + ); + } + + const minPricePerLamport = DLMM.getPricePerLamport( + baseDecimals, + quoteDecimals, + minPrice, + ); + const maxPricePerLamport = DLMM.getPricePerLamport( + baseDecimals, + quoteDecimals, + maxPrice, + ); + + await seedLiquidityLfg( + connection, + keypair, + baseKeypair, + operatorKeypair, + positionOwner, + feeOwner, + baseMint, + quoteMint, + seedAmount, + curvature, + minPricePerLamport, + maxPricePerLamport, + lockReleasePoint, + seedTokenXToPositionOwner, + config.dryRun, + config.computeUnitPriceMicroLamports, + ); +} + +main(); diff --git a/src/seed_liquidity_single_bin.ts b/src/seed_liquidity_single_bin.ts index af9f1a0..32b13fb 100644 --- a/src/seed_liquidity_single_bin.ts +++ b/src/seed_liquidity_single_bin.ts @@ -1,11 +1,4 @@ -import { - ComputeBudgetProgram, - Connection, - Keypair, - PublicKey, - Transaction, - sendAndConfirmTransaction, -} from "@solana/web3.js"; +import { Connection, PublicKey } from "@solana/web3.js"; import { DEFAULT_COMMITMENT_LEVEL, MeteoraConfig, @@ -13,8 +6,8 @@ import { getQuoteMint, getQuoteDecimals, safeParseKeypairFromFile, - runSimulateTransaction, parseConfigFromCli, + seedLiquiditySingleBin, } from "."; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; import DLMM, { @@ -95,70 +88,26 @@ async function main() { const lockReleasePoint = new BN( config.singleBinSeedLiquidity.lockReleasePoint, ); - - console.log(`- Using seedAmount in lamports = ${seedAmount}`); - console.log(`- Using priceRounding = ${priceRounding}`); - console.log(`- Using price ${price}`); - console.log(`- Using operator ${operator}`); - console.log(`- Using positionOwner ${positionOwner}`); - console.log(`- Using feeOwner ${feeOwner}`); - console.log(`- Using lockReleasePoint ${lockReleasePoint}`); - console.log(`- Using seedTokenXToPositionOwner ${config.singleBinSeedLiquidity.seedTokenXToPositionOwner}`); - - if (!config.singleBinSeedLiquidity.seedTokenXToPositionOwner) { - console.log(`WARNING: You selected seedTokenXToPositionOwner = false, you should manually send 1 lamport of token X to the position owner account to prove ownership.`) - } - - const seedLiquidityIxs = await pair.seedLiquiditySingleBin( - wallet.publicKey, - basePublickey, - seedAmount, - price, - priceRounding == "up", + const seedTokenXToPositionOwner = + config.singleBinSeedLiquidity.seedTokenXToPositionOwner; + + await seedLiquiditySingleBin( + connection, + keypair, + baseKeypair, + operatorKeypair, positionOwner, feeOwner, - operator, + baseMint, + quoteMint, + seedAmount, + price, + priceRounding, lockReleasePoint, - config.singleBinSeedLiquidity.seedTokenXToPositionOwner, + seedTokenXToPositionOwner, + config.dryRun, + config.computeUnitPriceMicroLamports, ); - - const setCUPriceIx = ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: config.computeUnitPriceMicroLamports, - }); - - const { blockhash, lastValidBlockHeight } = - await connection.getLatestBlockhash("confirmed"); - - const tx = new Transaction({ - feePayer: keypair.publicKey, - blockhash, - lastValidBlockHeight, - }) - .add(setCUPriceIx) - .add(...seedLiquidityIxs); - - if (config.dryRun) { - console.log(`\n> Simulating seedLiquiditySingleBin transaction...`); - await runSimulateTransaction( - connection, - [wallet.payer, baseKeypair, operatorKeypair], - wallet.publicKey, - [tx], - ); - } else { - console.log(`>> Sending seedLiquiditySingleBin transaction...`); - const txHash = await sendAndConfirmTransaction(connection, tx, [ - wallet.payer, - baseKeypair, - operatorKeypair, - ]).catch((err) => { - console.error(err); - throw err; - }); - console.log( - `>>> SeedLiquiditySingleBin successfully with tx hash: ${txHash}`, - ); - } } main(); diff --git a/src/tests/artifacts/accounts/21bR3D4QR4GzopVco44PVMBXwHFpSYrbrdeNwdKk7umb.json b/src/tests/artifacts/accounts/21bR3D4QR4GzopVco44PVMBXwHFpSYrbrdeNwdKk7umb.json new file mode 100644 index 0000000..66f0208 --- /dev/null +++ b/src/tests/artifacts/accounts/21bR3D4QR4GzopVco44PVMBXwHFpSYrbrdeNwdKk7umb.json @@ -0,0 +1,14 @@ +{ + "pubkey": "21bR3D4QR4GzopVco44PVMBXwHFpSYrbrdeNwdKk7umb", + "account": { + "lamports": 1461600, + "data": [ + "AQAAAHQMqJZ15aFdp8y8JXLSbnFb6oH+RX0aw4TnXuK0Zp80o9b0ohtKAAAJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 82 + } +} diff --git a/src/tests/artifacts/accounts/3ifhD4Ywaa8aBZAaQSqYgN4Q1kaFArioLU8uumJMaqkE.json b/src/tests/artifacts/accounts/3ifhD4Ywaa8aBZAaQSqYgN4Q1kaFArioLU8uumJMaqkE.json new file mode 100644 index 0000000..6c48aab --- /dev/null +++ b/src/tests/artifacts/accounts/3ifhD4Ywaa8aBZAaQSqYgN4Q1kaFArioLU8uumJMaqkE.json @@ -0,0 +1,14 @@ +{ + "pubkey": "3ifhD4Ywaa8aBZAaQSqYgN4Q1kaFArioLU8uumJMaqkE", + "account": { + "lamports": 2039280, + "data": [ + "C2K6B09yLJ1BFPLY9woAxmACM3ub+QyHNlem0gHbTIB0DKiWdeWhXafMvCVy0m5xW+qB/kV9GsOE517itGafNEKUdUU2SgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 165 + } +} diff --git a/src/tests/artifacts/accounts/8p1VKP45hhqq5iZG5fNGoi7ucme8nFLeChoDWNy7rWFm.json b/src/tests/artifacts/accounts/8p1VKP45hhqq5iZG5fNGoi7ucme8nFLeChoDWNy7rWFm.json new file mode 100644 index 0000000..fa7a863 --- /dev/null +++ b/src/tests/artifacts/accounts/8p1VKP45hhqq5iZG5fNGoi7ucme8nFLeChoDWNy7rWFm.json @@ -0,0 +1,14 @@ +{ + "pubkey": "8p1VKP45hhqq5iZG5fNGoi7ucme8nFLeChoDWNy7rWFm", + "account": { + "lamports": 72161280, + "data": [ + "0wjoKwKYdXcB//+WMwh+30oAAChj8AdMM3Roo5e8WShXfxVlCjb4yJlA/629EjGvhJeTPzvrujjni68sYOdtu1cd+ST3e0v0NYlemnAjRAMkjKALYroHT3IsnUEU8tj3CgDGYAIze5v5DIc2V6bSAdtMgA8CmXbCnC6fK4mhwIMtU+WWHZwZTolmeUlGv1oKNlBKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVYjD2jmSzJ8iKVVvvczh3mcSUXHlvymkqFKQAjYExuoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9Wnf3iAjM1mNx9dLHZS4Ykd5wfgvHiWmW25O+KO+m5u2e56X0eKRLWzIx/+ghj0k//KEX4XTatRi0UFIIRWIIP3XuavZ8UEL0g+I/X213XzzqDeRogziiu/5GyCo9+PiqLrrAAAAAABpZxVmAAAAAOhswgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 10240 + } +} diff --git a/src/tests/artifacts/accounts/8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC.json b/src/tests/artifacts/accounts/8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC.json new file mode 100644 index 0000000..05d8772 --- /dev/null +++ b/src/tests/artifacts/accounts/8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC.json @@ -0,0 +1,14 @@ +{ + "pubkey": "8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC", + "account": { + "lamports": 19098240, + "data": [ + "2JJrXmhLtrELYroHT3IsnUEU8tj3CgDGYAIze5v5DIc2V6bSAdtMgCz0pOqMnBuYfUelv3scmcjjaofs/kslxbI+gD7+Ut+uC/49Oop/nKXYae0Dqcuh7qDJnWp2becvnXIEbPLV10KUupJ+KKToqJZx7UJwZ9wkAWDSf0aWriv0Dv97EAFr4P/98B0fAAAAAABYAgAAkXxLkU5TmaKSfT1u3GUbZ6yxn8JqR8F+QHm/uCY9TmU4AAAALAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//9QRgAAAAAAAHgCAAAAAAAAAMqaOwAAAAD2AAAAwDIL/Z2D7Ta752Tw4gzH+4TU3CHhPclFCUyyyPHhXrM9AAAA+gUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv+PTqKf5yl2GntA6nLoe6gyZ1qdm3nL51yBGzy1ddCvkx8APwJFxQAHxMAAAT3xv1GEmfOZWpZn+8+77eG7hSUZQYRGrRQHOtpsr0a//7/XSpe5WhcF+B87eW++YMA1BcOu+LZnwZMS7Be6Xs13n0AUIpxGRMAAIQDAAABAAAAiBMAAER6byKGDQAAAAAAAAAAAAD//////////0vbrPWevQAAQf756ax7EACDyc8yAQAAAAsEAAAAAAAA7DetX2kbAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAD//////////wAAAAAAAAAAkI1cukqxvvmHbzHoCpbW25/Jsuhuk7o3gXFWXUu9zhcAAAAAANwFAAABeAIAAAAAAABrD3QsimMAABAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 2616 + } +} diff --git a/src/tests/artifacts/accounts/AYX6kFvQRacdbC1eBaK5kgEPGjzjYYd1fyduHg55Z1ai.json b/src/tests/artifacts/accounts/AYX6kFvQRacdbC1eBaK5kgEPGjzjYYd1fyduHg55Z1ai.json new file mode 100644 index 0000000..1887d8c --- /dev/null +++ b/src/tests/artifacts/accounts/AYX6kFvQRacdbC1eBaK5kgEPGjzjYYd1fyduHg55Z1ai.json @@ -0,0 +1,14 @@ +{ + "pubkey": "AYX6kFvQRacdbC1eBaK5kgEPGjzjYYd1fyduHg55Z1ai", + "account": { + "lamports": 2039280, + "data": [ + "C2K6B09yLJ1BFPLY9woAxmACM3ub+QyHNlem0gHbTID1XEvqBOK7QWBl0Y2UbZnVUAPjJolE7qJ/8SYaXaaSvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 165 + } +} diff --git a/src/tests/artifacts/accounts/B2uEs9zjnz222hfUaUuRgesryUEYwy3JGuWe31sE9gsG.json b/src/tests/artifacts/accounts/B2uEs9zjnz222hfUaUuRgesryUEYwy3JGuWe31sE9gsG.json new file mode 100644 index 0000000..97c6501 --- /dev/null +++ b/src/tests/artifacts/accounts/B2uEs9zjnz222hfUaUuRgesryUEYwy3JGuWe31sE9gsG.json @@ -0,0 +1,14 @@ +{ + "pubkey": "B2uEs9zjnz222hfUaUuRgesryUEYwy3JGuWe31sE9gsG", + "account": { + "lamports": 1461600, + "data": [ + "AQAAAPVcS+oE4rtBYGXRjZRtmdVQA+MmiUTuon/xJhpdppK+MFmHf+NxAAAJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 82 + } +} diff --git a/src/tests/artifacts/accounts/FERjPVNEa7Udq8CEv68h6tPL46Tq7ieE49HrE2wea3XT.json b/src/tests/artifacts/accounts/FERjPVNEa7Udq8CEv68h6tPL46Tq7ieE49HrE2wea3XT.json new file mode 100644 index 0000000..95cd932 --- /dev/null +++ b/src/tests/artifacts/accounts/FERjPVNEa7Udq8CEv68h6tPL46Tq7ieE49HrE2wea3XT.json @@ -0,0 +1,14 @@ +{ + "pubkey": "FERjPVNEa7Udq8CEv68h6tPL46Tq7ieE49HrE2wea3XT", + "account": { + "lamports": 422161280, + "data": [ + "0wjoKwKYdXcB//rHFUKJbYwAAPYXzUUQr3DHYZIEmfj+l9WyMRpp4ZoXaDyiqqSL7Jp9eIeL5KxSmCOQf2sQDUviEFojkbwAx9pNoKXVeZ5zzpQGm4hX/quBhPtof2NGGMA12sQ53BrrO1WYoPAAAAAAAdhOEpDBmOeIug4RTwOCFGeWCNoo/NIKFjHvw5P7v2kqeBcOo0QuzyMKx3n81slCN5PKNoTElL/e23/oREPbvOO1g3zfGHL8KeVZFXjmADNHfED9MuhwdY3/p+rrvb3JTouBGLsh0D3x4BZ5ZIgnGgUpG7ceGD+tDq7XdR58W8hXD51iadxN19RkNHSFoiQV7JzmsCW5Kh0m1qOP33FJ72cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeLuCqy9Ft+d0Mi2NuL2fQAEjMeqjT1ZHz/oA6dlxNb8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9Wnf3iAjM1mNx9dLHZS4Ykd5wfgvHiWmW25O+KO+m5u2e56X0eKRLWzIx/+ghj0k//KEX4XTatRi0UFIIRWIIP3XuavZ8UEL0g+I/X213XzzqDeRogziiu/5GyCo9+PifM7npQAAAADvaXlmAAAAAOhswgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 10240 + } +} diff --git a/src/tests/artifacts/accounts/FZN7QZ8ZUUAxMPfxYEYkH3cXUASzH8EqA6B4tyCL8f1j.json b/src/tests/artifacts/accounts/FZN7QZ8ZUUAxMPfxYEYkH3cXUASzH8EqA6B4tyCL8f1j.json new file mode 100644 index 0000000..acab211 --- /dev/null +++ b/src/tests/artifacts/accounts/FZN7QZ8ZUUAxMPfxYEYkH3cXUASzH8EqA6B4tyCL8f1j.json @@ -0,0 +1,14 @@ +{ + "pubkey": "FZN7QZ8ZUUAxMPfxYEYkH3cXUASzH8EqA6B4tyCL8f1j", + "account": { + "lamports": 1461600, + "data": [ + "AQAAANN0IXa4nF4UyWt0iZhCHzgQHgnlxZMxsWi2Jc0nFbsmYW8Z0MiEAAAJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 82 + } +} diff --git a/src/tests/artifacts/accounts/GGG4DxkYa86g2v4KwtvR8Xu2tXEp1xd4BRC3yNnpve3g.json b/src/tests/artifacts/accounts/GGG4DxkYa86g2v4KwtvR8Xu2tXEp1xd4BRC3yNnpve3g.json new file mode 100644 index 0000000..17a598a --- /dev/null +++ b/src/tests/artifacts/accounts/GGG4DxkYa86g2v4KwtvR8Xu2tXEp1xd4BRC3yNnpve3g.json @@ -0,0 +1,14 @@ +{ + "pubkey": "GGG4DxkYa86g2v4KwtvR8Xu2tXEp1xd4BRC3yNnpve3g", + "account": { + "lamports": 2039280, + "data": [ + "DwKZdsKcLp8riaHAgy1T5ZYdnBlOiWZ5SUa/Wgo2UEr1XEvqBOK7QWBl0Y2UbZnVUAPjJolE7qJ/8SYaXaaSvmslxsPURwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 165 + } +} diff --git a/src/tests/artifacts/accounts/GYuyKhedWVsndqG5tyXwRiRSTxFKrLJysgedccqU7aH8.json b/src/tests/artifacts/accounts/GYuyKhedWVsndqG5tyXwRiRSTxFKrLJysgedccqU7aH8.json new file mode 100644 index 0000000..40aceaa --- /dev/null +++ b/src/tests/artifacts/accounts/GYuyKhedWVsndqG5tyXwRiRSTxFKrLJysgedccqU7aH8.json @@ -0,0 +1,14 @@ +{ + "pubkey": "GYuyKhedWVsndqG5tyXwRiRSTxFKrLJysgedccqU7aH8", + "account": { + "lamports": 2039280, + "data": [ + "BpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAH1XEvqBOK7QWBl0Y2UbZnVUAPjJolE7qJ/8SYaXaaSvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAADwHR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 165 + } +} diff --git a/src/tests/artifacts/accounts/HWnWnLvBzvSmXH5hnHJCFmuQbDTsX3Ba2w9CPE5zf4YD.json b/src/tests/artifacts/accounts/HWnWnLvBzvSmXH5hnHJCFmuQbDTsX3Ba2w9CPE5zf4YD.json new file mode 100644 index 0000000..6b5d1c6 --- /dev/null +++ b/src/tests/artifacts/accounts/HWnWnLvBzvSmXH5hnHJCFmuQbDTsX3Ba2w9CPE5zf4YD.json @@ -0,0 +1,14 @@ +{ + "pubkey": "HWnWnLvBzvSmXH5hnHJCFmuQbDTsX3Ba2w9CPE5zf4YD", + "account": { + "lamports": 2039280, + "data": [ + "2E4SkMGY54i6DhFPA4IUZ5YI2ij80goWMe/Dk/u/aSr1XEvqBOK7QWBl0Y2UbZnVUAPjJolE7qJ/8SYaXaaSvknJ11UhJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 165 + } +} diff --git a/src/tests/artifacts/accounts/HZeLxbZ9uHtSpwZC3LBr4Nubd14iHwz7bRSghRZf5VCG.json b/src/tests/artifacts/accounts/HZeLxbZ9uHtSpwZC3LBr4Nubd14iHwz7bRSghRZf5VCG.json new file mode 100644 index 0000000..bd71e28 --- /dev/null +++ b/src/tests/artifacts/accounts/HZeLxbZ9uHtSpwZC3LBr4Nubd14iHwz7bRSghRZf5VCG.json @@ -0,0 +1,14 @@ +{ + "pubkey": "HZeLxbZ9uHtSpwZC3LBr4Nubd14iHwz7bRSghRZf5VCG", + "account": { + "lamports": 63190651879008, + "data": [ + "BpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAHTdCF2uJxeFMlrdImYQh84EB4J5cWTMbFotiXNJxW7JnCgPLh4OQAAAAAAACCNsxRkCXS8q0WTSvTwSFWYFR/1oIvF8ggnbxxLTbFlAQEAAADwHR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 165 + } +} diff --git a/src/tests/artifacts/accounts/HcjZvfeSNJbNkfLD4eEcRBr96AD3w1GpmMppaeRZf7ur.json b/src/tests/artifacts/accounts/HcjZvfeSNJbNkfLD4eEcRBr96AD3w1GpmMppaeRZf7ur.json new file mode 100644 index 0000000..45f89f0 --- /dev/null +++ b/src/tests/artifacts/accounts/HcjZvfeSNJbNkfLD4eEcRBr96AD3w1GpmMppaeRZf7ur.json @@ -0,0 +1,14 @@ +{ + "pubkey": "HcjZvfeSNJbNkfLD4eEcRBr96AD3w1GpmMppaeRZf7ur", + "account": { + "lamports": 10544400, + "data": [ + "8ZptBBGxbbyVEYRW8gXtmhkmbvr1ljBJwCg7O1lO0GY/wq9t+0WELwabiFf+q4GE+2h/Y0YYwDXaxDncGus7VZig8AAAAAABC2K6B09yLJ1BFPLY9woAxmACM3ub+QyHNlem0gHbTIDTdCF2uJxeFMlrdImYQh84EB4J5cWTMbFotiXNJxW7JnQMqJZ15aFdp8y8JXLSbnFb6oH+RX0aw4TnXuK0Zp809VxL6gTiu0FgZdGNlG2Z1VAD4yaJRO6if/EmGl2mkr7iyAoX3ZxhiQdWvg2S0aX/QHbSgOIpGxar96X/R8wzZ/8B5ww0HL5IWSR3u78ipHfHCXiQu1VbX9zoAAALAGcI3SuNzDh1Wl2wSU8kQKe5KY+TENvCcoLa1/8WSdLxs6i/I0j0Weo18CFGxFjKyXY/SxEFclXL7jxORa3uw9uLKhs3ZAAAAAAAAABAQg8AAAAAAAAAAAAAAAAAQEIPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXA84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHoAwAAAAAAAAEAAAAAAAAAAQAAAAAAAAAJk0kSAAAAAAA1bnlmAAAAAAEEdc1kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 1387 + } +} diff --git a/src/tests/artifacts/accounts/mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So.json b/src/tests/artifacts/accounts/mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So.json new file mode 100644 index 0000000..f9cf296 --- /dev/null +++ b/src/tests/artifacts/accounts/mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So.json @@ -0,0 +1,14 @@ +{ + "pubkey": "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So", + "account": { + "lamports": 7897437308, + "data": [ + "AQAAACIoKeiXZ7IEPIbRtR8xNk5a2uuGH9Yuen9Gvk27xVykN1CCP6x7EAAJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "base64" + ], + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "executable": false, + "rentEpoch": 18446744073709551615, + "space": 82 + } +} diff --git a/src/tests/artifacts/dynamic_amm.so b/src/tests/artifacts/dynamic_amm.so new file mode 100644 index 0000000..835c087 Binary files /dev/null and b/src/tests/artifacts/dynamic_amm.so differ diff --git a/src/tests/artifacts/dynamic_vault.so b/src/tests/artifacts/dynamic_vault.so new file mode 100644 index 0000000..36e8e20 Binary files /dev/null and b/src/tests/artifacts/dynamic_vault.so differ diff --git a/src/tests/artifacts/lb_clmm.so b/src/tests/artifacts/lb_clmm.so new file mode 100644 index 0000000..3c9fbac Binary files /dev/null and b/src/tests/artifacts/lb_clmm.so differ diff --git a/src/tests/artifacts/metaplex.so b/src/tests/artifacts/metaplex.so new file mode 100644 index 0000000..fdc129a Binary files /dev/null and b/src/tests/artifacts/metaplex.so differ diff --git a/src/tests/create_pool.test.ts b/src/tests/create_pool.test.ts new file mode 100644 index 0000000..8eec75f --- /dev/null +++ b/src/tests/create_pool.test.ts @@ -0,0 +1,200 @@ +import { Connection, Keypair, PublicKey } from "@solana/web3.js"; +import fs from "fs"; +import { + DLMM_PROGRAM_IDS, + DYNAMIC_AMM_PROGRAM_IDS, + SOL_TOKEN_MINT, +} from "../libs/constants"; +import { + createPermissionlessDlmmPool, + createPermissionlessDynamicPool, +} from "../index"; +import { Wallet, web3 } from "@coral-xyz/anchor"; +import { MeteoraConfig } from "../libs/config"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + createMint, + getOrCreateAssociatedTokenAccount, + mintTo, +} from "@solana/spl-token"; + +const keypairFilePath = + "./src/tests/keys/localnet/admin-bossj3JvwiNK7pvjr149DqdtJxf2gdygbcmEPTkb2F1.json"; +const keypairBuffer = fs.readFileSync(keypairFilePath, "utf-8"); +const rpcUrl = "http://127.0.0.1:8899"; +const connection = new Connection("http://127.0.0.1:8899", "confirmed"); +const payerKeypair = Keypair.fromSecretKey( + new Uint8Array(JSON.parse(keypairBuffer)), +); +const payerWallet = new Wallet(payerKeypair); +const DLMM_PROGRAM_ID = new PublicKey(DLMM_PROGRAM_IDS["localhost"]); +const DYNAMIC_AMM_PROGRAM_ID = new PublicKey( + DYNAMIC_AMM_PROGRAM_IDS["localhost"], +); + +describe("Test Create Pool", () => { + const WEN_DECIMALS = 5; + const USDC_DECIMALS = 6; + const WEN_SUPPLY = 100_000_000; + const USDC_SUPPLY = 100_000_000; + + let WEN: PublicKey; + let USDC: PublicKey; + let userWEN: web3.PublicKey; + let userUSDC: web3.PublicKey; + + beforeAll(async () => { + WEN = await createMint( + connection, + payerKeypair, + payerKeypair.publicKey, + null, + WEN_DECIMALS, + Keypair.generate(), + undefined, + TOKEN_PROGRAM_ID, + ); + + USDC = await createMint( + connection, + payerKeypair, + payerKeypair.publicKey, + null, + USDC_DECIMALS, + Keypair.generate(), + undefined, + TOKEN_PROGRAM_ID, + ); + + const userWenInfo = await getOrCreateAssociatedTokenAccount( + connection, + payerKeypair, + WEN, + payerKeypair.publicKey, + false, + "confirmed", + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + userWEN = userWenInfo.address; + + const userUsdcInfo = await getOrCreateAssociatedTokenAccount( + connection, + payerKeypair, + USDC, + payerKeypair.publicKey, + false, + "confirmed", + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + userUSDC = userUsdcInfo.address; + + await mintTo( + connection, + payerKeypair, + WEN, + userWEN, + payerKeypair.publicKey, + WEN_SUPPLY * 10 ** WEN_DECIMALS, + [], + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ); + + await mintTo( + connection, + payerKeypair, + USDC, + userUSDC, + payerKeypair.publicKey, + USDC_SUPPLY * 10 ** USDC_DECIMALS, + [], + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ); + }); + + it("Should able to create Basic Dynamic AMM pool", async () => { + const config: MeteoraConfig = { + dryRun: false, + rpcUrl, + keypairFilePath, + computeUnitPriceMicroLamports: 100000, + createBaseToken: null, + baseMint: WEN.toString(), + quoteSymbol: "SOL", + dynamicAmm: { + baseAmount: 1000, + quoteAmount: 0.1, + tradeFeeNumerator: 2500, + activationType: "timestamp", + activationPoint: null, + hasAlphaVault: false, + }, + dlmm: null, + alphaVault: null, + lockLiquidity: null, + lfgSeedLiquidity: null, + singleBinSeedLiquidity: null, + }; + await createPermissionlessDynamicPool( + config, + connection, + payerWallet, + WEN, + new PublicKey(SOL_TOKEN_MINT), + { + programId: DYNAMIC_AMM_PROGRAM_ID, + }, + ); + }); + + it("Should be able to create Basic DLMM pool", async () => { + const config: MeteoraConfig = { + dryRun: false, + rpcUrl, + keypairFilePath, + computeUnitPriceMicroLamports: 100000, + createBaseToken: null, + baseMint: WEN.toString(), + quoteSymbol: "USDC", + dlmm: { + binStep: 200, + feeBps: 200, + initialPrice: 0.5, + activationType: "timestamp", + activationPoint: null, + priceRounding: "up", + hasAlphaVault: false, + }, + dynamicAmm: null, + alphaVault: null, + lockLiquidity: null, + lfgSeedLiquidity: null, + singleBinSeedLiquidity: null, + }; + await createPermissionlessDlmmPool( + config, + connection, + payerWallet, + WEN, + USDC, + { + cluster: "localhost", + programId: DLMM_PROGRAM_ID, + }, + ); + }); +}); diff --git a/src/tests/keys/localnet/admin-bossj3JvwiNK7pvjr149DqdtJxf2gdygbcmEPTkb2F1.json b/src/tests/keys/localnet/admin-bossj3JvwiNK7pvjr149DqdtJxf2gdygbcmEPTkb2F1.json new file mode 100644 index 0000000..19865f8 --- /dev/null +++ b/src/tests/keys/localnet/admin-bossj3JvwiNK7pvjr149DqdtJxf2gdygbcmEPTkb2F1.json @@ -0,0 +1,6 @@ +[ + 230, 207, 238, 109, 95, 154, 47, 93, 183, 250, 147, 189, 87, 15, 117, 184, 44, + 91, 94, 231, 126, 140, 238, 134, 29, 58, 8, 182, 88, 22, 113, 234, 8, 234, + 192, 109, 87, 125, 190, 55, 129, 173, 227, 8, 104, 201, 104, 13, 31, 178, 74, + 80, 54, 14, 77, 78, 226, 57, 47, 122, 166, 165, 57, 144 +] diff --git a/src/tests/keys/localnet/program-LbVRzDTvBDEcrthxfZ4RL6yiq3uZw8bS6MwtdY6UhFQ.json b/src/tests/keys/localnet/program-LbVRzDTvBDEcrthxfZ4RL6yiq3uZw8bS6MwtdY6UhFQ.json new file mode 100644 index 0000000..5533ab5 --- /dev/null +++ b/src/tests/keys/localnet/program-LbVRzDTvBDEcrthxfZ4RL6yiq3uZw8bS6MwtdY6UhFQ.json @@ -0,0 +1,6 @@ +[ + 237, 14, 0, 252, 204, 70, 136, 161, 168, 214, 209, 214, 165, 86, 118, 17, 167, + 67, 226, 89, 141, 50, 93, 57, 21, 217, 228, 215, 232, 31, 23, 19, 5, 5, 8, + 150, 192, 245, 85, 119, 65, 35, 231, 38, 247, 167, 119, 108, 169, 108, 10, + 152, 101, 233, 92, 168, 216, 177, 25, 12, 113, 154, 69, 75 +] diff --git a/src/tests/seed_liquidity_lfg.test.ts b/src/tests/seed_liquidity_lfg.test.ts new file mode 100644 index 0000000..03df345 --- /dev/null +++ b/src/tests/seed_liquidity_lfg.test.ts @@ -0,0 +1,275 @@ +import { Connection, Keypair, PublicKey } from "@solana/web3.js"; +import fs from "fs"; +import { DLMM_PROGRAM_IDS, DYNAMIC_AMM_PROGRAM_IDS } from "../libs/constants"; +import { + createPermissionlessDlmmPool, + createPermissionlessDynamicPool, + seedLiquidityLfg, + seedLiquiditySingleBin, +} from "../index"; +import { BN, Wallet, web3 } from "@coral-xyz/anchor"; +import { MeteoraConfig } from "../libs/config"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + createMint, + getOrCreateAssociatedTokenAccount, + mintTo, +} from "@solana/spl-token"; +import DLMM, { + deriveCustomizablePermissionlessLbPair, + getBinArrayLowerUpperBinId, + getPriceOfBinByBinId, + getTokenBalance, +} from "@meteora-ag/dlmm"; +import Decimal from "decimal.js"; +import babar from "babar"; + +const keypairFilePath = + "./src/tests/keys/localnet/admin-bossj3JvwiNK7pvjr149DqdtJxf2gdygbcmEPTkb2F1.json"; +const keypairBuffer = fs.readFileSync(keypairFilePath, "utf-8"); +const rpcUrl = "http://127.0.0.1:8899"; +const connection = new Connection("http://127.0.0.1:8899", "confirmed"); +const payerKeypair = Keypair.fromSecretKey( + new Uint8Array(JSON.parse(keypairBuffer)), +); +const payerWallet = new Wallet(payerKeypair); +const DLMM_PROGRAM_ID = new PublicKey(DLMM_PROGRAM_IDS["localhost"]); + +describe("Test Seed Liquidity Single Bin", () => { + const WEN_DECIMALS = 5; + const USDC_DECIMALS = 6; + const WEN_SUPPLY = 100_000_000; + const USDC_SUPPLY = 100_000_000; + const binStep = 200; + const feeBps = 200; + const initialPrice = 0.005; + + const baseKeypair = Keypair.generate(); + const positionOwner = Keypair.generate().publicKey; + const feeOwner = Keypair.generate().publicKey; + + let WEN: PublicKey; + let USDC: PublicKey; + let userWEN: web3.PublicKey; + let userUSDC: web3.PublicKey; + let poolKey: PublicKey; + + beforeAll(async () => { + WEN = await createMint( + connection, + payerKeypair, + payerKeypair.publicKey, + null, + WEN_DECIMALS, + Keypair.generate(), + undefined, + TOKEN_PROGRAM_ID, + ); + + USDC = await createMint( + connection, + payerKeypair, + payerKeypair.publicKey, + null, + USDC_DECIMALS, + Keypair.generate(), + undefined, + TOKEN_PROGRAM_ID, + ); + + const userWenInfo = await getOrCreateAssociatedTokenAccount( + connection, + payerKeypair, + WEN, + payerKeypair.publicKey, + false, + "confirmed", + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + userWEN = userWenInfo.address; + + const userUsdcInfo = await getOrCreateAssociatedTokenAccount( + connection, + payerKeypair, + USDC, + payerKeypair.publicKey, + false, + "confirmed", + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + userUSDC = userUsdcInfo.address; + + await mintTo( + connection, + payerKeypair, + WEN, + userWEN, + payerKeypair.publicKey, + WEN_SUPPLY * 10 ** WEN_DECIMALS, + [], + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ); + + await mintTo( + connection, + payerKeypair, + USDC, + userUSDC, + payerKeypair.publicKey, + USDC_SUPPLY * 10 ** USDC_DECIMALS, + [], + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ); + + const slot = await connection.getSlot(); + const activationPoint = new BN(slot).add(new BN(100)); + + const config: MeteoraConfig = { + dryRun: false, + rpcUrl, + keypairFilePath, + computeUnitPriceMicroLamports: 100000, + createBaseToken: null, + baseMint: WEN.toString(), + quoteSymbol: "USDC", + dlmm: { + binStep, + feeBps, + initialPrice, + activationType: "slot", + activationPoint, + priceRounding: "up", + hasAlphaVault: false, + }, + dynamicAmm: null, + alphaVault: null, + lockLiquidity: null, + lfgSeedLiquidity: null, + singleBinSeedLiquidity: null, + }; + + //create DLMM pool + await createPermissionlessDlmmPool( + config, + connection, + payerWallet, + WEN, + USDC, + { + cluster: "localhost", + programId: DLMM_PROGRAM_ID, + }, + ); + + // send SOL to wallets + const payerBalance = await connection.getBalance(payerKeypair.publicKey); + console.log(`Payer balance ${payerBalance} lamports`); + + const [poolKeyString] = deriveCustomizablePermissionlessLbPair( + WEN, + USDC, + new PublicKey(DLMM_PROGRAM_ID), + ); + poolKey = new PublicKey(poolKeyString); + }); + + it("Should able to seed liquidity LFG", async () => { + const seedAmount = new BN(200_000 * 10 ** WEN_DECIMALS); + const lockReleasePoint = new BN(0); + const seedTokenXToPositionOwner = true; + const dryRun = false; + const computeUnitPriceMicroLamports = 100000; + const curvature = 0.6; + const minPrice = 0.005; + const maxPrice = 0.1; + + const minPricePerLamport = DLMM.getPricePerLamport( + WEN_DECIMALS, + USDC_DECIMALS, + minPrice, + ); + const maxPricePerLamport = DLMM.getPricePerLamport( + WEN_DECIMALS, + USDC_DECIMALS, + maxPrice, + ); + + await seedLiquidityLfg( + connection, + payerKeypair, + baseKeypair, + payerKeypair, + positionOwner, + feeOwner, + WEN, + USDC, + seedAmount, + curvature, + minPricePerLamport, + maxPricePerLamport, + lockReleasePoint, + seedTokenXToPositionOwner, + dryRun, + computeUnitPriceMicroLamports, + { + cluster: "localhost", + programId: DLMM_PROGRAM_ID, + }, + ); + + // WEN balance after = WEN supply - seed amount - 1 lamport + const wenBalanceAfter = await getTokenBalance(connection, userWEN); + const expectedBalanceAfter = new BN( + WEN_SUPPLY * 10 ** WEN_DECIMALS - 1, + ).sub(seedAmount); + expect(wenBalanceAfter.toString()).toEqual(expectedBalanceAfter.toString()); + + const pair = await DLMM.create(connection, poolKey, { + cluster: "localhost", + programId: DLMM_PROGRAM_ID, + }); + + await pair.refetchStates(); + + let binArrays = await pair.getBinArrays(); + binArrays = binArrays.sort((a, b) => a.account.index.cmp(b.account.index)); + + const binLiquidities = binArrays + .map((ba) => { + const [lowerBinId, upperBinId] = getBinArrayLowerUpperBinId( + ba.account.index, + ); + const binWithLiquidity: [number, number][] = []; + for (let i = lowerBinId.toNumber(); i <= upperBinId.toNumber(); i++) { + const binAmountX = ba.account.bins[i - lowerBinId.toNumber()].amountX; + const binPrice = getPriceOfBinByBinId(i, pair.lbPair.binStep); + const liquidity = new Decimal(binAmountX.toString()) + .mul(binPrice) + .floor() + .toNumber(); + binWithLiquidity.push([i, liquidity]); + } + return binWithLiquidity; + }) + .flat(); + + console.log(binLiquidities.filter((b) => b[1] > 0).reverse()); + console.log(binLiquidities.filter((b) => b[1] > 0)); + console.log(babar(binLiquidities)); + }); +}); diff --git a/src/tests/seed_liquidity_single_bin.test.ts b/src/tests/seed_liquidity_single_bin.test.ts new file mode 100644 index 0000000..73b360b --- /dev/null +++ b/src/tests/seed_liquidity_single_bin.test.ts @@ -0,0 +1,214 @@ +import { Connection, Keypair, PublicKey } from "@solana/web3.js"; +import fs from "fs"; +import { DLMM_PROGRAM_IDS, DYNAMIC_AMM_PROGRAM_IDS } from "../libs/constants"; +import { + createPermissionlessDlmmPool, + createPermissionlessDynamicPool, + seedLiquiditySingleBin, +} from "../index"; +import { BN, Wallet, web3 } from "@coral-xyz/anchor"; +import { MeteoraConfig } from "../libs/config"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + createMint, + getOrCreateAssociatedTokenAccount, + mintTo, +} from "@solana/spl-token"; +import { + deriveCustomizablePermissionlessLbPair, + getTokenBalance, +} from "@meteora-ag/dlmm"; + +const keypairFilePath = + "./src/tests/keys/localnet/admin-bossj3JvwiNK7pvjr149DqdtJxf2gdygbcmEPTkb2F1.json"; +const keypairBuffer = fs.readFileSync(keypairFilePath, "utf-8"); +const rpcUrl = "http://127.0.0.1:8899"; +const connection = new Connection("http://127.0.0.1:8899", "confirmed"); +const payerKeypair = Keypair.fromSecretKey( + new Uint8Array(JSON.parse(keypairBuffer)), +); +const payerWallet = new Wallet(payerKeypair); +const DLMM_PROGRAM_ID = new PublicKey(DLMM_PROGRAM_IDS["localhost"]); + +describe("Test Seed Liquidity Single Bin", () => { + const WEN_DECIMALS = 5; + const USDC_DECIMALS = 6; + const WEN_SUPPLY = 100_000_000; + const USDC_SUPPLY = 100_000_000; + const binStep = 200; + const feeBps = 200; + const initialPrice = 0.005; + const baseKeypair = Keypair.generate(); + const positionOwner = Keypair.generate().publicKey; + const feeOwner = Keypair.generate().publicKey; + + let WEN: PublicKey; + let USDC: PublicKey; + let userWEN: web3.PublicKey; + let userUSDC: web3.PublicKey; + let poolKey: PublicKey; + + beforeAll(async () => { + WEN = await createMint( + connection, + payerKeypair, + payerKeypair.publicKey, + null, + WEN_DECIMALS, + Keypair.generate(), + undefined, + TOKEN_PROGRAM_ID, + ); + + USDC = await createMint( + connection, + payerKeypair, + payerKeypair.publicKey, + null, + USDC_DECIMALS, + Keypair.generate(), + undefined, + TOKEN_PROGRAM_ID, + ); + + const userWenInfo = await getOrCreateAssociatedTokenAccount( + connection, + payerKeypair, + WEN, + payerKeypair.publicKey, + false, + "confirmed", + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + userWEN = userWenInfo.address; + + const userUsdcInfo = await getOrCreateAssociatedTokenAccount( + connection, + payerKeypair, + USDC, + payerKeypair.publicKey, + false, + "confirmed", + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + userUSDC = userUsdcInfo.address; + + await mintTo( + connection, + payerKeypair, + WEN, + userWEN, + payerKeypair.publicKey, + WEN_SUPPLY * 10 ** WEN_DECIMALS, + [], + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ); + + await mintTo( + connection, + payerKeypair, + USDC, + userUSDC, + payerKeypair.publicKey, + USDC_SUPPLY * 10 ** USDC_DECIMALS, + [], + { + commitment: "confirmed", + }, + TOKEN_PROGRAM_ID, + ); + + const slot = await connection.getSlot(); + const activationPoint = new BN(slot).add(new BN(100)); + + const config: MeteoraConfig = { + dryRun: false, + rpcUrl, + keypairFilePath, + computeUnitPriceMicroLamports: 100000, + createBaseToken: null, + baseMint: WEN.toString(), + quoteSymbol: "USDC", + dlmm: { + binStep, + feeBps, + initialPrice, + activationType: "slot", + activationPoint, + priceRounding: "up", + hasAlphaVault: false, + }, + dynamicAmm: null, + alphaVault: null, + lockLiquidity: null, + lfgSeedLiquidity: null, + singleBinSeedLiquidity: null, + }; + + //create DLMM pool + await createPermissionlessDlmmPool( + config, + connection, + payerWallet, + WEN, + USDC, + { + cluster: "localhost", + programId: DLMM_PROGRAM_ID, + }, + ); + + // send SOL to wallets + const payerBalance = await connection.getBalance(payerKeypair.publicKey); + console.log(`Payer balance ${payerBalance} lamports`); + + const [poolKeyString] = deriveCustomizablePermissionlessLbPair( + WEN, + USDC, + new PublicKey(DLMM_PROGRAM_ID), + ); + poolKey = new PublicKey(poolKeyString); + }); + + it("Should able to seed liquidity single bin", async () => { + const seedAmount = new BN(1000 * 10 ** WEN_DECIMALS); + const priceRounding = "up"; + const lockReleasePoint = new BN(0); + const seedTokenXToPositionOwner = true; + const dryRun = false; + const computeUnitPriceMicroLamports = 100000; + + await seedLiquiditySingleBin( + connection, + payerKeypair, + baseKeypair, + payerKeypair, + positionOwner, + feeOwner, + WEN, + USDC, + seedAmount, + initialPrice, + priceRounding, + lockReleasePoint, + seedTokenXToPositionOwner, + dryRun, + computeUnitPriceMicroLamports, + { + cluster: "localhost", + }, + ); + }); +}); diff --git a/src/tests/utils.ts b/src/tests/utils.ts new file mode 100644 index 0000000..378e8e2 --- /dev/null +++ b/src/tests/utils.ts @@ -0,0 +1,57 @@ +import { getAssociatedTokenAccount } from "@mercurial-finance/dynamic-amm-sdk/dist/cjs/src/amm/utils"; +import { wrapSOLInstruction } from "@meteora-ag/dlmm"; +import { NATIVE_MINT } from "@solana/spl-token"; +import { + Connection, + Keypair, + LAMPORTS_PER_SOL, + PublicKey, + Transaction, +} from "@solana/web3.js"; +import { BN } from "bn.js"; + +export const wrapSol = async ( + connection: Connection, + amount: BN, + user: Keypair, +) => { + const userAta = getAssociatedTokenAccount(NATIVE_MINT, user.publicKey); + const wrapSolIx = wrapSOLInstruction( + user.publicKey, + userAta, + BigInt(amount.toString()), + ); + const latestBlockHash = await connection.getLatestBlockhash(); + const tx = new Transaction({ + feePayer: user.publicKey, + ...latestBlockHash, + }).add(...wrapSolIx); + tx.sign(user); + const txHash = await connection.sendRawTransaction(tx.serialize()); + await connection.confirmTransaction(txHash, "finalized"); +}; + +export const airDropSol = async ( + connection: Connection, + publicKey: PublicKey, + amount = 1, +) => { + try { + const airdropSignature = await connection.requestAirdrop( + publicKey, + amount * LAMPORTS_PER_SOL, + ); + const latestBlockHash = await connection.getLatestBlockhash(); + await connection.confirmTransaction( + { + blockhash: latestBlockHash.blockhash, + lastValidBlockHeight: latestBlockHash.lastValidBlockHeight, + signature: airdropSignature, + }, + connection.commitment, + ); + } catch (error) { + console.error(error); + throw error; + } +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d939310 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "dist", + "types": [ + "jest", + "bun" + ], + "typeRoots": [ + "./node_modules/@types" + ], + "module": "commonjs", + "target": "ESNext", + "moduleResolution": "node", + "esModuleInterop": true, + "resolveJsonModule": true, + "noImplicitAny": false, + "skipLibCheck": true + } +} \ No newline at end of file