UNPKG

yamaswap-sdk

Version:
1,216 lines (1,208 loc) 33.2 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/core/client.ts var client_exports = {}; __export(client_exports, { DexClient: () => DexClient }); module.exports = __toCommonJS(client_exports); var anchor2 = __toESM(require("@coral-xyz/anchor")); var import_web33 = require("@solana/web3.js"); // src/idl/iswap.json var iswap_default = { address: "dXgZyuguvD2m5G5385XkdokZBryUoSE6LbNJeWiFiN5", metadata: { name: "iswap", version: "0.1.0", spec: "0.1.0", description: "Created with Anchor" }, instructions: [ { name: "close", discriminator: [ 98, 165, 201, 177, 108, 65, 206, 96 ], accounts: [ { name: "payer", writable: true, signer: true }, { name: "iswap", writable: true } ], args: [] }, { name: "decrement", discriminator: [ 106, 227, 168, 59, 248, 27, 150, 101 ], accounts: [ { name: "iswap", writable: true } ], args: [] }, { name: "etf_burn", discriminator: [ 185, 203, 114, 195, 129, 70, 235, 23 ], accounts: [ { name: "etf_token_info", pda: { seeds: [ { kind: "const", value: [ 101, 116, 102, 95, 116, 111, 107, 101, 110, 95, 118, 51 ] }, { kind: "account", path: "etf_token_mint_account" } ] } }, { name: "etf_token_mint_account", writable: true }, { name: "etf_token_ata", writable: true, pda: { seeds: [ { kind: "account", path: "authority" }, { kind: "const", value: [ 6, 221, 246, 225, 215, 101, 161, 147, 217, 203, 225, 70, 206, 235, 121, 172, 28, 180, 133, 237, 95, 91, 55, 145, 58, 140, 245, 133, 126, 255, 0, 169 ] }, { kind: "account", path: "etf_token_mint_account" } ], program: { kind: "const", value: [ 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, 219, 233, 248, 89 ] } } }, { name: "authority", writable: true, signer: true }, { name: "token_program", address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }, { name: "associated_token_program", address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" }, { name: "system_program", address: "11111111111111111111111111111111" } ], args: [ { name: "lamports", type: "u64" } ] }, { name: "etf_create", discriminator: [ 0, 81, 62, 85, 242, 37, 4, 245 ], accounts: [ { name: "etf_token_info", writable: true, pda: { seeds: [ { kind: "const", value: [ 101, 116, 102, 95, 116, 111, 107, 101, 110, 95, 118, 51 ] }, { kind: "account", path: "etf_token_mint_account" } ] } }, { name: "metadata_account", writable: true, pda: { seeds: [ { kind: "const", value: [ 109, 101, 116, 97, 100, 97, 116, 97 ] }, { kind: "account", path: "token_metadata_program" }, { kind: "account", path: "etf_token_mint_account" } ], program: { kind: "account", path: "token_metadata_program" } } }, { name: "etf_token_mint_account", writable: true, pda: { seeds: [ { kind: "const", value: [ 101, 116, 102, 95, 116, 111, 107, 101, 110, 95, 118, 51 ] }, { kind: "arg", path: "args.symbol" } ] } }, { name: "rent", address: "SysvarRent111111111111111111111111111111111" }, { name: "authority", writable: true, signer: true }, { name: "token_metadata_program", address: "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" }, { name: "token_program", address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }, { name: "system_program", address: "11111111111111111111111111111111" } ], args: [ { name: "args", type: { defined: { name: "EtfTokenArgs" } } } ] }, { name: "etf_mint", discriminator: [ 253, 102, 37, 173, 154, 46, 245, 224 ], accounts: [ { name: "etf_token_info", pda: { seeds: [ { kind: "const", value: [ 101, 116, 102, 95, 116, 111, 107, 101, 110, 95, 118, 51 ] }, { kind: "account", path: "etf_token_mint_account" } ] } }, { name: "etf_token_mint_account", writable: true }, { name: "etf_token_ata", writable: true, pda: { seeds: [ { kind: "account", path: "authority" }, { kind: "const", value: [ 6, 221, 246, 225, 215, 101, 161, 147, 217, 203, 225, 70, 206, 235, 121, 172, 28, 180, 133, 237, 95, 91, 55, 145, 58, 140, 245, 133, 126, 255, 0, 169 ] }, { kind: "account", path: "etf_token_mint_account" } ], program: { kind: "const", value: [ 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218, 255, 16, 132, 4, 142, 123, 216, 219, 233, 248, 89 ] } } }, { name: "authority", writable: true, signer: true }, { name: "token_program", address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }, { name: "associated_token_program", address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" }, { name: "system_program", address: "11111111111111111111111111111111" } ], args: [ { name: "lamports", type: "u64" } ] }, { name: "increment", discriminator: [ 11, 18, 104, 9, 104, 174, 59, 33 ], accounts: [ { name: "iswap", writable: true } ], args: [] }, { name: "initialize", discriminator: [ 175, 175, 109, 31, 13, 152, 155, 237 ], accounts: [ { name: "payer", writable: true, signer: true }, { name: "iswap", writable: true, signer: true }, { name: "system_program", address: "11111111111111111111111111111111" } ], args: [] }, { name: "set", discriminator: [ 198, 51, 53, 241, 116, 29, 126, 194 ], accounts: [ { name: "iswap", writable: true } ], args: [ { name: "value", type: "u8" } ] } ], accounts: [ { name: "EtfToken", discriminator: [ 187, 90, 26, 73, 137, 112, 105, 60 ] }, { name: "Iswap", discriminator: [ 228, 5, 227, 43, 35, 87, 170, 87 ] } ], errors: [ { code: 6e3, name: "InvalidAccounts", msg: "Lack of necessary accounts" }, { code: 6001, name: "InsufficientBalance", msg: "Insufficient balance" } ], types: [ { name: "EtfAsset", type: { kind: "struct", fields: [ { name: "token", type: "pubkey" }, { name: "weight", type: "u16" } ] } }, { name: "EtfToken", type: { kind: "struct", fields: [ { name: "mint_account", type: "pubkey" }, { name: "creator", type: "pubkey" }, { name: "create_at", type: "i64" }, { name: "description", type: "string" }, { name: "assets", type: { vec: { defined: { name: "EtfAsset" } } } } ] } }, { name: "EtfTokenArgs", type: { kind: "struct", fields: [ { name: "name", type: "string" }, { name: "symbol", type: "string" }, { name: "description", type: "string" }, { name: "url", type: "string" }, { name: "assets", type: { vec: { defined: { name: "EtfAsset" } } } } ] } }, { name: "Iswap", type: { kind: "struct", fields: [ { name: "count", type: "u8" } ] } } ] }; // src/utils/getAddress.ts var anchor = __toESM(require("@coral-xyz/anchor")); var import_web3 = require("@solana/web3.js"); function deriveEtfTokenMintAccount(program, seeds) { const buffers = seeds.map((part) => { if (typeof part === "string") { return Buffer.from(part); } else if (part instanceof import_web3.PublicKey) { return part.toBuffer(); } else { throw new Error(`Unsupported type in symbolParts: ${typeof part}`); } }); return anchor.web3.PublicKey.findProgramAddressSync( [ ...buffers ], program.programId ); } // src/utils/checks.ts var import_web32 = require("@solana/web3.js"); var import_spl_token = require("@solana/spl-token"); // src/utils/error.ts var DexSDKError = class _DexSDKError extends Error { constructor(message) { super(message); this.name = "DexSDKError"; Object.setPrototypeOf(this, _DexSDKError.prototype); } }; var ETFNotExistsError = class _ETFNotExistsError extends DexSDKError { constructor(message) { super(message); this.name = "ETFNotExistsError"; Object.setPrototypeOf(this, _ETFNotExistsError.prototype); } }; var ETFInsufficientBalanceError = class _ETFInsufficientBalanceError extends DexSDKError { constructor(message) { super(message); this.name = "ETFInsufficientBalanceError"; Object.setPrototypeOf(this, _ETFInsufficientBalanceError.prototype); } }; var ETFInvalidParamsError = class _ETFInvalidParamsError extends DexSDKError { constructor(message) { super(message); this.name = "ETFInvalidParamsError"; Object.setPrototypeOf(this, _ETFInvalidParamsError.prototype); } }; // src/utils/checks.ts async function checkATAExists(client, tokens, owner, isContract = false) { for (const token of tokens) { const tokenAccountAddress = (0, import_spl_token.getAssociatedTokenAddressSync)(token, owner, isContract); try { return await (0, import_spl_token.getAccount)(client.connection, tokenAccountAddress); } catch (e) { if (e instanceof import_spl_token.TokenAccountNotFoundError) { throw new import_spl_token.TokenAccountNotFoundError(`\u8D26\u6237 ${tokenAccountAddress.toBase58()} \u4E0D\u5B58\u5728`); } throw e; } } return void 0; } async function checkBalance(client, etfAddress, owner, requiredAmount) { const balance = await client.queries.getETFBalance(etfAddress, owner); if (balance < requiredAmount) { throw new ETFInsufficientBalanceError(`\u7528\u6237\u4EE3\u5E01\u8D26\u6237\u4F59\u989D\u4E0D\u8DB3\uFF0C\u6240\u9700: ${requiredAmount}, \u5F53\u524D: ${balance}`); } } async function checkETFExists(client, etfAddress) { const etfInfo = await client.queries.getETFInfo(etfAddress); if (typeof etfInfo === "string") { throw new ETFNotExistsError(etfInfo); } return etfInfo; } function validateETFCreateParams(params) { const { assets } = params; if (!assets?.length) { throw new ETFInvalidParamsError("ETF\u7EC4\u6210\u90E8\u5206\u4E0D\u80FD\u4E3A\u7A7A"); } const totalWeight = assets.reduce((sum, item) => sum + item.weight, 0); if (totalWeight !== 100) { throw new ETFInvalidParamsError(`ETF\u7EC4\u6210\u90E8\u5206\u6743\u91CD\u4E4B\u548C\u5FC5\u987B\u4E3A100\uFF0C\u5F53\u524D\u4E3A: ${totalWeight}`); } const tokenSet = /* @__PURE__ */ new Set(); for (const item of assets) { let tokenKey; try { tokenKey = new import_web32.PublicKey(item.token); } catch (e) { throw new ETFInvalidParamsError(`\u65E0\u6548\u7684\u4EE3\u5E01\u5730\u5740: ${item.token}`); } if (tokenSet.has(tokenKey.toBase58())) { throw new ETFInvalidParamsError(`\u5B58\u5728\u91CD\u590D\u7684\u4EE3\u5E01\u5730\u5740: ${item.token}`); } tokenSet.add(tokenKey.toBase58()); if (item.weight <= 0) { throw new ETFInvalidParamsError(`\u4EE3\u5E01\u6743\u91CD\u5FC5\u987B\u5927\u4E8E0: ${item.token}`); } } } function validateMintETFParams(params) { if (params.lamports <= 0) { throw new ETFInvalidParamsError("\u8D2D\u4E70\u4EFD\u989D\u5FC5\u987B\u5927\u4E8E0"); } if (params.lamports > 1e15) { throw new ETFInvalidParamsError("\u8D2D\u4E70\u4EFD\u989D\u8D85\u8FC7\u6700\u5927\u9650\u5236\uFF081000000 SOL\uFF09"); } } // src/utils/queries.ts var ETFQueries = class { constructor(client) { this.client = client; } async getETFInfo(etfAddress) { const [etfInfoAddress] = deriveEtfTokenMintAccount(this.client.program, ["etf_token_v3", etfAddress]); try { const etfInfo = await this.client.program.account.etfToken.fetch(etfInfoAddress); return { etfCoreAddress: etfInfoAddress, creator: etfInfo.creator.toString(), description: etfInfo.description, assets: etfInfo.assets.map((item) => ({ token: item.token, weight: item.weight })) }; } catch (error) { if (error instanceof Error && error.message.includes("Account does not exist")) { return `ETF\u4E0D\u5B58\u5728: ${etfAddress}`; } throw error; } } async getETFBalance(etfAddress, ownerAddress) { try { const tokenAccount = await checkATAExists(this.client, [etfAddress], ownerAddress); return Number(tokenAccount?.amount); } catch (error) { if (error instanceof Error && error.message.includes("Account does not exist")) { return 0; } throw error; } } }; // src/core/client.ts var import_spl_token2 = require("@solana/spl-token"); var DexClient = class { constructor(connection, wallet, provider) { this.connection = connection; this.connection = connection; this.wallet = wallet; this.program = new anchor2.Program(iswap_default, provider); this.queries = new ETFQueries(this); } async createETF(params) { try { validateETFCreateParams(params); const { name, symbol, description, url, assets } = params; const [etfAddress] = deriveEtfTokenMintAccount(this.program, ["etf_token_v3", symbol]); const [etfCoreAddress] = deriveEtfTokenMintAccount(this.program, ["etf_token_v3", etfAddress]); const etfInfo = await this.queries.getETFInfo(etfAddress); if (typeof etfInfo !== "string") { return { success: false, error: "etf already exists", data: { etfAddress, etfCoreAddress: etfInfo.etfCoreAddress, symbol, description: etfInfo.description, creator: etfInfo.creator, assets: etfInfo.assets } }; } const latestBlockhash = await this.connection.getLatestBlockhash("confirmed"); console.log("get the latest blockhash:", latestBlockhash.blockhash); let tx = new import_web33.Transaction(); for (const { token } of assets) { const tokenKey = new import_web33.PublicKey(token); const address = (0, import_spl_token2.getAssociatedTokenAddressSync)(tokenKey, etfCoreAddress, true); try { await (0, import_spl_token2.getAccount)(this.connection, address); } catch (e) { if (e instanceof import_spl_token2.TokenAccountNotFoundError) { tx.add( (0, import_spl_token2.createAssociatedTokenAccountInstruction)( this.wallet.publicKey, address, etfCoreAddress, tokenKey ) ); } } } const ix = await this.program.methods.etfCreate({ name, symbol, description, url, assets: assets.map((c) => ({ token: new import_web33.PublicKey(c.token), weight: c.weight })) }).transaction(); tx.add(ix); tx.recentBlockhash = latestBlockhash.blockhash; tx.feePayer = this.wallet.publicKey; console.log("transaction created:", { recentBlockhash: tx.recentBlockhash, feePayer: tx.feePayer.toBase58(), instructions: tx.instructions.length }); try { const signedTx = await this.wallet.signTransaction(tx); console.log("transaction signed"); const txid = await this.connection.sendRawTransaction(signedTx.serialize(), { skipPreflight: false, preflightCommitment: "confirmed", maxRetries: 5 }); console.log("transaction sent, signed:", txid); const confirmation = await this.connection.confirmTransaction({ signature: txid, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight }, "confirmed"); if (confirmation.value.err) { throw new Error(`transaction confirm failed: ${JSON.stringify(confirmation.value.err)}`); } console.log("transaction confirmed"); const confirmedEtfInfo = await this.queries.getETFInfo(etfAddress); if (typeof confirmedEtfInfo === "string") { throw new Error("etf create success but not found etf info"); } return { success: true, txid, data: { etfAddress, etfCoreAddress, symbol, name, description, creator: confirmedEtfInfo.creator, assets: confirmedEtfInfo.assets } }; } catch (error) { console.error("createETF error:", error); throw error; } } catch (error) { console.error("Error details:", error); if (error instanceof Error) { console.error("Error stack:", error.stack); } return { success: false, txid: "", error: error instanceof Error ? error.message : "meet an unknown error when create ETF" }; } } async purchaseETF(params) { try { const etfAddress = new import_web33.PublicKey(params.etfAddress); validateMintETFParams(params); const etfInfo = await checkETFExists(this, etfAddress); await checkATAExists(this, etfInfo.assets.map((item) => item.token), this.wallet.publicKey); await checkATAExists(this, etfInfo.assets.map((item) => item.token), etfInfo.etfCoreAddress, true); for (const item of etfInfo.assets) { const requiredAmount = params.lamports * item.weight / 1e4; await checkBalance(this, item.token, this.wallet.publicKey, requiredAmount); } const latestBlockhash = await this.connection.getLatestBlockhash("confirmed"); console.log("get the latest blockhash:", latestBlockhash.blockhash); const remainingAccounts = etfInfo.assets.flatMap((item) => { const userATA = (0, import_spl_token2.getAssociatedTokenAddressSync)(item.token, this.wallet.publicKey); const contractATA = (0, import_spl_token2.getAssociatedTokenAddressSync)(item.token, etfInfo.etfCoreAddress, true); return [userATA, contractATA]; }); const ix = await this.program.methods.etfMint(new anchor2.BN(params.lamports)).accounts({ etfTokenMintAccount: params.etfAddress, authority: this.wallet.publicKey }).remainingAccounts( remainingAccounts.map((item) => ({ pubkey: item, isSigner: false, isWritable: true })) ).transaction(); const modifyComputeUnits = anchor2.web3.ComputeBudgetProgram.setComputeUnitLimit({ units: 4e5 }); let tx = new import_web33.Transaction().add(ix).add(modifyComputeUnits); tx.recentBlockhash = latestBlockhash.blockhash; tx.feePayer = this.wallet.publicKey; console.log("transaction created:", { recentBlockhash: tx.recentBlockhash, feePayer: tx.feePayer.toBase58(), instructions: tx.instructions.length }); try { const signedTx = await this.wallet.signTransaction(tx); console.log("transaction signed"); const txid = await this.connection.sendRawTransaction(signedTx.serialize(), { skipPreflight: false, preflightCommitment: "confirmed", maxRetries: 5 }); console.log("transaction sent, signed:", txid); const confirmation = await this.connection.confirmTransaction({ signature: txid, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight }, "confirmed"); if (confirmation.value.err) { throw new Error(`transaction confirm failed: ${JSON.stringify(confirmation.value.err)}`); } console.log("transaction confirmed"); const mintTokenataAccount = (0, import_spl_token2.getAssociatedTokenAddressSync)(etfAddress, this.wallet.publicKey); const balance = await this.queries.getETFBalance(etfAddress, this.wallet.publicKey); return { success: true, txid, data: { mintTokenataAccount, balance, etfAddress: params.etfAddress } }; } catch (error) { console.error("purchaseETF error:", error); throw error; } } catch (error) { console.error("Error in purchaseETF:"); console.error("Error details:", error); if (error instanceof Error) { console.error("Error name:", error.name); console.error("Error message:", error.message); console.error("Error stack:", error.stack); } if (error instanceof anchor2.web3.SendTransactionError) { console.error("Transaction error logs:", error.logs); } return { success: false, txid: "", error: error instanceof Error ? error.message : "Mint ETF Token \u65F6\u53D1\u751F\u672A\u77E5\u9519\u8BEF" }; } } async burnETF(params) { try { const etfAddress = new import_web33.PublicKey(params.etfAddress); const etfInfo = await checkETFExists(this, etfAddress); const tokensToCheck = [...etfInfo.assets.map((item) => item.token), etfAddress]; await checkATAExists(this, tokensToCheck, this.wallet.publicKey); await checkBalance(this, etfAddress, this.wallet.publicKey, params.lamports); const latestBlockhash = await this.connection.getLatestBlockhash("confirmed"); console.log("get the latest blockhash:", latestBlockhash.blockhash); const remainingAccounts = etfInfo.assets.flatMap((item) => { const userATA = (0, import_spl_token2.getAssociatedTokenAddressSync)(item.token, this.wallet.publicKey); const contractATA = (0, import_spl_token2.getAssociatedTokenAddressSync)(item.token, etfInfo.etfCoreAddress, true); return [ userATA, contractATA ]; }); const ix = await this.program.methods.etfBurn(new anchor2.BN(params.lamports)).accounts({ etfTokenMintAccount: params.etfAddress, authority: this.wallet.publicKey }).remainingAccounts( remainingAccounts.map((item) => ({ pubkey: item, isSigner: false, isWritable: true })) ).transaction(); const modifyComputeUnits = anchor2.web3.ComputeBudgetProgram.setComputeUnitLimit({ units: 4e5 }); let tx = new import_web33.Transaction().add(ix).add(modifyComputeUnits); tx.recentBlockhash = latestBlockhash.blockhash; tx.feePayer = this.wallet.publicKey; console.log("transaction created:", { recentBlockhash: tx.recentBlockhash, feePayer: tx.feePayer.toBase58(), instructions: tx.instructions.length }); try { const signedTx = await this.wallet.signTransaction(tx); console.log("transaction signed"); const txid = await this.connection.sendRawTransaction(signedTx.serialize(), { skipPreflight: false, preflightCommitment: "confirmed", maxRetries: 5 }); console.log("transaction sent, signed:", txid); const confirmation = await this.connection.confirmTransaction({ signature: txid, blockhash: latestBlockhash.blockhash, lastValidBlockHeight: latestBlockhash.lastValidBlockHeight }, "confirmed"); if (confirmation.value.err) { throw new Error(`transaction confirm failed: ${JSON.stringify(confirmation.value.err)}`); } console.log("transaction confirmed"); const mintTokenataAccount = (0, import_spl_token2.getAssociatedTokenAddressSync)(etfAddress, this.wallet.publicKey); const balance = await this.queries.getETFBalance(etfAddress, this.wallet.publicKey); return { success: true, txid, data: { mintTokenataAccount, balance, etfAddress: params.etfAddress } }; } catch (error) { console.error("burnETF error:", error); throw error; } } catch (error) { console.error("Error in burnETF:"); console.error("Error details:", error); if (error instanceof Error) { console.error("Error name:", error.name); console.error("Error message:", error.message); console.error("Error stack:", error.stack); } if (error instanceof anchor2.web3.SendTransactionError) { console.error("Transaction error logs:", error.logs); } return { success: false, txid: "", error: error instanceof Error ? error.message : "Burn ETF Token \u65F6\u53D1\u751F\u672A\u77E5\u9519\u8BEF" }; } } }; //# sourceMappingURL=solana.js.map