yamaswap-sdk
Version:
ETF SDK for Solana and Evm
1 lines • 61.8 kB
Source Map (JSON)
{"version":3,"sources":["../src/core/client.ts","../src/idl/iswap.json","../src/utils/getAddress.ts","../src/utils/checks.ts","../src/utils/error.ts","../src/utils/queries.ts"],"sourcesContent":["import * as anchor from \"@coral-xyz/anchor\";\nimport { Connection, PublicKey, Transaction } from \"@solana/web3.js\";\nimport idl from \"../idl/iswap.json\";\nimport type { Iswap } from \"../types/iswap\";\nimport { AnchorWallet, ETFCreateParams, ETFBurnParams, MintETFTokenParams, ETFCreateResult, MintETFResult } from \"../types/params\";\nimport { deriveEtfTokenMintAccount } from \"../utils/getAddress\";\nimport { ETFQueries } from \"../utils/queries\";\nimport {\n createAssociatedTokenAccountInstruction,\n getAccount,\n getAssociatedTokenAddressSync,\n TokenAccountNotFoundError,\n} from '@solana/spl-token';\nimport { checkATAExists, checkBalance, checkETFExists, validateETFCreateParams, validateMintETFParams } from '../utils/checks';\n\nconst DEFAULT_PROGRAM_ID = \"dXgZyuguvD2m5G5385XkdokZBryUoSE6LbNJeWiFiN5\";\n\nexport class DexClient {\n public readonly program: anchor.Program<Iswap>;\n\n public readonly queries: ETFQueries;\n private readonly wallet: AnchorWallet;\n\n constructor(\n public connection: Connection,\n wallet: AnchorWallet,\n provider: anchor.AnchorProvider,\n ) {\n this.connection = connection;\n this.wallet = wallet;\n\n this.program = new anchor.Program(idl as Iswap, provider)\n this.queries = new ETFQueries(this);\n }\n\n public async createETF(params: ETFCreateParams): Promise<ETFCreateResult> {\n try {\n validateETFCreateParams(params);\n\n const { name, symbol, description, url, assets } = params;\n\n const [etfAddress] = deriveEtfTokenMintAccount(this.program as unknown as anchor.Program, [\"etf_token_v3\", symbol]);\n const [etfCoreAddress] = deriveEtfTokenMintAccount(this.program as unknown as anchor.Program, [\"etf_token_v3\", etfAddress]);\n\n const etfInfo = await this.queries.getETFInfo(etfAddress);\n if (typeof etfInfo !== 'string') {\n return {\n success: false,\n error: 'etf already exists',\n data: {\n etfAddress,\n etfCoreAddress: etfInfo.etfCoreAddress,\n symbol,\n description: etfInfo.description,\n creator: etfInfo.creator,\n assets: etfInfo.assets\n }\n };\n }\n\n const latestBlockhash = await this.connection.getLatestBlockhash('confirmed');\n console.log('get the latest blockhash:', latestBlockhash.blockhash);\n\n\n let tx = new Transaction();\n\n for (const { token } of assets) {\n\n const tokenKey = new PublicKey(token);\n const address = getAssociatedTokenAddressSync(tokenKey, etfCoreAddress, true);\n\n try {\n await getAccount(this.connection, address);\n } catch (e) {\n if (e instanceof TokenAccountNotFoundError) {\n tx.add(\n createAssociatedTokenAccountInstruction(\n this.wallet.publicKey,\n address,\n etfCoreAddress,\n tokenKey,\n ),\n );\n }\n }\n }\n\n const ix = await this.program.methods\n .etfCreate({\n name,\n symbol,\n description,\n url,\n assets: assets.map(c => ({\n token: new PublicKey(c.token),\n weight: c.weight\n }))\n })\n .transaction();\n\n tx.add(ix);\n\n tx.recentBlockhash = latestBlockhash.blockhash;\n tx.feePayer = this.wallet.publicKey;\n\n console.log('transaction created:', {\n recentBlockhash: tx.recentBlockhash,\n feePayer: tx.feePayer.toBase58(),\n instructions: tx.instructions.length\n });\n\n try {\n\n const signedTx = await this.wallet.signTransaction(tx);\n console.log('transaction signed');\n\n const txid = await this.connection.sendRawTransaction(signedTx.serialize(), {\n skipPreflight: false,\n preflightCommitment: 'confirmed',\n maxRetries: 5\n });\n console.log('transaction sent, signed:', txid);\n\n\n const confirmation = await this.connection.confirmTransaction({\n signature: txid,\n blockhash: latestBlockhash.blockhash,\n lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,\n }, 'confirmed');\n\n if (confirmation.value.err) {\n throw new Error(`transaction confirm failed: ${JSON.stringify(confirmation.value.err)}`);\n }\n\n console.log('transaction confirmed');\n\n // get the confirmed etf info\n const confirmedEtfInfo = await this.queries.getETFInfo(etfAddress);\n if (typeof confirmedEtfInfo === 'string') {\n throw new Error('etf create success but not found etf info');\n }\n\n return {\n success: true,\n txid,\n data: {\n etfAddress,\n etfCoreAddress,\n symbol,\n name,\n description,\n creator: confirmedEtfInfo.creator,\n assets: confirmedEtfInfo.assets\n }\n };\n } catch (error) {\n console.error(\"createETF error:\", error);\n throw error;\n }\n } catch (error) {\n console.error(\"Error details:\", error);\n if (error instanceof Error) {\n console.error(\"Error stack:\", error.stack);\n }\n return {\n success: false,\n txid: '',\n error: error instanceof Error ? error.message : 'meet an unknown error when create ETF'\n };\n }\n }\n\n public async purchaseETF(params: MintETFTokenParams): Promise<MintETFResult> {\n try {\n\n const etfAddress = new PublicKey(params.etfAddress);\n validateMintETFParams(params);\n\n const etfInfo = await checkETFExists(this, etfAddress);\n\n // check if the user has the required tokens\n await checkATAExists(this, etfInfo.assets.map(item => item.token), this.wallet.publicKey);\n\n // check if the contract has the required tokens, if the token is pda, need to pass the true \n await checkATAExists(this, etfInfo.assets.map(item => item.token), etfInfo.etfCoreAddress, true);\n\n for (const item of etfInfo.assets) {\n const requiredAmount = (params.lamports * item.weight) / 10000;\n await checkBalance(this, item.token, this.wallet.publicKey, requiredAmount);\n }\n\n\n const latestBlockhash = await this.connection.getLatestBlockhash('confirmed');\n console.log('get the latest blockhash:', latestBlockhash.blockhash);\n\n const remainingAccounts = etfInfo.assets.flatMap((item) => {\n const userATA = getAssociatedTokenAddressSync(item.token, this.wallet.publicKey);\n const contractATA = getAssociatedTokenAddressSync(item.token, etfInfo.etfCoreAddress, true);\n return [userATA, contractATA];\n });\n\n const ix = await this.program.methods\n .etfMint(new anchor.BN(params.lamports))\n .accounts({\n etfTokenMintAccount: params.etfAddress,\n authority: this.wallet.publicKey,\n })\n .remainingAccounts(\n remainingAccounts.map((item) => ({ pubkey: item, isSigner: false, isWritable: true })),\n )\n .transaction();\n\n const modifyComputeUnits = anchor.web3.ComputeBudgetProgram.setComputeUnitLimit({\n units: 400_000,\n });\n\n let tx = new Transaction().add(ix).add(modifyComputeUnits);\n\n tx.recentBlockhash = latestBlockhash.blockhash;\n tx.feePayer = this.wallet.publicKey;\n\n console.log('transaction created:', {\n recentBlockhash: tx.recentBlockhash,\n feePayer: tx.feePayer.toBase58(),\n instructions: tx.instructions.length\n });\n\n try {\n\n const signedTx = await this.wallet.signTransaction(tx);\n console.log('transaction signed');\n\n // 发送交易\n const txid = await this.connection.sendRawTransaction(signedTx.serialize(), {\n skipPreflight: false,\n preflightCommitment: 'confirmed',\n maxRetries: 5\n });\n console.log('transaction sent, signed:', txid);\n\n const confirmation = await this.connection.confirmTransaction({\n signature: txid,\n blockhash: latestBlockhash.blockhash,\n lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,\n }, 'confirmed');\n\n if (confirmation.value.err) {\n throw new Error(`transaction confirm failed: ${JSON.stringify(confirmation.value.err)}`);\n }\n\n console.log('transaction confirmed');\n\n // get the mint tokenata account\n const mintTokenataAccount = getAssociatedTokenAddressSync(etfAddress, this.wallet.publicKey);\n const balance = await this.queries.getETFBalance(etfAddress, this.wallet.publicKey);\n\n return {\n success: true,\n txid,\n data: {\n mintTokenataAccount,\n balance,\n etfAddress: params.etfAddress\n }\n };\n } catch (error) {\n console.error(\"purchaseETF error:\", error);\n throw error;\n }\n } catch (error) {\n console.error(\"Error in purchaseETF:\");\n console.error(\"Error details:\", error);\n if (error instanceof Error) {\n console.error(\"Error name:\", error.name);\n console.error(\"Error message:\", error.message);\n console.error(\"Error stack:\", error.stack);\n }\n if (error instanceof anchor.web3.SendTransactionError) {\n console.error(\"Transaction error logs:\", error.logs);\n }\n return {\n success: false,\n txid: '',\n error: error instanceof Error ? error.message : 'Mint ETF Token 时发生未知错误'\n };\n }\n }\n\n public async burnETF(params: ETFBurnParams): Promise<MintETFResult> {\n try {\n const etfAddress = new PublicKey(params.etfAddress);\n const etfInfo = await checkETFExists(this, etfAddress);\n\n const tokensToCheck = [...etfInfo.assets.map(item => item.token), etfAddress];\n await checkATAExists(this, tokensToCheck, this.wallet.publicKey);\n await checkBalance(this, etfAddress, this.wallet.publicKey, params.lamports as number);\n\n\n const latestBlockhash = await this.connection.getLatestBlockhash('confirmed');\n console.log('get the latest blockhash:', latestBlockhash.blockhash);\n\n const remainingAccounts = etfInfo.assets.flatMap((item) => {\n const userATA = getAssociatedTokenAddressSync(item.token, this.wallet.publicKey);\n const contractATA = getAssociatedTokenAddressSync(item.token, etfInfo.etfCoreAddress, true);\n return [\n userATA,\n contractATA,\n ];\n });\n\n const ix = await this.program.methods\n .etfBurn(new anchor.BN(params.lamports as number))\n .accounts({\n etfTokenMintAccount: params.etfAddress,\n authority: this.wallet.publicKey,\n })\n .remainingAccounts(\n remainingAccounts.map((item) => ({ pubkey: item, isSigner: false, isWritable: true })),\n )\n .transaction();\n\n const modifyComputeUnits = anchor.web3.ComputeBudgetProgram.setComputeUnitLimit({\n units: 400_000,\n });\n\n let tx = new Transaction().add(ix).add(modifyComputeUnits);\n\n tx.recentBlockhash = latestBlockhash.blockhash;\n tx.feePayer = this.wallet.publicKey;\n\n console.log('transaction created:', {\n recentBlockhash: tx.recentBlockhash,\n feePayer: tx.feePayer.toBase58(),\n instructions: tx.instructions.length\n });\n\n try {\n\n const signedTx = await this.wallet.signTransaction(tx);\n console.log('transaction signed');\n\n const txid = await this.connection.sendRawTransaction(signedTx.serialize(), {\n skipPreflight: false,\n preflightCommitment: 'confirmed',\n maxRetries: 5\n });\n console.log('transaction sent, signed:', txid);\n\n const confirmation = await this.connection.confirmTransaction({\n signature: txid,\n blockhash: latestBlockhash.blockhash,\n lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,\n }, 'confirmed');\n\n if (confirmation.value.err) {\n throw new Error(`transaction confirm failed: ${JSON.stringify(confirmation.value.err)}`);\n }\n\n console.log('transaction confirmed');\n\n const mintTokenataAccount = getAssociatedTokenAddressSync(etfAddress, this.wallet.publicKey);\n const balance = await this.queries.getETFBalance(etfAddress, this.wallet.publicKey);\n\n return {\n success: true,\n txid,\n data: {\n mintTokenataAccount,\n balance,\n etfAddress: params.etfAddress\n }\n };\n } catch (error) {\n console.error(\"burnETF error:\", error);\n throw error;\n }\n } catch (error) {\n console.error(\"Error in burnETF:\");\n console.error(\"Error details:\", error);\n if (error instanceof Error) {\n console.error(\"Error name:\", error.name);\n console.error(\"Error message:\", error.message);\n console.error(\"Error stack:\", error.stack);\n }\n if (error instanceof anchor.web3.SendTransactionError) {\n console.error(\"Transaction error logs:\", error.logs);\n }\n return {\n success: false,\n txid: '',\n error: error instanceof Error ? error.message : 'Burn ETF Token 时发生未知错误'\n };\n }\n }\n\n}\n\n","{\n \"address\": \"dXgZyuguvD2m5G5385XkdokZBryUoSE6LbNJeWiFiN5\",\n \"metadata\": {\n \"name\": \"iswap\",\n \"version\": \"0.1.0\",\n \"spec\": \"0.1.0\",\n \"description\": \"Created with Anchor\"\n },\n \"instructions\": [\n {\n \"name\": \"close\",\n \"discriminator\": [\n 98,\n 165,\n 201,\n 177,\n 108,\n 65,\n 206,\n 96\n ],\n \"accounts\": [\n {\n \"name\": \"payer\",\n \"writable\": true,\n \"signer\": true\n },\n {\n \"name\": \"iswap\",\n \"writable\": true\n }\n ],\n \"args\": []\n },\n {\n \"name\": \"decrement\",\n \"discriminator\": [\n 106,\n 227,\n 168,\n 59,\n 248,\n 27,\n 150,\n 101\n ],\n \"accounts\": [\n {\n \"name\": \"iswap\",\n \"writable\": true\n }\n ],\n \"args\": []\n },\n {\n \"name\": \"etf_burn\",\n \"discriminator\": [\n 185,\n 203,\n 114,\n 195,\n 129,\n 70,\n 235,\n 23\n ],\n \"accounts\": [\n {\n \"name\": \"etf_token_info\",\n \"pda\": {\n \"seeds\": [\n {\n \"kind\": \"const\",\n \"value\": [\n 101,\n 116,\n 102,\n 95,\n 116,\n 111,\n 107,\n 101,\n 110,\n 95,\n 118,\n 51\n ]\n },\n {\n \"kind\": \"account\",\n \"path\": \"etf_token_mint_account\"\n }\n ]\n }\n },\n {\n \"name\": \"etf_token_mint_account\",\n \"writable\": true\n },\n {\n \"name\": \"etf_token_ata\",\n \"writable\": true,\n \"pda\": {\n \"seeds\": [\n {\n \"kind\": \"account\",\n \"path\": \"authority\"\n },\n {\n \"kind\": \"const\",\n \"value\": [\n 6,\n 221,\n 246,\n 225,\n 215,\n 101,\n 161,\n 147,\n 217,\n 203,\n 225,\n 70,\n 206,\n 235,\n 121,\n 172,\n 28,\n 180,\n 133,\n 237,\n 95,\n 91,\n 55,\n 145,\n 58,\n 140,\n 245,\n 133,\n 126,\n 255,\n 0,\n 169\n ]\n },\n {\n \"kind\": \"account\",\n \"path\": \"etf_token_mint_account\"\n }\n ],\n \"program\": {\n \"kind\": \"const\",\n \"value\": [\n 140,\n 151,\n 37,\n 143,\n 78,\n 36,\n 137,\n 241,\n 187,\n 61,\n 16,\n 41,\n 20,\n 142,\n 13,\n 131,\n 11,\n 90,\n 19,\n 153,\n 218,\n 255,\n 16,\n 132,\n 4,\n 142,\n 123,\n 216,\n 219,\n 233,\n 248,\n 89\n ]\n }\n }\n },\n {\n \"name\": \"authority\",\n \"writable\": true,\n \"signer\": true\n },\n {\n \"name\": \"token_program\",\n \"address\": \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\"\n },\n {\n \"name\": \"associated_token_program\",\n \"address\": \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\"\n },\n {\n \"name\": \"system_program\",\n \"address\": \"11111111111111111111111111111111\"\n }\n ],\n \"args\": [\n {\n \"name\": \"lamports\",\n \"type\": \"u64\"\n }\n ]\n },\n {\n \"name\": \"etf_create\",\n \"discriminator\": [\n 0,\n 81,\n 62,\n 85,\n 242,\n 37,\n 4,\n 245\n ],\n \"accounts\": [\n {\n \"name\": \"etf_token_info\",\n \"writable\": true,\n \"pda\": {\n \"seeds\": [\n {\n \"kind\": \"const\",\n \"value\": [\n 101,\n 116,\n 102,\n 95,\n 116,\n 111,\n 107,\n 101,\n 110,\n 95,\n 118,\n 51\n ]\n },\n {\n \"kind\": \"account\",\n \"path\": \"etf_token_mint_account\"\n }\n ]\n }\n },\n {\n \"name\": \"metadata_account\",\n \"writable\": true,\n \"pda\": {\n \"seeds\": [\n {\n \"kind\": \"const\",\n \"value\": [\n 109,\n 101,\n 116,\n 97,\n 100,\n 97,\n 116,\n 97\n ]\n },\n {\n \"kind\": \"account\",\n \"path\": \"token_metadata_program\"\n },\n {\n \"kind\": \"account\",\n \"path\": \"etf_token_mint_account\"\n }\n ],\n \"program\": {\n \"kind\": \"account\",\n \"path\": \"token_metadata_program\"\n }\n }\n },\n {\n \"name\": \"etf_token_mint_account\",\n \"writable\": true,\n \"pda\": {\n \"seeds\": [\n {\n \"kind\": \"const\",\n \"value\": [\n 101,\n 116,\n 102,\n 95,\n 116,\n 111,\n 107,\n 101,\n 110,\n 95,\n 118,\n 51\n ]\n },\n {\n \"kind\": \"arg\",\n \"path\": \"args.symbol\"\n }\n ]\n }\n },\n {\n \"name\": \"rent\",\n \"address\": \"SysvarRent111111111111111111111111111111111\"\n },\n {\n \"name\": \"authority\",\n \"writable\": true,\n \"signer\": true\n },\n {\n \"name\": \"token_metadata_program\",\n \"address\": \"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s\"\n },\n {\n \"name\": \"token_program\",\n \"address\": \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\"\n },\n {\n \"name\": \"system_program\",\n \"address\": \"11111111111111111111111111111111\"\n }\n ],\n \"args\": [\n {\n \"name\": \"args\",\n \"type\": {\n \"defined\": {\n \"name\": \"EtfTokenArgs\"\n }\n }\n }\n ]\n },\n {\n \"name\": \"etf_mint\",\n \"discriminator\": [\n 253,\n 102,\n 37,\n 173,\n 154,\n 46,\n 245,\n 224\n ],\n \"accounts\": [\n {\n \"name\": \"etf_token_info\",\n \"pda\": {\n \"seeds\": [\n {\n \"kind\": \"const\",\n \"value\": [\n 101,\n 116,\n 102,\n 95,\n 116,\n 111,\n 107,\n 101,\n 110,\n 95,\n 118,\n 51\n ]\n },\n {\n \"kind\": \"account\",\n \"path\": \"etf_token_mint_account\"\n }\n ]\n }\n },\n {\n \"name\": \"etf_token_mint_account\",\n \"writable\": true\n },\n {\n \"name\": \"etf_token_ata\",\n \"writable\": true,\n \"pda\": {\n \"seeds\": [\n {\n \"kind\": \"account\",\n \"path\": \"authority\"\n },\n {\n \"kind\": \"const\",\n \"value\": [\n 6,\n 221,\n 246,\n 225,\n 215,\n 101,\n 161,\n 147,\n 217,\n 203,\n 225,\n 70,\n 206,\n 235,\n 121,\n 172,\n 28,\n 180,\n 133,\n 237,\n 95,\n 91,\n 55,\n 145,\n 58,\n 140,\n 245,\n 133,\n 126,\n 255,\n 0,\n 169\n ]\n },\n {\n \"kind\": \"account\",\n \"path\": \"etf_token_mint_account\"\n }\n ],\n \"program\": {\n \"kind\": \"const\",\n \"value\": [\n 140,\n 151,\n 37,\n 143,\n 78,\n 36,\n 137,\n 241,\n 187,\n 61,\n 16,\n 41,\n 20,\n 142,\n 13,\n 131,\n 11,\n 90,\n 19,\n 153,\n 218,\n 255,\n 16,\n 132,\n 4,\n 142,\n 123,\n 216,\n 219,\n 233,\n 248,\n 89\n ]\n }\n }\n },\n {\n \"name\": \"authority\",\n \"writable\": true,\n \"signer\": true\n },\n {\n \"name\": \"token_program\",\n \"address\": \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\"\n },\n {\n \"name\": \"associated_token_program\",\n \"address\": \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\"\n },\n {\n \"name\": \"system_program\",\n \"address\": \"11111111111111111111111111111111\"\n }\n ],\n \"args\": [\n {\n \"name\": \"lamports\",\n \"type\": \"u64\"\n }\n ]\n },\n {\n \"name\": \"increment\",\n \"discriminator\": [\n 11,\n 18,\n 104,\n 9,\n 104,\n 174,\n 59,\n 33\n ],\n \"accounts\": [\n {\n \"name\": \"iswap\",\n \"writable\": true\n }\n ],\n \"args\": []\n },\n {\n \"name\": \"initialize\",\n \"discriminator\": [\n 175,\n 175,\n 109,\n 31,\n 13,\n 152,\n 155,\n 237\n ],\n \"accounts\": [\n {\n \"name\": \"payer\",\n \"writable\": true,\n \"signer\": true\n },\n {\n \"name\": \"iswap\",\n \"writable\": true,\n \"signer\": true\n },\n {\n \"name\": \"system_program\",\n \"address\": \"11111111111111111111111111111111\"\n }\n ],\n \"args\": []\n },\n {\n \"name\": \"set\",\n \"discriminator\": [\n 198,\n 51,\n 53,\n 241,\n 116,\n 29,\n 126,\n 194\n ],\n \"accounts\": [\n {\n \"name\": \"iswap\",\n \"writable\": true\n }\n ],\n \"args\": [\n {\n \"name\": \"value\",\n \"type\": \"u8\"\n }\n ]\n }\n ],\n \"accounts\": [\n {\n \"name\": \"EtfToken\",\n \"discriminator\": [\n 187,\n 90,\n 26,\n 73,\n 137,\n 112,\n 105,\n 60\n ]\n },\n {\n \"name\": \"Iswap\",\n \"discriminator\": [\n 228,\n 5,\n 227,\n 43,\n 35,\n 87,\n 170,\n 87\n ]\n }\n ],\n \"errors\": [\n {\n \"code\": 6000,\n \"name\": \"InvalidAccounts\",\n \"msg\": \"Lack of necessary accounts\"\n },\n {\n \"code\": 6001,\n \"name\": \"InsufficientBalance\",\n \"msg\": \"Insufficient balance\"\n }\n ],\n \"types\": [\n {\n \"name\": \"EtfAsset\",\n \"type\": {\n \"kind\": \"struct\",\n \"fields\": [\n {\n \"name\": \"token\",\n \"type\": \"pubkey\"\n },\n {\n \"name\": \"weight\",\n \"type\": \"u16\"\n }\n ]\n }\n },\n {\n \"name\": \"EtfToken\",\n \"type\": {\n \"kind\": \"struct\",\n \"fields\": [\n {\n \"name\": \"mint_account\",\n \"type\": \"pubkey\"\n },\n {\n \"name\": \"creator\",\n \"type\": \"pubkey\"\n },\n {\n \"name\": \"create_at\",\n \"type\": \"i64\"\n },\n {\n \"name\": \"description\",\n \"type\": \"string\"\n },\n {\n \"name\": \"assets\",\n \"type\": {\n \"vec\": {\n \"defined\": {\n \"name\": \"EtfAsset\"\n }\n }\n }\n }\n ]\n }\n },\n {\n \"name\": \"EtfTokenArgs\",\n \"type\": {\n \"kind\": \"struct\",\n \"fields\": [\n {\n \"name\": \"name\",\n \"type\": \"string\"\n },\n {\n \"name\": \"symbol\",\n \"type\": \"string\"\n },\n {\n \"name\": \"description\",\n \"type\": \"string\"\n },\n {\n \"name\": \"url\",\n \"type\": \"string\"\n },\n {\n \"name\": \"assets\",\n \"type\": {\n \"vec\": {\n \"defined\": {\n \"name\": \"EtfAsset\"\n }\n }\n }\n }\n ]\n }\n },\n {\n \"name\": \"Iswap\",\n \"type\": {\n \"kind\": \"struct\",\n \"fields\": [\n {\n \"name\": \"count\",\n \"type\": \"u8\"\n }\n ]\n }\n }\n ]\n}","import * as anchor from \"@coral-xyz/anchor\";\nimport { getMint, Mint } from \"@solana/spl-token\";\nimport { Connection, PublicKey } from \"@solana/web3.js\";\nimport { ETFNotExistsError } from \"./error\";\n\nexport function deriveEtfTokenMintAccount(program: anchor.Program, seeds: (string | PublicKey)[],): [anchor.web3.PublicKey, number] {\n const buffers = seeds.map(part => {\n if (typeof part === 'string') {\n return Buffer.from(part); // 字符串直接转换为 Buffer\n } else if (part instanceof PublicKey) {\n return part.toBuffer(); // PublicKey 转换为 Buffer\n } else {\n throw new Error(`Unsupported type in symbolParts: ${typeof part}`);\n }\n });\n\n\n return anchor.web3.PublicKey.findProgramAddressSync(\n [\n ...buffers\n ],\n program.programId,\n );\n}\n\n\n\n// export async function checkETFExists(etfAddress: PublicKey, connection: Connection): Promise<void> {\n// try {\n\n\n// const mintInfo: Mint = await getMint(connection, etfAddress);\n// console.log('ETF合约已存在:', mintInfo.address.toBase58());\n// return;\n// } catch (error) {\n// if (error instanceof Error && error.message.includes('Account does not exist')) {\n// throw new ETFNotExistsError(`ETF合约不存在: ${etfAddress}`);\n// } else {\n// throw new Error(`检查ETF合约时发生错误: ${error.message}`);\n// }\n// }\n// }0x9826eF6A1aFb97Bc7C2473B1D893A1067023bAF8\n\nexport function getSavedContractAddresses() {\n let json = {\n \"hardhat\": {\n \"TWAP\": \"0xDb731EaaFA0FFA7854A24C2379585a85D768Ed5C\",\n \"CETO\": \"0x335796f7A0F72368D1588839e38f163d90C92C80\",\n \"ETFROUTER\": \"0xa86582Ad5E80abc19F95f8A9Fb3905Cda0dAbd59\",\n \"ETFFACTORY\": \"0x97915c43511f8cB4Fbe7Ea03B96EEe940eC4AF12\",\n },\n \"localhost\": {\n \"TWAP\": \"0xDb731EaaFA0FFA7854A24C2379585a85D768Ed5C\",\n \"CETO\": \"0x335796f7A0F72368D1588839e38f163d90C92C80\",\n \"ETFROUTER\": \"0xa86582Ad5E80abc19F95f8A9Fb3905Cda0dAbd59\",\n \"ETFFACTORY\": \"0x97915c43511f8cB4Fbe7Ea03B96EEe940eC4AF12\",\n \"WETH\": \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"\n },\n \"arbitrumSepolia\": {\n \"TWAP\": \"0x5571Af8f9277E5b00DDF5fdA7738eA01d9fc5dFe\",\n \"CETO\": \"0x197D6Fe658Eb2D9fa3eEE5d6F079ce1E1619Ad9b\",\n \"ETFROUTER\": \"0xE2B2efFC24c7e3Fe1ABb1c3eDD33481d9a7d9346\",\n \"ETFFACTORY\": \"0xD324c3B60B94b9Fe8aaA66F4A44a5b3a3Dbcb811\",\n \"WETH\": \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"\n },\n \"sepolia\": {\n \"TWAP\": \"0x5617E6B8d33E4bE32c1c8eA140998bf6c1280E2D\",\n \"CETO\": \"0xC0Fc8583c7c308ea92471d3897cA04514615C84D\",\n \"ETFROUTER\": \"0x84E8120aC983b3125737DAebED2C579e9a9967F1\",\n \"ETFFACTORY\": \"0x21cFE67901E071fc178a1D766414Dfa20E285209\",\n \"WETH\": \"0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14\"\n },\n }\n\n return json;\n}","import { PublicKey } from '@solana/web3.js';\nimport { getAccount, getAssociatedTokenAddressSync, TokenAccountNotFoundError, Account } from '@solana/spl-token';\nimport { ETFInvalidParamsError, ETFInsufficientBalanceError, ETFNotExistsError } from './error';\nimport { DexClient } from '../core/client';\n// Start of Selection\nimport { ETFInfo } from './queries';\nimport { ETFCreateParams, MintETFTokenParams } from '../types/params';\n\nexport async function checkATAExists(client: DexClient, tokens: PublicKey[], owner: PublicKey, isContract: boolean = false): Promise<Account | undefined> {\n for (const token of tokens) {\n const tokenAccountAddress = getAssociatedTokenAddressSync(token, owner, isContract);\n try {\n return await getAccount(client.connection, tokenAccountAddress);\n } catch (e) {\n if (e instanceof TokenAccountNotFoundError) {\n throw new TokenAccountNotFoundError(`账户 ${tokenAccountAddress.toBase58()} 不存在`);\n }\n throw e;\n }\n }\n return undefined;\n}\n\nexport async function checkBalance(client: DexClient, etfAddress: PublicKey, owner: PublicKey, requiredAmount: number): Promise<void> {\n const balance = await client.queries.getETFBalance(etfAddress, owner);\n if (balance < requiredAmount) {\n throw new ETFInsufficientBalanceError(`用户代币账户余额不足,所需: ${requiredAmount}, 当前: ${balance}`);\n }\n}\n\nexport async function checkETFExists(client: DexClient, etfAddress: PublicKey): Promise<ETFInfo> {\n const etfInfo = await client.queries.getETFInfo(etfAddress);\n if (typeof etfInfo === 'string') {\n throw new ETFNotExistsError(etfInfo);\n }\n return etfInfo;\n}\n\nexport function validateETFCreateParams(params: ETFCreateParams): void {\n const { assets } = params;\n\n // check if the constituent is not empty\n if (!assets?.length) {\n throw new ETFInvalidParamsError('ETF组成部分不能为空');\n }\n\n const totalWeight = assets.reduce((sum, item) => sum + item.weight, 0);\n if (totalWeight !== 100) {\n throw new ETFInvalidParamsError(`ETF组成部分权重之和必须为100,当前为: ${totalWeight}`);\n }\n\n // check if the token address is valid\n const tokenSet = new Set<string>();\n for (const item of assets) {\n let tokenKey: PublicKey;\n try {\n tokenKey = new PublicKey(item.token);\n } catch (e) {\n throw new ETFInvalidParamsError(`无效的代币地址: ${item.token}`);\n }\n\n // check if there are duplicate tokens\n if (tokenSet.has(tokenKey.toBase58())) {\n throw new ETFInvalidParamsError(`存在重复的代币地址: ${item.token}`);\n }\n tokenSet.add(tokenKey.toBase58());\n if (item.weight <= 0) {\n throw new ETFInvalidParamsError(`代币权重必须大于0: ${item.token}`);\n }\n }\n}\n\nexport function validateMintETFParams(params: MintETFTokenParams): void {\n if (params.lamports <= 0) {\n throw new ETFInvalidParamsError('购买份额必须大于0');\n }\n // maximum limit 1000000 SOL\n if (params.lamports > 1e15) { // 1000000 * 1e9\n throw new ETFInvalidParamsError('购买份额超过最大限制(1000000 SOL)');\n }\n} ","\nexport class DexSDKError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'DexSDKError';\n Object.setPrototypeOf(this, DexSDKError.prototype);\n }\n}\n\n\nexport class ETFNotExistsError extends DexSDKError {\n constructor(message: string) {\n super(message);\n this.name = 'ETFNotExistsError';\n Object.setPrototypeOf(this, ETFNotExistsError.prototype);\n }\n}\n\nexport class ETFExistsError extends DexSDKError {\n constructor(message: string) {\n super(message);\n this.name = 'ETFExistsError';\n Object.setPrototypeOf(this, ETFExistsError.prototype);\n }\n}\n\nexport class ETFInsufficientBalanceError extends DexSDKError {\n constructor(message: string) {\n super(message);\n this.name = 'ETFInsufficientBalanceError';\n Object.setPrototypeOf(this, ETFInsufficientBalanceError.prototype);\n }\n}\n\nexport class ETFInvalidParamsError extends DexSDKError {\n constructor(message: string) {\n super(message);\n this.name = 'ETFInvalidParamsError';\n Object.setPrototypeOf(this, ETFInvalidParamsError.prototype);\n }\n}\n\n\nexport const isETFError = (error: unknown): error is ETFNotExistsError | ETFInsufficientBalanceError | ETFInvalidParamsError => {\n return error instanceof ETFNotExistsError ||\n error instanceof ETFInsufficientBalanceError ||\n error instanceof ETFInvalidParamsError;\n};\n","import { PublicKey } from \"@solana/web3.js\";\nimport { DexClient } from \"../core/client\";\nimport { ETFNotExistsError } from \"./error\";\nimport { ETFCreateParams } from \"../types/params\";\nimport { getAccount } from \"@solana/spl-token\";\nimport { deriveEtfTokenMintAccount } from \"./getAddress\";\nimport * as anchor from \"@coral-xyz/anchor\";\nimport { getAssociatedTokenAddressSync, Account } from \"@solana/spl-token\";\nimport { checkATAExists } from \"./checks\";\nexport interface ETFInfo {\n etfCoreAddress: PublicKey;\n creator: string;\n description: string;\n assets: Array<{\n token: PublicKey;\n weight: number;\n }>;\n}\n\nexport class ETFQueries {\n constructor(private client: DexClient) { }\n\n\n async getETFInfo(etfAddress: PublicKey): Promise<ETFInfo | string> {\n const [etfInfoAddress] = deriveEtfTokenMintAccount(this.client.program as unknown as anchor.Program, [\"etf_token_v3\", etfAddress]);\n try {\n\n const etfInfo = await this.client.program.account.etfToken.fetch(etfInfoAddress);\n\n return {\n etfCoreAddress: etfInfoAddress,\n creator: etfInfo.creator.toString(),\n description: etfInfo.description,\n assets: etfInfo.assets.map(item => ({\n token: item.token,\n weight: item.weight\n }))\n };\n } catch (error) {\n if (error instanceof Error && error.message.includes('Account does not exist')) {\n return `ETF不存在: ${etfAddress}`;\n }\n throw error;\n }\n }\n\n async getETFBalance(etfAddress: PublicKey, ownerAddress: PublicKey): Promise<number> {\n try {\n const tokenAccount = await checkATAExists(this.client, [etfAddress], ownerAddress);\n return Number(tokenAccount?.amount);\n } catch (error) {\n if (error instanceof Error && error.message.includes('Account does not exist')) {\n return 0;\n }\n throw error;\n }\n }\n} "],"mappings":"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