@lifi/sdk-provider-solana 4.0.0-beta.11 → 4.0.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/actions/getSolanaBalance.js +1 -1
  3. package/dist/cjs/actions/getSolanaBalance.js.map +1 -1
  4. package/dist/cjs/actions/sendAndConfirmBundle.js +6 -1
  5. package/dist/cjs/actions/sendAndConfirmBundle.js.map +1 -1
  6. package/dist/cjs/actions/sendAndConfirmTransaction.js +6 -1
  7. package/dist/cjs/actions/sendAndConfirmTransaction.js.map +1 -1
  8. package/dist/cjs/core/tasks/SolanaJitoWaitForTransactionTask.js +4 -2
  9. package/dist/cjs/core/tasks/SolanaJitoWaitForTransactionTask.js.map +1 -1
  10. package/dist/cjs/core/tasks/SolanaStandardWaitForTransactionTask.js +5 -4
  11. package/dist/cjs/core/tasks/SolanaStandardWaitForTransactionTask.js.map +1 -1
  12. package/dist/cjs/index.d.ts +2 -1
  13. package/dist/cjs/index.js +2 -0
  14. package/dist/cjs/rpc/registry.js +4 -4
  15. package/dist/cjs/rpc/registry.js.map +1 -1
  16. package/dist/cjs/utils/solanaErrorCause.d.ts +15 -0
  17. package/dist/cjs/utils/solanaErrorCause.js +24 -0
  18. package/dist/cjs/utils/solanaErrorCause.js.map +1 -0
  19. package/dist/cjs/version.d.ts +1 -1
  20. package/dist/cjs/version.js +1 -1
  21. package/dist/cjs/version.js.map +1 -1
  22. package/dist/esm/actions/getSolanaBalance.js +1 -1
  23. package/dist/esm/actions/getSolanaBalance.js.map +1 -1
  24. package/dist/esm/actions/sendAndConfirmBundle.d.ts.map +1 -1
  25. package/dist/esm/actions/sendAndConfirmBundle.js +6 -1
  26. package/dist/esm/actions/sendAndConfirmBundle.js.map +1 -1
  27. package/dist/esm/actions/sendAndConfirmTransaction.d.ts.map +1 -1
  28. package/dist/esm/actions/sendAndConfirmTransaction.js +6 -1
  29. package/dist/esm/actions/sendAndConfirmTransaction.js.map +1 -1
  30. package/dist/esm/core/tasks/SolanaJitoWaitForTransactionTask.d.ts.map +1 -1
  31. package/dist/esm/core/tasks/SolanaJitoWaitForTransactionTask.js +4 -2
  32. package/dist/esm/core/tasks/SolanaJitoWaitForTransactionTask.js.map +1 -1
  33. package/dist/esm/core/tasks/SolanaStandardWaitForTransactionTask.d.ts.map +1 -1
  34. package/dist/esm/core/tasks/SolanaStandardWaitForTransactionTask.js +5 -4
  35. package/dist/esm/core/tasks/SolanaStandardWaitForTransactionTask.js.map +1 -1
  36. package/dist/esm/index.d.ts +2 -1
  37. package/dist/esm/index.js +2 -1
  38. package/dist/esm/rpc/registry.d.ts.map +1 -1
  39. package/dist/esm/rpc/registry.js +4 -4
  40. package/dist/esm/rpc/registry.js.map +1 -1
  41. package/dist/esm/utils/solanaErrorCause.d.ts +15 -0
  42. package/dist/esm/utils/solanaErrorCause.d.ts.map +1 -0
  43. package/dist/esm/utils/solanaErrorCause.js +22 -0
  44. package/dist/esm/utils/solanaErrorCause.js.map +1 -0
  45. package/dist/esm/version.d.ts +1 -1
  46. package/dist/esm/version.js +1 -1
  47. package/dist/esm/version.js.map +1 -1
  48. package/package.json +2 -1
  49. package/src/actions/getSolanaBalance.ts +2 -1
  50. package/src/actions/sendAndConfirmBundle.ts +11 -1
  51. package/src/actions/sendAndConfirmTransaction.ts +13 -1
  52. package/src/core/tasks/SolanaJitoWaitForTransactionTask.ts +11 -5
  53. package/src/core/tasks/SolanaStandardWaitForTransactionTask.ts +12 -15
  54. package/src/index.ts +1 -0
  55. package/src/rpc/registry.ts +12 -6
  56. package/src/utils/solanaErrorCause.ts +24 -0
  57. package/src/version.ts +1 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # @lifi/sdk-provider-solana
2
+
3
+ ## 4.0.0-beta.12
4
+
5
+ ### Patch Changes
6
+
7
+ - [#387](https://github.com/lifinance/sdk/pull/387) [`12ee1f1`](https://github.com/lifinance/sdk/commit/12ee1f1bf7e79b67842d4d8ca606a80fe0913653) Thanks [@chybisov](https://github.com/chybisov)! - Preserve Solana RPC error details on failed transactions. The structured `err` payload (and `logs`, for simulation failures) from a failed simulation or confirmation is now attached to the thrown `TransactionError`'s `cause` as a new `SolanaTransactionDetailsError`, so consumers can inspect the original error and logs directly without re-simulating. `SolanaTransactionDetailsError` is exported from the package root.
@@ -36,7 +36,7 @@ const getSolanaBalanceDefault = async (client, _chainId, tokens, walletAddress)
36
36
  const walletTokenAmounts = [...tokenProgramOk ? tokenAccountsByOwner.value.value : [], ...token2022ProgramOk ? token2022AccountsByOwner.value.value : []].reduce((tokenAmounts, value) => {
37
37
  const tokenAccount = value.account.data.parsed.info;
38
38
  const amount = BigInt(tokenAccount.tokenAmount.amount);
39
- if (amount > 0n) tokenAmounts[tokenAccount.mint] = amount;
39
+ if (amount > 0n) tokenAmounts[tokenAccount.mint] = (tokenAmounts[tokenAccount.mint] ?? 0n) + amount;
40
40
  return tokenAmounts;
41
41
  }, {});
42
42
  const splZeroIsKnown = tokenProgramOk && token2022ProgramOk;
@@ -1 +1 @@
1
- {"version":3,"file":"getSolanaBalance.js","names":["callSolanaRpcsWithRetry"],"sources":["../../../src/actions/getSolanaBalance.ts"],"sourcesContent":["import type { SDKClient } from '@lifi/sdk'\nimport {\n type ChainId,\n type Token,\n type TokenAmount,\n withDedupe,\n} from '@lifi/sdk'\nimport { address, type JsonParsedTokenAccount } from '@solana/kit'\n\nimport { callSolanaRpcsWithRetry } from '../rpc/utils.js'\n\nconst SolSystemProgram = '11111111111111111111111111111111'\nconst TokenProgramId = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'\nconst Token2022ProgramId = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'\n\nexport const getSolanaBalance = async (\n client: SDKClient,\n walletAddress: string,\n tokens: Token[]\n): Promise<TokenAmount[]> => {\n if (tokens.length === 0) {\n return []\n }\n const { chainId } = tokens[0]\n for (const token of tokens) {\n if (token.chainId !== chainId) {\n console.warn('Requested tokens have to be on the same chain.')\n }\n }\n\n return getSolanaBalanceDefault(client, chainId, tokens, walletAddress)\n}\n\nconst getSolanaBalanceDefault = async (\n client: SDKClient,\n _chainId: ChainId,\n tokens: Token[],\n walletAddress: string\n): Promise<TokenAmount[]> => {\n // Convert addresses to Solana Kit's address type\n const accountAddress = address(walletAddress)\n const tokenProgramAddress = address(TokenProgramId)\n const token2022ProgramAddress = address(Token2022ProgramId)\n\n // Use Solana Kit's RPC API with the retry wrapper\n const [slot, balance, tokenAccountsByOwner, token2022AccountsByOwner] =\n await Promise.allSettled([\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc.getSlot({ commitment: 'confirmed' }).send()\n ),\n { id: `${getSolanaBalanceDefault.name}.getSlot` }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc.getBalance(accountAddress, { commitment: 'confirmed' }).send()\n ),\n { id: `${getSolanaBalanceDefault.name}.getBalance` }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc\n .getTokenAccountsByOwner(\n accountAddress,\n {\n programId: tokenProgramAddress,\n },\n {\n commitment: 'confirmed',\n encoding: 'jsonParsed',\n }\n )\n .send()\n ),\n {\n id: `${getSolanaBalanceDefault.name}.getTokenAccountsByOwner.${TokenProgramId}`,\n }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc\n .getTokenAccountsByOwner(\n accountAddress,\n {\n programId: token2022ProgramAddress,\n },\n {\n commitment: 'confirmed',\n encoding: 'jsonParsed',\n }\n )\n .send()\n ),\n {\n id: `${getSolanaBalanceDefault.name}.getTokenAccountsByOwner.${Token2022ProgramId}`,\n }\n ),\n ])\n const blockNumber = slot.status === 'fulfilled' ? BigInt(slot.value) : 0n\n const nativeBalanceOk = balance.status === 'fulfilled'\n const solBalance = nativeBalanceOk ? BigInt(balance.value.value) : 0n\n const tokenProgramOk = tokenAccountsByOwner.status === 'fulfilled'\n const token2022ProgramOk = token2022AccountsByOwner.status === 'fulfilled'\n\n const walletTokenAmounts = [\n ...(tokenProgramOk ? tokenAccountsByOwner.value.value : []),\n ...(token2022ProgramOk ? token2022AccountsByOwner.value.value : []),\n ].reduce(\n (tokenAmounts: Record<string, bigint>, value) => {\n const tokenAccount: JsonParsedTokenAccount =\n value.account.data.parsed.info\n const amount = BigInt(tokenAccount.tokenAmount.amount)\n if (amount > 0n) {\n tokenAmounts[tokenAccount.mint] = amount\n }\n return tokenAmounts\n },\n {} as Record<string, bigint>\n )\n\n // We can only confidently report 0n for an SPL mint when both Token and\n // Token2022 program queries succeeded — otherwise the mint may live in the\n // program whose query failed (e.g. PYUSD on Token2022).\n const splZeroIsKnown = tokenProgramOk && token2022ProgramOk\n\n const tokenAmounts: TokenAmount[] = tokens.map((token) => {\n const isNative = token.address === SolSystemProgram\n if (isNative) {\n if (!nativeBalanceOk) {\n return { ...token, blockNumber }\n }\n return { ...token, amount: solBalance, blockNumber }\n }\n const found = walletTokenAmounts[token.address]\n if (found !== undefined) {\n return { ...token, amount: found, blockNumber }\n }\n if (splZeroIsKnown) {\n return { ...token, amount: 0n, blockNumber }\n }\n return { ...token, blockNumber }\n })\n return tokenAmounts\n}\n"],"mappings":";;;;;AAWA,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;AACvB,MAAM,qBAAqB;AAE3B,MAAa,mBAAmB,OAC9B,QACA,eACA,WAC2B;CAC3B,IAAI,OAAO,WAAW,GACpB,OAAO,CAAC;CAEV,MAAM,EAAE,YAAY,OAAO;CAC3B,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,YAAY,SACpB,QAAQ,KAAK,gDAAgD;CAIjE,OAAO,wBAAwB,QAAQ,SAAS,QAAQ,aAAa;AACvE;AAEA,MAAM,0BAA0B,OAC9B,QACA,UACA,QACA,kBAC2B;CAE3B,MAAM,kBAAA,GAAA,YAAA,SAAyB,aAAa;CAC5C,MAAM,uBAAA,GAAA,YAAA,SAA8B,cAAc;CAClD,MAAM,2BAAA,GAAA,YAAA,SAAkC,kBAAkB;CAG1D,MAAM,CAAC,MAAM,SAAS,sBAAsB,4BAC1C,MAAM,QAAQ,WAAW;kCAGnBA,kBAAAA,wBAAwB,SAAS,QAC/B,IAAI,QAAQ,EAAE,YAAY,YAAY,CAAC,EAAE,KAAK,CAChD,GACF,EAAE,IAAI,GAAG,wBAAwB,KAAK,UAAU,CAClD;kCAGIA,kBAAAA,wBAAwB,SAAS,QAC/B,IAAI,WAAW,gBAAgB,EAAE,YAAY,YAAY,CAAC,EAAE,KAAK,CACnE,GACF,EAAE,IAAI,GAAG,wBAAwB,KAAK,aAAa,CACrD;kCAGIA,kBAAAA,wBAAwB,SAAS,QAC/B,IACG,wBACC,gBACA,EACE,WAAW,oBACb,GACA;GACE,YAAY;GACZ,UAAU;EACZ,CACF,EACC,KAAK,CACV,GACF,EACE,IAAI,GAAG,wBAAwB,KAAK,2BAA2B,iBACjE,CACF;kCAGIA,kBAAAA,wBAAwB,SAAS,QAC/B,IACG,wBACC,gBACA,EACE,WAAW,wBACb,GACA;GACE,YAAY;GACZ,UAAU;EACZ,CACF,EACC,KAAK,CACV,GACF,EACE,IAAI,GAAG,wBAAwB,KAAK,2BAA2B,qBACjE,CACF;CACF,CAAC;CACH,MAAM,cAAc,KAAK,WAAW,cAAc,OAAO,KAAK,KAAK,IAAI;CACvE,MAAM,kBAAkB,QAAQ,WAAW;CAC3C,MAAM,aAAa,kBAAkB,OAAO,QAAQ,MAAM,KAAK,IAAI;CACnE,MAAM,iBAAiB,qBAAqB,WAAW;CACvD,MAAM,qBAAqB,yBAAyB,WAAW;CAE/D,MAAM,qBAAqB,CACzB,GAAI,iBAAiB,qBAAqB,MAAM,QAAQ,CAAC,GACzD,GAAI,qBAAqB,yBAAyB,MAAM,QAAQ,CAAC,CACnE,EAAE,QACC,cAAsC,UAAU;EAC/C,MAAM,eACJ,MAAM,QAAQ,KAAK,OAAO;EAC5B,MAAM,SAAS,OAAO,aAAa,YAAY,MAAM;EACrD,IAAI,SAAS,IACX,aAAa,aAAa,QAAQ;EAEpC,OAAO;CACT,GACA,CAAC,CACH;CAKA,MAAM,iBAAiB,kBAAkB;CAmBzC,OAjBoC,OAAO,KAAK,UAAU;EAExD,IADiB,MAAM,YAAY,kBACrB;GACZ,IAAI,CAAC,iBACH,OAAO;IAAE,GAAG;IAAO;GAAY;GAEjC,OAAO;IAAE,GAAG;IAAO,QAAQ;IAAY;GAAY;EACrD;EACA,MAAM,QAAQ,mBAAmB,MAAM;EACvC,IAAI,UAAU,KAAA,GACZ,OAAO;GAAE,GAAG;GAAO,QAAQ;GAAO;EAAY;EAEhD,IAAI,gBACF,OAAO;GAAE,GAAG;GAAO,QAAQ;GAAI;EAAY;EAE7C,OAAO;GAAE,GAAG;GAAO;EAAY;CACjC,CACkB;AACpB"}
1
+ {"version":3,"file":"getSolanaBalance.js","names":["callSolanaRpcsWithRetry"],"sources":["../../../src/actions/getSolanaBalance.ts"],"sourcesContent":["import type { SDKClient } from '@lifi/sdk'\nimport {\n type ChainId,\n type Token,\n type TokenAmount,\n withDedupe,\n} from '@lifi/sdk'\nimport { address, type JsonParsedTokenAccount } from '@solana/kit'\n\nimport { callSolanaRpcsWithRetry } from '../rpc/utils.js'\n\nconst SolSystemProgram = '11111111111111111111111111111111'\nconst TokenProgramId = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'\nconst Token2022ProgramId = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'\n\nexport const getSolanaBalance = async (\n client: SDKClient,\n walletAddress: string,\n tokens: Token[]\n): Promise<TokenAmount[]> => {\n if (tokens.length === 0) {\n return []\n }\n const { chainId } = tokens[0]\n for (const token of tokens) {\n if (token.chainId !== chainId) {\n console.warn('Requested tokens have to be on the same chain.')\n }\n }\n\n return getSolanaBalanceDefault(client, chainId, tokens, walletAddress)\n}\n\nconst getSolanaBalanceDefault = async (\n client: SDKClient,\n _chainId: ChainId,\n tokens: Token[],\n walletAddress: string\n): Promise<TokenAmount[]> => {\n // Convert addresses to Solana Kit's address type\n const accountAddress = address(walletAddress)\n const tokenProgramAddress = address(TokenProgramId)\n const token2022ProgramAddress = address(Token2022ProgramId)\n\n // Use Solana Kit's RPC API with the retry wrapper\n const [slot, balance, tokenAccountsByOwner, token2022AccountsByOwner] =\n await Promise.allSettled([\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc.getSlot({ commitment: 'confirmed' }).send()\n ),\n { id: `${getSolanaBalanceDefault.name}.getSlot` }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc.getBalance(accountAddress, { commitment: 'confirmed' }).send()\n ),\n { id: `${getSolanaBalanceDefault.name}.getBalance` }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc\n .getTokenAccountsByOwner(\n accountAddress,\n {\n programId: tokenProgramAddress,\n },\n {\n commitment: 'confirmed',\n encoding: 'jsonParsed',\n }\n )\n .send()\n ),\n {\n id: `${getSolanaBalanceDefault.name}.getTokenAccountsByOwner.${TokenProgramId}`,\n }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc\n .getTokenAccountsByOwner(\n accountAddress,\n {\n programId: token2022ProgramAddress,\n },\n {\n commitment: 'confirmed',\n encoding: 'jsonParsed',\n }\n )\n .send()\n ),\n {\n id: `${getSolanaBalanceDefault.name}.getTokenAccountsByOwner.${Token2022ProgramId}`,\n }\n ),\n ])\n const blockNumber = slot.status === 'fulfilled' ? BigInt(slot.value) : 0n\n const nativeBalanceOk = balance.status === 'fulfilled'\n const solBalance = nativeBalanceOk ? BigInt(balance.value.value) : 0n\n const tokenProgramOk = tokenAccountsByOwner.status === 'fulfilled'\n const token2022ProgramOk = token2022AccountsByOwner.status === 'fulfilled'\n\n const walletTokenAmounts = [\n ...(tokenProgramOk ? tokenAccountsByOwner.value.value : []),\n ...(token2022ProgramOk ? token2022AccountsByOwner.value.value : []),\n ].reduce(\n (tokenAmounts: Record<string, bigint>, value) => {\n const tokenAccount: JsonParsedTokenAccount =\n value.account.data.parsed.info\n const amount = BigInt(tokenAccount.tokenAmount.amount)\n if (amount > 0n) {\n tokenAmounts[tokenAccount.mint] =\n (tokenAmounts[tokenAccount.mint] ?? 0n) + amount\n }\n return tokenAmounts\n },\n {} as Record<string, bigint>\n )\n\n // We can only confidently report 0n for an SPL mint when both Token and\n // Token2022 program queries succeeded — otherwise the mint may live in the\n // program whose query failed (e.g. PYUSD on Token2022).\n const splZeroIsKnown = tokenProgramOk && token2022ProgramOk\n\n const tokenAmounts: TokenAmount[] = tokens.map((token) => {\n const isNative = token.address === SolSystemProgram\n if (isNative) {\n if (!nativeBalanceOk) {\n return { ...token, blockNumber }\n }\n return { ...token, amount: solBalance, blockNumber }\n }\n const found = walletTokenAmounts[token.address]\n if (found !== undefined) {\n return { ...token, amount: found, blockNumber }\n }\n if (splZeroIsKnown) {\n return { ...token, amount: 0n, blockNumber }\n }\n return { ...token, blockNumber }\n })\n return tokenAmounts\n}\n"],"mappings":";;;;;AAWA,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;AACvB,MAAM,qBAAqB;AAE3B,MAAa,mBAAmB,OAC9B,QACA,eACA,WAC2B;CAC3B,IAAI,OAAO,WAAW,GACpB,OAAO,CAAC;CAEV,MAAM,EAAE,YAAY,OAAO;CAC3B,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,YAAY,SACpB,QAAQ,KAAK,gDAAgD;CAIjE,OAAO,wBAAwB,QAAQ,SAAS,QAAQ,aAAa;AACvE;AAEA,MAAM,0BAA0B,OAC9B,QACA,UACA,QACA,kBAC2B;CAE3B,MAAM,kBAAA,GAAA,YAAA,SAAyB,aAAa;CAC5C,MAAM,uBAAA,GAAA,YAAA,SAA8B,cAAc;CAClD,MAAM,2BAAA,GAAA,YAAA,SAAkC,kBAAkB;CAG1D,MAAM,CAAC,MAAM,SAAS,sBAAsB,4BAC1C,MAAM,QAAQ,WAAW;kCAGnBA,kBAAAA,wBAAwB,SAAS,QAC/B,IAAI,QAAQ,EAAE,YAAY,YAAY,CAAC,EAAE,KAAK,CAChD,GACF,EAAE,IAAI,GAAG,wBAAwB,KAAK,UAAU,CAClD;kCAGIA,kBAAAA,wBAAwB,SAAS,QAC/B,IAAI,WAAW,gBAAgB,EAAE,YAAY,YAAY,CAAC,EAAE,KAAK,CACnE,GACF,EAAE,IAAI,GAAG,wBAAwB,KAAK,aAAa,CACrD;kCAGIA,kBAAAA,wBAAwB,SAAS,QAC/B,IACG,wBACC,gBACA,EACE,WAAW,oBACb,GACA;GACE,YAAY;GACZ,UAAU;EACZ,CACF,EACC,KAAK,CACV,GACF,EACE,IAAI,GAAG,wBAAwB,KAAK,2BAA2B,iBACjE,CACF;kCAGIA,kBAAAA,wBAAwB,SAAS,QAC/B,IACG,wBACC,gBACA,EACE,WAAW,wBACb,GACA;GACE,YAAY;GACZ,UAAU;EACZ,CACF,EACC,KAAK,CACV,GACF,EACE,IAAI,GAAG,wBAAwB,KAAK,2BAA2B,qBACjE,CACF;CACF,CAAC;CACH,MAAM,cAAc,KAAK,WAAW,cAAc,OAAO,KAAK,KAAK,IAAI;CACvE,MAAM,kBAAkB,QAAQ,WAAW;CAC3C,MAAM,aAAa,kBAAkB,OAAO,QAAQ,MAAM,KAAK,IAAI;CACnE,MAAM,iBAAiB,qBAAqB,WAAW;CACvD,MAAM,qBAAqB,yBAAyB,WAAW;CAE/D,MAAM,qBAAqB,CACzB,GAAI,iBAAiB,qBAAqB,MAAM,QAAQ,CAAC,GACzD,GAAI,qBAAqB,yBAAyB,MAAM,QAAQ,CAAC,CACnE,EAAE,QACC,cAAsC,UAAU;EAC/C,MAAM,eACJ,MAAM,QAAQ,KAAK,OAAO;EAC5B,MAAM,SAAS,OAAO,aAAa,YAAY,MAAM;EACrD,IAAI,SAAS,IACX,aAAa,aAAa,SACvB,aAAa,aAAa,SAAS,MAAM;EAE9C,OAAO;CACT,GACA,CAAC,CACH;CAKA,MAAM,iBAAiB,kBAAkB;CAmBzC,OAjBoC,OAAO,KAAK,UAAU;EAExD,IADiB,MAAM,YAAY,kBACrB;GACZ,IAAI,CAAC,iBACH,OAAO;IAAE,GAAG;IAAO;GAAY;GAEjC,OAAO;IAAE,GAAG;IAAO,QAAQ;IAAY;GAAY;EACrD;EACA,MAAM,QAAQ,mBAAmB,MAAM;EACvC,IAAI,UAAU,KAAA,GACZ,OAAO;GAAE,GAAG;GAAO,QAAQ;GAAO;EAAY;EAEhD,IAAI,gBACF,OAAO;GAAE,GAAG;GAAO,QAAQ;GAAI;EAAY;EAE7C,OAAO;GAAE,GAAG;GAAO;EAAY;CACjC,CACkB;AACpB"}
@@ -3,6 +3,7 @@ const require_rpc_registry = require("../rpc/registry.js");
3
3
  let _lifi_sdk = require("@lifi/sdk");
4
4
  let _solana_kit = require("@solana/kit");
5
5
  //#region src/actions/sendAndConfirmBundle.ts
6
+ const NULL_BUNDLE_RESULT = /* @__PURE__ */ new Error("Bundle was not confirmed by this RPC");
6
7
  /**
7
8
  * Send and confirm a bundle of transactions using Jito.
8
9
  * Automatically selects a Jito-enabled RPC connection and polls for confirmation
@@ -51,7 +52,11 @@ async function sendAndConfirmBundle(client, signedTransactions) {
51
52
  throw error;
52
53
  }
53
54
  });
54
- const result = await Promise.any(confirmPromises).catch(() => null);
55
+ const result = await Promise.any(confirmPromises.map(async (promise) => {
56
+ const bundleResult = await promise;
57
+ if (!bundleResult) throw NULL_BUNDLE_RESULT;
58
+ return bundleResult;
59
+ })).catch(() => null);
55
60
  if (!abortController.signal.aborted) abortController.abort();
56
61
  if (!result) throw new Error("Failed to send and confirm bundle");
57
62
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"sendAndConfirmBundle.js","names":["getJitoRpcs"],"sources":["../../../src/actions/sendAndConfirmBundle.ts"],"sourcesContent":["import { type SDKClient, sleep } from '@lifi/sdk'\nimport {\n type Commitment,\n getBase64EncodedWireTransaction,\n type Signature,\n type Transaction,\n type TransactionError,\n} from '@solana/kit'\n\nimport { getJitoRpcs } from '../rpc/registry.js'\n\ntype SignatureStatus = {\n slot: bigint\n confirmations: bigint | null\n err: TransactionError | null\n confirmationStatus: Commitment | null\n status: Readonly<{ Err: TransactionError }> | Readonly<{ Ok: null }>\n}\n\nexport type BundleResult = {\n bundleId: string\n txSignatures: Signature[]\n signatureResults: (SignatureStatus | null)[]\n}\n\n/**\n * Send and confirm a bundle of transactions using Jito.\n * Automatically selects a Jito-enabled RPC connection and polls for confirmation\n * across multiple Jito RPCs in parallel.\n * @param client - The SDK client.\n * @param signedTransactions - Array of signed transactions to bundle.\n * @returns BundleResult containing Bundle ID, transaction signatures, and confirmation results.\n */\nexport async function sendAndConfirmBundle(\n client: SDKClient,\n signedTransactions: Transaction[]\n): Promise<BundleResult> {\n const jitoRpcs = await getJitoRpcs(client)\n\n if (jitoRpcs.length === 0) {\n throw new Error(\n 'No Jito-enabled RPC connection available for bundle submission'\n )\n }\n\n // Serialize transactions to base64\n const serializedTransactions = signedTransactions.map((tx) =>\n getBase64EncodedWireTransaction(tx)\n )\n\n const abortController = new AbortController()\n\n const confirmPromises = jitoRpcs.map(async (jitoRpc) => {\n try {\n // Send bundle to Jito\n let bundleId: string\n try {\n bundleId = await jitoRpc.sendBundle(serializedTransactions).send()\n } catch (_) {\n return null\n }\n\n const [{ value: blockhashResult }, initialBlockHeight] =\n await Promise.all([\n jitoRpc\n .getLatestBlockhash({\n commitment: 'confirmed',\n })\n .send(),\n jitoRpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send(),\n ])\n\n let currentBlockHeight = initialBlockHeight\n\n while (\n currentBlockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted\n ) {\n const statusResponse = await jitoRpc\n .getBundleStatuses([bundleId])\n .send()\n\n const bundleStatus = statusResponse.value[0]\n\n // Check if bundle is confirmed or finalized\n if (\n bundleStatus &&\n (bundleStatus.confirmation_status === 'confirmed' ||\n bundleStatus.confirmation_status === 'finalized')\n ) {\n // Bundle confirmed! Extract transaction signatures from bundle status\n const txSignatures = bundleStatus.transactions\n\n // Fetch individual signature results from Jito RPC\n const sigResponse = await jitoRpc\n .getSignatureStatuses(txSignatures)\n .send()\n\n if (!sigResponse?.value || !Array.isArray(sigResponse.value)) {\n // Keep polling if can't find signature results\n await sleep(400)\n continue\n }\n\n // Immediately abort all other connections when we find a result\n abortController.abort()\n return {\n bundleId,\n txSignatures,\n signatureResults: sigResponse.value,\n }\n }\n\n await sleep(400)\n if (!abortController.signal.aborted) {\n currentBlockHeight = await jitoRpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send()\n }\n }\n\n return null\n } catch (error) {\n if (abortController.signal.aborted) {\n return null // Don't treat abortion as an error\n }\n throw error\n }\n })\n\n // Wait for first successful confirmation\n const result = await Promise.any(confirmPromises).catch(() => null)\n\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n\n if (!result) {\n throw new Error('Failed to send and confirm bundle')\n }\n\n return result\n}\n"],"mappings":";;;;;;;;;;;;;AAiCA,eAAsB,qBACpB,QACA,oBACuB;CACvB,MAAM,WAAW,MAAMA,qBAAAA,YAAY,MAAM;CAEzC,IAAI,SAAS,WAAW,GACtB,MAAM,IAAI,MACR,gEACF;CAIF,MAAM,yBAAyB,mBAAmB,KAAK,QAAA,GAAA,YAAA,iCACrB,EAAE,CACpC;CAEA,MAAM,kBAAkB,IAAI,gBAAgB;CAE5C,MAAM,kBAAkB,SAAS,IAAI,OAAO,YAAY;EACtD,IAAI;GAEF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,QAAQ,WAAW,sBAAsB,EAAE,KAAK;GACnE,SAAS,GAAG;IACV,OAAO;GACT;GAEA,MAAM,CAAC,EAAE,OAAO,mBAAmB,sBACjC,MAAM,QAAQ,IAAI,CAChB,QACG,mBAAmB,EAClB,YAAY,YACd,CAAC,EACA,KAAK,GACR,QACG,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK,CACV,CAAC;GAEH,IAAI,qBAAqB;GAEzB,OACE,qBAAqB,gBAAgB,wBACrC,CAAC,gBAAgB,OAAO,SACxB;IAKA,MAAM,gBAAe,MAJQ,QAC1B,kBAAkB,CAAC,QAAQ,CAAC,EAC5B,KAAK,GAE4B,MAAM;IAG1C,IACE,iBACC,aAAa,wBAAwB,eACpC,aAAa,wBAAwB,cACvC;KAEA,MAAM,eAAe,aAAa;KAGlC,MAAM,cAAc,MAAM,QACvB,qBAAqB,YAAY,EACjC,KAAK;KAER,IAAI,CAAC,aAAa,SAAS,CAAC,MAAM,QAAQ,YAAY,KAAK,GAAG;MAE5D,OAAA,GAAA,UAAA,OAAY,GAAG;MACf;KACF;KAGA,gBAAgB,MAAM;KACtB,OAAO;MACL;MACA;MACA,kBAAkB,YAAY;KAChC;IACF;IAEA,OAAA,GAAA,UAAA,OAAY,GAAG;IACf,IAAI,CAAC,gBAAgB,OAAO,SAC1B,qBAAqB,MAAM,QACxB,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK;GAEZ;GAEA,OAAO;EACT,SAAS,OAAO;GACd,IAAI,gBAAgB,OAAO,SACzB,OAAO;GAET,MAAM;EACR;CACF,CAAC;CAGD,MAAM,SAAS,MAAM,QAAQ,IAAI,eAAe,EAAE,YAAY,IAAI;CAElE,IAAI,CAAC,gBAAgB,OAAO,SAC1B,gBAAgB,MAAM;CAGxB,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mCAAmC;CAGrD,OAAO;AACT"}
1
+ {"version":3,"file":"sendAndConfirmBundle.js","names":["getJitoRpcs"],"sources":["../../../src/actions/sendAndConfirmBundle.ts"],"sourcesContent":["import { type SDKClient, sleep } from '@lifi/sdk'\nimport {\n type Commitment,\n getBase64EncodedWireTransaction,\n type Signature,\n type Transaction,\n type TransactionError,\n} from '@solana/kit'\n\nimport { getJitoRpcs } from '../rpc/registry.js'\n\ntype SignatureStatus = {\n slot: bigint\n confirmations: bigint | null\n err: TransactionError | null\n confirmationStatus: Commitment | null\n status: Readonly<{ Err: TransactionError }> | Readonly<{ Ok: null }>\n}\n\nexport type BundleResult = {\n bundleId: string\n txSignatures: Signature[]\n signatureResults: (SignatureStatus | null)[]\n}\n\nconst NULL_BUNDLE_RESULT = new Error('Bundle was not confirmed by this RPC')\n\n/**\n * Send and confirm a bundle of transactions using Jito.\n * Automatically selects a Jito-enabled RPC connection and polls for confirmation\n * across multiple Jito RPCs in parallel.\n * @param client - The SDK client.\n * @param signedTransactions - Array of signed transactions to bundle.\n * @returns BundleResult containing Bundle ID, transaction signatures, and confirmation results.\n */\nexport async function sendAndConfirmBundle(\n client: SDKClient,\n signedTransactions: Transaction[]\n): Promise<BundleResult> {\n const jitoRpcs = await getJitoRpcs(client)\n\n if (jitoRpcs.length === 0) {\n throw new Error(\n 'No Jito-enabled RPC connection available for bundle submission'\n )\n }\n\n // Serialize transactions to base64\n const serializedTransactions = signedTransactions.map((tx) =>\n getBase64EncodedWireTransaction(tx)\n )\n\n const abortController = new AbortController()\n\n const confirmPromises = jitoRpcs.map(async (jitoRpc) => {\n try {\n // Send bundle to Jito\n let bundleId: string\n try {\n bundleId = await jitoRpc.sendBundle(serializedTransactions).send()\n } catch (_) {\n return null\n }\n\n const [{ value: blockhashResult }, initialBlockHeight] =\n await Promise.all([\n jitoRpc\n .getLatestBlockhash({\n commitment: 'confirmed',\n })\n .send(),\n jitoRpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send(),\n ])\n\n let currentBlockHeight = initialBlockHeight\n\n while (\n currentBlockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted\n ) {\n const statusResponse = await jitoRpc\n .getBundleStatuses([bundleId])\n .send()\n\n const bundleStatus = statusResponse.value[0]\n\n // Check if bundle is confirmed or finalized\n if (\n bundleStatus &&\n (bundleStatus.confirmation_status === 'confirmed' ||\n bundleStatus.confirmation_status === 'finalized')\n ) {\n // Bundle confirmed! Extract transaction signatures from bundle status\n const txSignatures = bundleStatus.transactions\n\n // Fetch individual signature results from Jito RPC\n const sigResponse = await jitoRpc\n .getSignatureStatuses(txSignatures)\n .send()\n\n if (!sigResponse?.value || !Array.isArray(sigResponse.value)) {\n // Keep polling if can't find signature results\n await sleep(400)\n continue\n }\n\n // Immediately abort all other connections when we find a result\n abortController.abort()\n return {\n bundleId,\n txSignatures,\n signatureResults: sigResponse.value,\n }\n }\n\n await sleep(400)\n if (!abortController.signal.aborted) {\n currentBlockHeight = await jitoRpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send()\n }\n }\n\n return null\n } catch (error) {\n if (abortController.signal.aborted) {\n return null // Don't treat abortion as an error\n }\n throw error\n }\n })\n\n // Wait for first successful confirmation\n const result = await Promise.any(\n confirmPromises.map(async (promise) => {\n const bundleResult = await promise\n if (!bundleResult) {\n throw NULL_BUNDLE_RESULT\n }\n return bundleResult\n })\n ).catch(() => null)\n\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n\n if (!result) {\n throw new Error('Failed to send and confirm bundle')\n }\n\n return result\n}\n"],"mappings":";;;;;AAyBA,MAAM,qCAAqB,IAAI,MAAM,sCAAsC;;;;;;;;;AAU3E,eAAsB,qBACpB,QACA,oBACuB;CACvB,MAAM,WAAW,MAAMA,qBAAAA,YAAY,MAAM;CAEzC,IAAI,SAAS,WAAW,GACtB,MAAM,IAAI,MACR,gEACF;CAIF,MAAM,yBAAyB,mBAAmB,KAAK,QAAA,GAAA,YAAA,iCACrB,EAAE,CACpC;CAEA,MAAM,kBAAkB,IAAI,gBAAgB;CAE5C,MAAM,kBAAkB,SAAS,IAAI,OAAO,YAAY;EACtD,IAAI;GAEF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,QAAQ,WAAW,sBAAsB,EAAE,KAAK;GACnE,SAAS,GAAG;IACV,OAAO;GACT;GAEA,MAAM,CAAC,EAAE,OAAO,mBAAmB,sBACjC,MAAM,QAAQ,IAAI,CAChB,QACG,mBAAmB,EAClB,YAAY,YACd,CAAC,EACA,KAAK,GACR,QACG,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK,CACV,CAAC;GAEH,IAAI,qBAAqB;GAEzB,OACE,qBAAqB,gBAAgB,wBACrC,CAAC,gBAAgB,OAAO,SACxB;IAKA,MAAM,gBAAe,MAJQ,QAC1B,kBAAkB,CAAC,QAAQ,CAAC,EAC5B,KAAK,GAE4B,MAAM;IAG1C,IACE,iBACC,aAAa,wBAAwB,eACpC,aAAa,wBAAwB,cACvC;KAEA,MAAM,eAAe,aAAa;KAGlC,MAAM,cAAc,MAAM,QACvB,qBAAqB,YAAY,EACjC,KAAK;KAER,IAAI,CAAC,aAAa,SAAS,CAAC,MAAM,QAAQ,YAAY,KAAK,GAAG;MAE5D,OAAA,GAAA,UAAA,OAAY,GAAG;MACf;KACF;KAGA,gBAAgB,MAAM;KACtB,OAAO;MACL;MACA;MACA,kBAAkB,YAAY;KAChC;IACF;IAEA,OAAA,GAAA,UAAA,OAAY,GAAG;IACf,IAAI,CAAC,gBAAgB,OAAO,SAC1B,qBAAqB,MAAM,QACxB,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK;GAEZ;GAEA,OAAO;EACT,SAAS,OAAO;GACd,IAAI,gBAAgB,OAAO,SACzB,OAAO;GAET,MAAM;EACR;CACF,CAAC;CAGD,MAAM,SAAS,MAAM,QAAQ,IAC3B,gBAAgB,IAAI,OAAO,YAAY;EACrC,MAAM,eAAe,MAAM;EAC3B,IAAI,CAAC,cACH,MAAM;EAER,OAAO;CACT,CAAC,CACH,EAAE,YAAY,IAAI;CAElB,IAAI,CAAC,gBAAgB,OAAO,SAC1B,gBAAgB,MAAM;CAGxB,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mCAAmC;CAGrD,OAAO;AACT"}
@@ -3,6 +3,7 @@ const require_rpc_registry = require("../rpc/registry.js");
3
3
  let _lifi_sdk = require("@lifi/sdk");
4
4
  let _solana_kit = require("@solana/kit");
5
5
  //#region src/actions/sendAndConfirmTransaction.ts
6
+ const NULL_CONFIRMATION_RESULT = /* @__PURE__ */ new Error("Transaction was not confirmed by this RPC");
6
7
  /**
7
8
  * Sends a Solana transaction to multiple RPC endpoints and returns the confirmation
8
9
  * as soon as any of them confirm the transaction.
@@ -58,7 +59,11 @@ async function sendAndConfirmTransaction(client, signedTransaction) {
58
59
  throw error;
59
60
  }
60
61
  });
61
- const signatureResult = await Promise.any(confirmPromises).catch(() => null);
62
+ const signatureResult = await Promise.any(confirmPromises.map(async (promise) => {
63
+ const result = await promise;
64
+ if (!result) throw NULL_CONFIRMATION_RESULT;
65
+ return result;
66
+ })).catch(() => null);
62
67
  if (!abortController.signal.aborted) abortController.abort();
63
68
  return {
64
69
  signatureResult,
@@ -1 +1 @@
1
- {"version":3,"file":"sendAndConfirmTransaction.js","names":["getSolanaRpcs"],"sources":["../../../src/actions/sendAndConfirmTransaction.ts"],"sourcesContent":["import { type SDKClient, sleep } from '@lifi/sdk'\nimport {\n type Commitment,\n getBase64EncodedWireTransaction,\n getSignatureFromTransaction,\n type Transaction,\n type TransactionError,\n} from '@solana/kit'\nimport { getSolanaRpcs } from '../rpc/registry.js'\n\ntype SignatureStatus = {\n slot: bigint\n confirmations: bigint | null\n err: TransactionError | null\n confirmationStatus: Commitment | null\n status: Readonly<{ Err: TransactionError }> | Readonly<{ Ok: null }>\n}\n\ntype ConfirmedTransactionResult = {\n signatureResult: SignatureStatus | null\n txSignature: string\n}\n\n/**\n * Sends a Solana transaction to multiple RPC endpoints and returns the confirmation\n * as soon as any of them confirm the transaction.\n * @param client - The SDK client.\n * @param signedTransaction - The signed transaction to send.\n * @returns - The confirmation result of the transaction.\n */\nexport async function sendAndConfirmTransaction(\n client: SDKClient,\n signedTransaction: Transaction\n): Promise<ConfirmedTransactionResult> {\n const solanaRpcs = await getSolanaRpcs(client)\n\n const signedTxSerialized = getBase64EncodedWireTransaction(signedTransaction)\n // Create transaction hash (signature)\n const txSignature = getSignatureFromTransaction(signedTransaction)\n\n if (!txSignature) {\n throw new Error('Transaction signature is missing.')\n }\n\n const rawTransactionOptions = {\n // We can skip preflight check after the first transaction has been sent\n // https://solana.com/docs/advanced/retry#the-cost-of-skipping-preflight\n skipPreflight: true,\n // Setting max retries to 0 as we are handling retries manually\n maxRetries: BigInt(0),\n // https://solana.com/docs/advanced/confirmation#use-an-appropriate-preflight-commitment-level\n preflightCommitment: 'confirmed' as Commitment,\n encoding: 'base64' as const,\n }\n\n const abortController = new AbortController()\n\n const confirmPromises = solanaRpcs.map(async (rpc) => {\n try {\n // Send initial transaction for this RPC\n try {\n await rpc\n .sendTransaction(signedTxSerialized, rawTransactionOptions)\n .send()\n } catch (_) {\n // Continue with confirmation even if initial send fails\n }\n\n const [{ value: blockhashResult }, initialBlockHeight] =\n await Promise.all([\n rpc\n .getLatestBlockhash({\n commitment: 'confirmed',\n })\n .send(),\n rpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send(),\n ])\n\n let signatureResult: SignatureStatus | null = null\n let blockHeight = initialBlockHeight\n const pollingPromise = (async () => {\n while (\n blockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted\n ) {\n const statusResponse = await rpc\n .getSignatureStatuses([txSignature])\n .send()\n\n const status = statusResponse.value[0]\n if (\n status &&\n (status.confirmationStatus === 'confirmed' ||\n status.confirmationStatus === 'finalized')\n ) {\n signatureResult = status\n // Immediately abort all other RPCs when we find a result\n abortController.abort()\n return status\n }\n\n await sleep(400)\n }\n return null\n })()\n\n const sendingPromise = (async () => {\n while (\n blockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted &&\n !signatureResult\n ) {\n try {\n await rpc\n .sendTransaction(signedTxSerialized, rawTransactionOptions)\n .send()\n } catch (_) {\n // Continue trying even if individual sends fail\n }\n\n await sleep(1000)\n if (!abortController.signal.aborted) {\n blockHeight = await rpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send()\n }\n }\n return null\n })()\n\n // Wait for polling to find the result\n const result = await Promise.race([pollingPromise, sendingPromise])\n return result\n } catch (error) {\n if (abortController.signal.aborted) {\n return null // Don't treat abortion as an error\n }\n throw error\n }\n })\n\n const signatureResult = await Promise.any(confirmPromises).catch(() => null)\n\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n\n return { signatureResult, txSignature }\n}\n"],"mappings":";;;;;;;;;;;;AA8BA,eAAsB,0BACpB,QACA,mBACqC;CACrC,MAAM,aAAa,MAAMA,qBAAAA,cAAc,MAAM;CAE7C,MAAM,sBAAA,GAAA,YAAA,iCAAqD,iBAAiB;CAE5E,MAAM,eAAA,GAAA,YAAA,6BAA0C,iBAAiB;CAEjE,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,mCAAmC;CAGrD,MAAM,wBAAwB;EAG5B,eAAe;EAEf,YAAY,OAAO,CAAC;EAEpB,qBAAqB;EACrB,UAAU;CACZ;CAEA,MAAM,kBAAkB,IAAI,gBAAgB;CAE5C,MAAM,kBAAkB,WAAW,IAAI,OAAO,QAAQ;EACpD,IAAI;GAEF,IAAI;IACF,MAAM,IACH,gBAAgB,oBAAoB,qBAAqB,EACzD,KAAK;GACV,SAAS,GAAG,CAEZ;GAEA,MAAM,CAAC,EAAE,OAAO,mBAAmB,sBACjC,MAAM,QAAQ,IAAI,CAChB,IACG,mBAAmB,EAClB,YAAY,YACd,CAAC,EACA,KAAK,GACR,IACG,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK,CACV,CAAC;GAEH,IAAI,kBAA0C;GAC9C,IAAI,cAAc;GAClB,MAAM,kBAAkB,YAAY;IAClC,OACE,cAAc,gBAAgB,wBAC9B,CAAC,gBAAgB,OAAO,SACxB;KAKA,MAAM,UAAS,MAJc,IAC1B,qBAAqB,CAAC,WAAW,CAAC,EAClC,KAAK,GAEsB,MAAM;KACpC,IACE,WACC,OAAO,uBAAuB,eAC7B,OAAO,uBAAuB,cAChC;MACA,kBAAkB;MAElB,gBAAgB,MAAM;MACtB,OAAO;KACT;KAEA,OAAA,GAAA,UAAA,OAAY,GAAG;IACjB;IACA,OAAO;GACT,GAAG;GAEH,MAAM,kBAAkB,YAAY;IAClC,OACE,cAAc,gBAAgB,wBAC9B,CAAC,gBAAgB,OAAO,WACxB,CAAC,iBACD;KACA,IAAI;MACF,MAAM,IACH,gBAAgB,oBAAoB,qBAAqB,EACzD,KAAK;KACV,SAAS,GAAG,CAEZ;KAEA,OAAA,GAAA,UAAA,OAAY,GAAI;KAChB,IAAI,CAAC,gBAAgB,OAAO,SAC1B,cAAc,MAAM,IACjB,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK;IAEZ;IACA,OAAO;GACT,GAAG;GAIH,OAAO,MADc,QAAQ,KAAK,CAAC,gBAAgB,cAAc,CAAC;EAEpE,SAAS,OAAO;GACd,IAAI,gBAAgB,OAAO,SACzB,OAAO;GAET,MAAM;EACR;CACF,CAAC;CAED,MAAM,kBAAkB,MAAM,QAAQ,IAAI,eAAe,EAAE,YAAY,IAAI;CAE3E,IAAI,CAAC,gBAAgB,OAAO,SAC1B,gBAAgB,MAAM;CAGxB,OAAO;EAAE;EAAiB;CAAY;AACxC"}
1
+ {"version":3,"file":"sendAndConfirmTransaction.js","names":["getSolanaRpcs"],"sources":["../../../src/actions/sendAndConfirmTransaction.ts"],"sourcesContent":["import { type SDKClient, sleep } from '@lifi/sdk'\nimport {\n type Commitment,\n getBase64EncodedWireTransaction,\n getSignatureFromTransaction,\n type Transaction,\n type TransactionError,\n} from '@solana/kit'\nimport { getSolanaRpcs } from '../rpc/registry.js'\n\ntype SignatureStatus = {\n slot: bigint\n confirmations: bigint | null\n err: TransactionError | null\n confirmationStatus: Commitment | null\n status: Readonly<{ Err: TransactionError }> | Readonly<{ Ok: null }>\n}\n\ntype ConfirmedTransactionResult = {\n signatureResult: SignatureStatus | null\n txSignature: string\n}\n\nconst NULL_CONFIRMATION_RESULT = new Error(\n 'Transaction was not confirmed by this RPC'\n)\n\n/**\n * Sends a Solana transaction to multiple RPC endpoints and returns the confirmation\n * as soon as any of them confirm the transaction.\n * @param client - The SDK client.\n * @param signedTransaction - The signed transaction to send.\n * @returns - The confirmation result of the transaction.\n */\nexport async function sendAndConfirmTransaction(\n client: SDKClient,\n signedTransaction: Transaction\n): Promise<ConfirmedTransactionResult> {\n const solanaRpcs = await getSolanaRpcs(client)\n\n const signedTxSerialized = getBase64EncodedWireTransaction(signedTransaction)\n // Create transaction hash (signature)\n const txSignature = getSignatureFromTransaction(signedTransaction)\n\n if (!txSignature) {\n throw new Error('Transaction signature is missing.')\n }\n\n const rawTransactionOptions = {\n // We can skip preflight check after the first transaction has been sent\n // https://solana.com/docs/advanced/retry#the-cost-of-skipping-preflight\n skipPreflight: true,\n // Setting max retries to 0 as we are handling retries manually\n maxRetries: BigInt(0),\n // https://solana.com/docs/advanced/confirmation#use-an-appropriate-preflight-commitment-level\n preflightCommitment: 'confirmed' as Commitment,\n encoding: 'base64' as const,\n }\n\n const abortController = new AbortController()\n\n const confirmPromises = solanaRpcs.map(async (rpc) => {\n try {\n // Send initial transaction for this RPC\n try {\n await rpc\n .sendTransaction(signedTxSerialized, rawTransactionOptions)\n .send()\n } catch (_) {\n // Continue with confirmation even if initial send fails\n }\n\n const [{ value: blockhashResult }, initialBlockHeight] =\n await Promise.all([\n rpc\n .getLatestBlockhash({\n commitment: 'confirmed',\n })\n .send(),\n rpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send(),\n ])\n\n let signatureResult: SignatureStatus | null = null\n let blockHeight = initialBlockHeight\n const pollingPromise = (async () => {\n while (\n blockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted\n ) {\n const statusResponse = await rpc\n .getSignatureStatuses([txSignature])\n .send()\n\n const status = statusResponse.value[0]\n if (\n status &&\n (status.confirmationStatus === 'confirmed' ||\n status.confirmationStatus === 'finalized')\n ) {\n signatureResult = status\n // Immediately abort all other RPCs when we find a result\n abortController.abort()\n return status\n }\n\n await sleep(400)\n }\n return null\n })()\n\n const sendingPromise = (async () => {\n while (\n blockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted &&\n !signatureResult\n ) {\n try {\n await rpc\n .sendTransaction(signedTxSerialized, rawTransactionOptions)\n .send()\n } catch (_) {\n // Continue trying even if individual sends fail\n }\n\n await sleep(1000)\n if (!abortController.signal.aborted) {\n blockHeight = await rpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send()\n }\n }\n return null\n })()\n\n // Wait for polling to find the result\n const result = await Promise.race([pollingPromise, sendingPromise])\n return result\n } catch (error) {\n if (abortController.signal.aborted) {\n return null // Don't treat abortion as an error\n }\n throw error\n }\n })\n\n const signatureResult = await Promise.any(\n confirmPromises.map(async (promise) => {\n const result = await promise\n if (!result) {\n throw NULL_CONFIRMATION_RESULT\n }\n return result\n })\n ).catch(() => null)\n\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n\n return { signatureResult, txSignature }\n}\n"],"mappings":";;;;;AAuBA,MAAM,2CAA2B,IAAI,MACnC,2CACF;;;;;;;;AASA,eAAsB,0BACpB,QACA,mBACqC;CACrC,MAAM,aAAa,MAAMA,qBAAAA,cAAc,MAAM;CAE7C,MAAM,sBAAA,GAAA,YAAA,iCAAqD,iBAAiB;CAE5E,MAAM,eAAA,GAAA,YAAA,6BAA0C,iBAAiB;CAEjE,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,mCAAmC;CAGrD,MAAM,wBAAwB;EAG5B,eAAe;EAEf,YAAY,OAAO,CAAC;EAEpB,qBAAqB;EACrB,UAAU;CACZ;CAEA,MAAM,kBAAkB,IAAI,gBAAgB;CAE5C,MAAM,kBAAkB,WAAW,IAAI,OAAO,QAAQ;EACpD,IAAI;GAEF,IAAI;IACF,MAAM,IACH,gBAAgB,oBAAoB,qBAAqB,EACzD,KAAK;GACV,SAAS,GAAG,CAEZ;GAEA,MAAM,CAAC,EAAE,OAAO,mBAAmB,sBACjC,MAAM,QAAQ,IAAI,CAChB,IACG,mBAAmB,EAClB,YAAY,YACd,CAAC,EACA,KAAK,GACR,IACG,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK,CACV,CAAC;GAEH,IAAI,kBAA0C;GAC9C,IAAI,cAAc;GAClB,MAAM,kBAAkB,YAAY;IAClC,OACE,cAAc,gBAAgB,wBAC9B,CAAC,gBAAgB,OAAO,SACxB;KAKA,MAAM,UAAS,MAJc,IAC1B,qBAAqB,CAAC,WAAW,CAAC,EAClC,KAAK,GAEsB,MAAM;KACpC,IACE,WACC,OAAO,uBAAuB,eAC7B,OAAO,uBAAuB,cAChC;MACA,kBAAkB;MAElB,gBAAgB,MAAM;MACtB,OAAO;KACT;KAEA,OAAA,GAAA,UAAA,OAAY,GAAG;IACjB;IACA,OAAO;GACT,GAAG;GAEH,MAAM,kBAAkB,YAAY;IAClC,OACE,cAAc,gBAAgB,wBAC9B,CAAC,gBAAgB,OAAO,WACxB,CAAC,iBACD;KACA,IAAI;MACF,MAAM,IACH,gBAAgB,oBAAoB,qBAAqB,EACzD,KAAK;KACV,SAAS,GAAG,CAEZ;KAEA,OAAA,GAAA,UAAA,OAAY,GAAI;KAChB,IAAI,CAAC,gBAAgB,OAAO,SAC1B,cAAc,MAAM,IACjB,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK;IAEZ;IACA,OAAO;GACT,GAAG;GAIH,OAAO,MADc,QAAQ,KAAK,CAAC,gBAAgB,cAAc,CAAC;EAEpE,SAAS,OAAO;GACd,IAAI,gBAAgB,OAAO,SACzB,OAAO;GAET,MAAM;EACR;CACF,CAAC;CAED,MAAM,kBAAkB,MAAM,QAAQ,IACpC,gBAAgB,IAAI,OAAO,YAAY;EACrC,MAAM,SAAS,MAAM;EACrB,IAAI,CAAC,QACH,MAAM;EAER,OAAO;CACT,CAAC,CACH,EAAE,YAAY,IAAI;CAElB,IAAI,CAAC,gBAAgB,OAAO,SAC1B,gBAAgB,MAAM;CAGxB,OAAO;EAAE;EAAiB;CAAY;AACxC"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_actions_sendAndConfirmBundle = require("../../actions/sendAndConfirmBundle.js");
3
+ const require_utils_solanaErrorCause = require("../../utils/solanaErrorCause.js");
3
4
  let _lifi_sdk = require("@lifi/sdk");
4
5
  //#region src/core/tasks/SolanaJitoWaitForTransactionTask.ts
5
6
  var SolanaJitoWaitForTransactionTask = class extends _lifi_sdk.BaseStepExecutionTask {
@@ -8,12 +9,13 @@ var SolanaJitoWaitForTransactionTask = class extends _lifi_sdk.BaseStepExecution
8
9
  const signedTransactions = contextSignedTransactions ?? [];
9
10
  const action = statusManager.findAction(step, isBridgeExecution ? "CROSS_CHAIN" : "SWAP");
10
11
  if (!action) throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionUnprepared, "Unable to prepare transaction. Action not found.");
12
+ if (!signedTransactions.length) throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionUnprepared, "Unable to prepare transaction. Signed transactions are not found.");
11
13
  const bundleResult = await require_actions_sendAndConfirmBundle.sendAndConfirmBundle(client, signedTransactions);
12
14
  if (!bundleResult.signatureResults.every((result) => result !== null)) throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionFailed, "Bundle confirmation failed: Not all transactions were confirmed.");
13
15
  const failedResult = bundleResult.signatureResults.find((result) => result?.err);
14
16
  if (failedResult?.err) {
15
- const reason = typeof failedResult.err === "object" ? JSON.stringify(failedResult.err) : String(failedResult.err);
16
- throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionFailed, `Transaction failed: ${reason}`);
17
+ const cause = new require_utils_solanaErrorCause.SolanaTransactionDetailsError(failedResult.err);
18
+ throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionFailed, `Transaction failed: ${cause.message}`, cause);
17
19
  }
18
20
  const confirmedTransaction = {
19
21
  txSignature: bundleResult.txSignatures[0],
@@ -1 +1 @@
1
- {"version":3,"file":"SolanaJitoWaitForTransactionTask.js","names":["BaseStepExecutionTask","TransactionError","LiFiErrorCode","sendAndConfirmBundle"],"sources":["../../../../src/core/tasks/SolanaJitoWaitForTransactionTask.ts"],"sourcesContent":["import {\n BaseStepExecutionTask,\n LiFiErrorCode,\n type TaskResult,\n TransactionError,\n} from '@lifi/sdk'\nimport { sendAndConfirmBundle } from '../../actions/sendAndConfirmBundle.js'\nimport type { SolanaStepExecutorContext } from '../../types.js'\n\nexport class SolanaJitoWaitForTransactionTask extends BaseStepExecutionTask {\n async run(context: SolanaStepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n fromChain,\n isBridgeExecution,\n signedTransactions: contextSignedTransactions,\n } = context\n\n const signedTransactions = contextSignedTransactions ?? []\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n // Use Jito bundle for transaction submission\n const bundleResult = await sendAndConfirmBundle(client, signedTransactions)\n\n const allConfirmed = bundleResult.signatureResults.every(\n (result) => result !== null\n )\n\n if (!allConfirmed) {\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n 'Bundle confirmation failed: Not all transactions were confirmed.'\n )\n }\n\n // Check for errors in any of the transactions\n const failedResult = bundleResult.signatureResults.find(\n (result) => result?.err\n )\n if (failedResult?.err) {\n const reason =\n typeof failedResult.err === 'object'\n ? JSON.stringify(failedResult.err)\n : String(failedResult.err)\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n `Transaction failed: ${reason}`\n )\n }\n\n const confirmedTransaction = {\n txSignature: bundleResult.txSignatures[0],\n bundleId: bundleResult.bundleId,\n }\n\n // Transaction has been confirmed and we can update the action\n statusManager.updateAction(step, action.type, 'PENDING', {\n txHash: confirmedTransaction.txSignature,\n txLink: `${fromChain.metamask.blockExplorerUrls[0]}tx/${confirmedTransaction.txSignature}`,\n })\n\n if (isBridgeExecution) {\n statusManager.updateAction(step, action.type, 'DONE')\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;AASA,IAAa,mCAAb,cAAsDA,UAAAA,sBAAsB;CAC1E,MAAM,IAAI,SAAyD;EACjE,MAAM,EACJ,QACA,MACA,eACA,WACA,mBACA,oBAAoB,8BAClB;EAEJ,MAAM,qBAAqB,6BAA6B,CAAC;EAEzD,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EACA,IAAI,CAAC,QACH,MAAM,IAAIC,UAAAA,iBACRC,UAAAA,cAAc,uBACd,kDACF;EAIF,MAAM,eAAe,MAAMC,qCAAAA,qBAAqB,QAAQ,kBAAkB;EAM1E,IAAI,CAJiB,aAAa,iBAAiB,OAChD,WAAW,WAAW,IAGT,GACd,MAAM,IAAIF,UAAAA,iBACRC,UAAAA,cAAc,mBACd,kEACF;EAIF,MAAM,eAAe,aAAa,iBAAiB,MAChD,WAAW,QAAQ,GACtB;EACA,IAAI,cAAc,KAAK;GACrB,MAAM,SACJ,OAAO,aAAa,QAAQ,WACxB,KAAK,UAAU,aAAa,GAAG,IAC/B,OAAO,aAAa,GAAG;GAC7B,MAAM,IAAID,UAAAA,iBACRC,UAAAA,cAAc,mBACd,uBAAuB,QACzB;EACF;EAEA,MAAM,uBAAuB;GAC3B,aAAa,aAAa,aAAa;GACvC,UAAU,aAAa;EACzB;EAGA,cAAc,aAAa,MAAM,OAAO,MAAM,WAAW;GACvD,QAAQ,qBAAqB;GAC7B,QAAQ,GAAG,UAAU,SAAS,kBAAkB,GAAG,KAAK,qBAAqB;EAC/E,CAAC;EAED,IAAI,mBACF,cAAc,aAAa,MAAM,OAAO,MAAM,MAAM;EAGtD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
1
+ {"version":3,"file":"SolanaJitoWaitForTransactionTask.js","names":["BaseStepExecutionTask","TransactionError","LiFiErrorCode","sendAndConfirmBundle","SolanaTransactionDetailsError"],"sources":["../../../../src/core/tasks/SolanaJitoWaitForTransactionTask.ts"],"sourcesContent":["import {\n BaseStepExecutionTask,\n LiFiErrorCode,\n type TaskResult,\n TransactionError,\n} from '@lifi/sdk'\nimport { sendAndConfirmBundle } from '../../actions/sendAndConfirmBundle.js'\nimport type { SolanaStepExecutorContext } from '../../types.js'\nimport { SolanaTransactionDetailsError } from '../../utils/solanaErrorCause.js'\n\nexport class SolanaJitoWaitForTransactionTask extends BaseStepExecutionTask {\n async run(context: SolanaStepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n fromChain,\n isBridgeExecution,\n signedTransactions: contextSignedTransactions,\n } = context\n\n const signedTransactions = contextSignedTransactions ?? []\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (!signedTransactions.length) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Signed transactions are not found.'\n )\n }\n\n // Use Jito bundle for transaction submission\n const bundleResult = await sendAndConfirmBundle(client, signedTransactions)\n\n const allConfirmed = bundleResult.signatureResults.every(\n (result) => result !== null\n )\n\n if (!allConfirmed) {\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n 'Bundle confirmation failed: Not all transactions were confirmed.'\n )\n }\n\n // Check for errors in any of the transactions\n const failedResult = bundleResult.signatureResults.find(\n (result) => result?.err\n )\n if (failedResult?.err) {\n const cause = new SolanaTransactionDetailsError(failedResult.err)\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n `Transaction failed: ${cause.message}`,\n cause\n )\n }\n\n const confirmedTransaction = {\n txSignature: bundleResult.txSignatures[0],\n bundleId: bundleResult.bundleId,\n }\n\n // Transaction has been confirmed and we can update the action\n statusManager.updateAction(step, action.type, 'PENDING', {\n txHash: confirmedTransaction.txSignature,\n txLink: `${fromChain.metamask.blockExplorerUrls[0]}tx/${confirmedTransaction.txSignature}`,\n })\n\n if (isBridgeExecution) {\n statusManager.updateAction(step, action.type, 'DONE')\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;AAUA,IAAa,mCAAb,cAAsDA,UAAAA,sBAAsB;CAC1E,MAAM,IAAI,SAAyD;EACjE,MAAM,EACJ,QACA,MACA,eACA,WACA,mBACA,oBAAoB,8BAClB;EAEJ,MAAM,qBAAqB,6BAA6B,CAAC;EAEzD,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EACA,IAAI,CAAC,QACH,MAAM,IAAIC,UAAAA,iBACRC,UAAAA,cAAc,uBACd,kDACF;EAGF,IAAI,CAAC,mBAAmB,QACtB,MAAM,IAAID,UAAAA,iBACRC,UAAAA,cAAc,uBACd,mEACF;EAIF,MAAM,eAAe,MAAMC,qCAAAA,qBAAqB,QAAQ,kBAAkB;EAM1E,IAAI,CAJiB,aAAa,iBAAiB,OAChD,WAAW,WAAW,IAGT,GACd,MAAM,IAAIF,UAAAA,iBACRC,UAAAA,cAAc,mBACd,kEACF;EAIF,MAAM,eAAe,aAAa,iBAAiB,MAChD,WAAW,QAAQ,GACtB;EACA,IAAI,cAAc,KAAK;GACrB,MAAM,QAAQ,IAAIE,+BAAAA,8BAA8B,aAAa,GAAG;GAChE,MAAM,IAAIH,UAAAA,iBACRC,UAAAA,cAAc,mBACd,uBAAuB,MAAM,WAC7B,KACF;EACF;EAEA,MAAM,uBAAuB;GAC3B,aAAa,aAAa,aAAa;GACvC,UAAU,aAAa;EACzB;EAGA,cAAc,aAAa,MAAM,OAAO,MAAM,WAAW;GACvD,QAAQ,qBAAqB;GAC7B,QAAQ,GAAG,UAAU,SAAS,kBAAkB,GAAG,KAAK,qBAAqB;EAC/E,CAAC;EAED,IAAI,mBACF,cAAc,aAAa,MAAM,OAAO,MAAM,MAAM;EAGtD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_rpc_utils = require("../../rpc/utils.js");
3
+ const require_utils_solanaErrorCause = require("../../utils/solanaErrorCause.js");
3
4
  const require_actions_sendAndConfirmTransaction = require("../../actions/sendAndConfirmTransaction.js");
4
5
  let _lifi_sdk = require("@lifi/sdk");
5
6
  let _solana_kit = require("@solana/kit");
@@ -20,15 +21,15 @@ var SolanaStandardWaitForTransactionTask = class extends _lifi_sdk.BaseStepExecu
20
21
  encoding: "base64"
21
22
  }).send());
22
23
  if (simulationResult.value.err) {
23
- const errorMessage = typeof simulationResult.value.err === "object" ? JSON.stringify(simulationResult.value.err, (_, v) => typeof v === "bigint" ? v.toString() : v) : simulationResult.value.err;
24
- throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionSimulationFailed, `Transaction simulation failed: ${errorMessage}`, new Error(errorMessage));
24
+ const cause = new require_utils_solanaErrorCause.SolanaTransactionDetailsError(simulationResult.value.err, simulationResult.value.logs);
25
+ throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionSimulationFailed, `Transaction simulation failed: ${cause.message}`, cause);
25
26
  }
26
27
  }
27
28
  const result = await require_actions_sendAndConfirmTransaction.sendAndConfirmTransaction(client, signedTransaction);
28
29
  if (!result.signatureResult) throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionExpired, "Transaction has expired: The block height has exceeded the maximum allowed limit.");
29
30
  if (result.signatureResult.err) {
30
- const reason = typeof result.signatureResult.err === "object" ? JSON.stringify(result.signatureResult.err, (_, v) => typeof v === "bigint" ? v.toString() : v) : result.signatureResult.err;
31
- throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionFailed, `Transaction failed: ${reason}`);
31
+ const cause = new require_utils_solanaErrorCause.SolanaTransactionDetailsError(result.signatureResult.err);
32
+ throw new _lifi_sdk.TransactionError(_lifi_sdk.LiFiErrorCode.TransactionFailed, `Transaction failed: ${cause.message}`, cause);
32
33
  }
33
34
  const confirmedTransaction = { txSignature: result.txSignature };
34
35
  statusManager.updateAction(step, action.type, "PENDING", {
@@ -1 +1 @@
1
- {"version":3,"file":"SolanaStandardWaitForTransactionTask.js","names":["BaseStepExecutionTask","TransactionError","LiFiErrorCode","callSolanaRpcsWithRetry","sendAndConfirmTransaction"],"sources":["../../../../src/core/tasks/SolanaStandardWaitForTransactionTask.ts"],"sourcesContent":["import {\n BaseStepExecutionTask,\n LiFiErrorCode,\n type TaskResult,\n TransactionError,\n} from '@lifi/sdk'\nimport { getBase64EncodedWireTransaction } from '@solana/kit'\nimport { sendAndConfirmTransaction } from '../../actions/sendAndConfirmTransaction.js'\nimport { callSolanaRpcsWithRetry } from '../../rpc/utils.js'\nimport type { SolanaStepExecutorContext } from '../../types.js'\n\nexport class SolanaStandardWaitForTransactionTask extends BaseStepExecutionTask {\n async run(context: SolanaStepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n fromChain,\n isBridgeExecution,\n signedTransactions: contextSignedTransactions,\n } = context\n\n const signedTransactions = contextSignedTransactions ?? []\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (!signedTransactions.length) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Signed transactions are not found.'\n )\n }\n\n // Use regular transaction submission\n const signedTransaction = signedTransactions[0]\n\n const encodedTransaction =\n getBase64EncodedWireTransaction(signedTransaction)\n\n if (!context.skipSimulation) {\n const simulationResult = await callSolanaRpcsWithRetry(\n client,\n (connection) =>\n connection\n .simulateTransaction(encodedTransaction, {\n commitment: 'confirmed',\n replaceRecentBlockhash: true,\n encoding: 'base64',\n })\n .send()\n )\n\n if (simulationResult.value.err) {\n const errorMessage =\n typeof simulationResult.value.err === 'object'\n ? JSON.stringify(simulationResult.value.err, (_, v) =>\n typeof v === 'bigint' ? v.toString() : v\n )\n : simulationResult.value.err\n throw new TransactionError(\n LiFiErrorCode.TransactionSimulationFailed,\n `Transaction simulation failed: ${errorMessage}`,\n new Error(errorMessage)\n )\n }\n }\n\n const result = await sendAndConfirmTransaction(client, signedTransaction)\n\n if (!result.signatureResult) {\n throw new TransactionError(\n LiFiErrorCode.TransactionExpired,\n 'Transaction has expired: The block height has exceeded the maximum allowed limit.'\n )\n }\n\n if (result.signatureResult.err) {\n const reason =\n typeof result.signatureResult.err === 'object'\n ? JSON.stringify(result.signatureResult.err, (_, v) =>\n typeof v === 'bigint' ? v.toString() : v\n )\n : result.signatureResult.err\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n `Transaction failed: ${reason}`\n )\n }\n\n const confirmedTransaction = {\n txSignature: result.txSignature,\n }\n\n // Transaction has been confirmed and we can update the action\n statusManager.updateAction(step, action.type, 'PENDING', {\n txHash: confirmedTransaction.txSignature,\n txLink: `${fromChain.metamask.blockExplorerUrls[0]}tx/${confirmedTransaction.txSignature}`,\n })\n\n if (isBridgeExecution) {\n statusManager.updateAction(step, action.type, 'DONE')\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;;AAWA,IAAa,uCAAb,cAA0DA,UAAAA,sBAAsB;CAC9E,MAAM,IAAI,SAAyD;EACjE,MAAM,EACJ,QACA,MACA,eACA,WACA,mBACA,oBAAoB,8BAClB;EAEJ,MAAM,qBAAqB,6BAA6B,CAAC;EAEzD,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EACA,IAAI,CAAC,QACH,MAAM,IAAIC,UAAAA,iBACRC,UAAAA,cAAc,uBACd,kDACF;EAGF,IAAI,CAAC,mBAAmB,QACtB,MAAM,IAAID,UAAAA,iBACRC,UAAAA,cAAc,uBACd,mEACF;EAIF,MAAM,oBAAoB,mBAAmB;EAE7C,MAAM,sBAAA,GAAA,YAAA,iCAC4B,iBAAiB;EAEnD,IAAI,CAAC,QAAQ,gBAAgB;GAC3B,MAAM,mBAAmB,MAAMC,kBAAAA,wBAC7B,SACC,eACC,WACG,oBAAoB,oBAAoB;IACvC,YAAY;IACZ,wBAAwB;IACxB,UAAU;GACZ,CAAC,EACA,KAAK,CACZ;GAEA,IAAI,iBAAiB,MAAM,KAAK;IAC9B,MAAM,eACJ,OAAO,iBAAiB,MAAM,QAAQ,WAClC,KAAK,UAAU,iBAAiB,MAAM,MAAM,GAAG,MAC7C,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CACzC,IACA,iBAAiB,MAAM;IAC7B,MAAM,IAAIF,UAAAA,iBACRC,UAAAA,cAAc,6BACd,kCAAkC,gBAClC,IAAI,MAAM,YAAY,CACxB;GACF;EACF;EAEA,MAAM,SAAS,MAAME,0CAAAA,0BAA0B,QAAQ,iBAAiB;EAExE,IAAI,CAAC,OAAO,iBACV,MAAM,IAAIH,UAAAA,iBACRC,UAAAA,cAAc,oBACd,mFACF;EAGF,IAAI,OAAO,gBAAgB,KAAK;GAC9B,MAAM,SACJ,OAAO,OAAO,gBAAgB,QAAQ,WAClC,KAAK,UAAU,OAAO,gBAAgB,MAAM,GAAG,MAC7C,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CACzC,IACA,OAAO,gBAAgB;GAC7B,MAAM,IAAID,UAAAA,iBACRC,UAAAA,cAAc,mBACd,uBAAuB,QACzB;EACF;EAEA,MAAM,uBAAuB,EAC3B,aAAa,OAAO,YACtB;EAGA,cAAc,aAAa,MAAM,OAAO,MAAM,WAAW;GACvD,QAAQ,qBAAqB;GAC7B,QAAQ,GAAG,UAAU,SAAS,kBAAkB,GAAG,KAAK,qBAAqB;EAC/E,CAAC;EAED,IAAI,mBACF,cAAc,aAAa,MAAM,OAAO,MAAM,MAAM;EAGtD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
1
+ {"version":3,"file":"SolanaStandardWaitForTransactionTask.js","names":["BaseStepExecutionTask","TransactionError","LiFiErrorCode","callSolanaRpcsWithRetry","SolanaTransactionDetailsError","sendAndConfirmTransaction"],"sources":["../../../../src/core/tasks/SolanaStandardWaitForTransactionTask.ts"],"sourcesContent":["import {\n BaseStepExecutionTask,\n LiFiErrorCode,\n type TaskResult,\n TransactionError,\n} from '@lifi/sdk'\nimport { getBase64EncodedWireTransaction } from '@solana/kit'\nimport { sendAndConfirmTransaction } from '../../actions/sendAndConfirmTransaction.js'\nimport { callSolanaRpcsWithRetry } from '../../rpc/utils.js'\nimport type { SolanaStepExecutorContext } from '../../types.js'\nimport { SolanaTransactionDetailsError } from '../../utils/solanaErrorCause.js'\n\nexport class SolanaStandardWaitForTransactionTask extends BaseStepExecutionTask {\n async run(context: SolanaStepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n fromChain,\n isBridgeExecution,\n signedTransactions: contextSignedTransactions,\n } = context\n\n const signedTransactions = contextSignedTransactions ?? []\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (!signedTransactions.length) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Signed transactions are not found.'\n )\n }\n\n // Use regular transaction submission\n const signedTransaction = signedTransactions[0]\n\n const encodedTransaction =\n getBase64EncodedWireTransaction(signedTransaction)\n\n if (!context.skipSimulation) {\n const simulationResult = await callSolanaRpcsWithRetry(\n client,\n (connection) =>\n connection\n .simulateTransaction(encodedTransaction, {\n commitment: 'confirmed',\n replaceRecentBlockhash: true,\n encoding: 'base64',\n })\n .send()\n )\n\n if (simulationResult.value.err) {\n const cause = new SolanaTransactionDetailsError(\n simulationResult.value.err,\n simulationResult.value.logs\n )\n throw new TransactionError(\n LiFiErrorCode.TransactionSimulationFailed,\n `Transaction simulation failed: ${cause.message}`,\n cause\n )\n }\n }\n\n const result = await sendAndConfirmTransaction(client, signedTransaction)\n\n if (!result.signatureResult) {\n throw new TransactionError(\n LiFiErrorCode.TransactionExpired,\n 'Transaction has expired: The block height has exceeded the maximum allowed limit.'\n )\n }\n\n if (result.signatureResult.err) {\n const cause = new SolanaTransactionDetailsError(\n result.signatureResult.err\n )\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n `Transaction failed: ${cause.message}`,\n cause\n )\n }\n\n const confirmedTransaction = {\n txSignature: result.txSignature,\n }\n\n // Transaction has been confirmed and we can update the action\n statusManager.updateAction(step, action.type, 'PENDING', {\n txHash: confirmedTransaction.txSignature,\n txLink: `${fromChain.metamask.blockExplorerUrls[0]}tx/${confirmedTransaction.txSignature}`,\n })\n\n if (isBridgeExecution) {\n statusManager.updateAction(step, action.type, 'DONE')\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;;;AAYA,IAAa,uCAAb,cAA0DA,UAAAA,sBAAsB;CAC9E,MAAM,IAAI,SAAyD;EACjE,MAAM,EACJ,QACA,MACA,eACA,WACA,mBACA,oBAAoB,8BAClB;EAEJ,MAAM,qBAAqB,6BAA6B,CAAC;EAEzD,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EACA,IAAI,CAAC,QACH,MAAM,IAAIC,UAAAA,iBACRC,UAAAA,cAAc,uBACd,kDACF;EAGF,IAAI,CAAC,mBAAmB,QACtB,MAAM,IAAID,UAAAA,iBACRC,UAAAA,cAAc,uBACd,mEACF;EAIF,MAAM,oBAAoB,mBAAmB;EAE7C,MAAM,sBAAA,GAAA,YAAA,iCAC4B,iBAAiB;EAEnD,IAAI,CAAC,QAAQ,gBAAgB;GAC3B,MAAM,mBAAmB,MAAMC,kBAAAA,wBAC7B,SACC,eACC,WACG,oBAAoB,oBAAoB;IACvC,YAAY;IACZ,wBAAwB;IACxB,UAAU;GACZ,CAAC,EACA,KAAK,CACZ;GAEA,IAAI,iBAAiB,MAAM,KAAK;IAC9B,MAAM,QAAQ,IAAIC,+BAAAA,8BAChB,iBAAiB,MAAM,KACvB,iBAAiB,MAAM,IACzB;IACA,MAAM,IAAIH,UAAAA,iBACRC,UAAAA,cAAc,6BACd,kCAAkC,MAAM,WACxC,KACF;GACF;EACF;EAEA,MAAM,SAAS,MAAMG,0CAAAA,0BAA0B,QAAQ,iBAAiB;EAExE,IAAI,CAAC,OAAO,iBACV,MAAM,IAAIJ,UAAAA,iBACRC,UAAAA,cAAc,oBACd,mFACF;EAGF,IAAI,OAAO,gBAAgB,KAAK;GAC9B,MAAM,QAAQ,IAAIE,+BAAAA,8BAChB,OAAO,gBAAgB,GACzB;GACA,MAAM,IAAIH,UAAAA,iBACRC,UAAAA,cAAc,mBACd,uBAAuB,MAAM,WAC7B,KACF;EACF;EAEA,MAAM,uBAAuB,EAC3B,aAAa,OAAO,YACtB;EAGA,cAAc,aAAa,MAAM,OAAO,MAAM,WAAW;GACvD,QAAQ,qBAAqB;GAC7B,QAAQ,GAAG,UAAU,SAAS,kBAAkB,GAAG,KAAK,qBAAqB;EAC/E,CAAC;EAED,IAAI,mBACF,cAAc,aAAa,MAAM,OAAO,MAAM,MAAM;EAGtD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
@@ -1,5 +1,6 @@
1
1
  import { SolanaProviderOptions, SolanaSDKProvider, isSolanaProvider } from "./types.js";
2
2
  import { SolanaProvider } from "./SolanaProvider.js";
3
3
  import { KeypairWalletAdapter } from "./utils/KeypairWalletAdapter.js";
4
+ import { SolanaTransactionDetailsError } from "./utils/solanaErrorCause.js";
4
5
  import { address as toAddress } from "@solana/kit";
5
- export { KeypairWalletAdapter, SolanaProvider, type SolanaProviderOptions, type SolanaSDKProvider, isSolanaProvider, toAddress };
6
+ export { KeypairWalletAdapter, SolanaProvider, type SolanaProviderOptions, type SolanaSDKProvider, SolanaTransactionDetailsError, isSolanaProvider, toAddress };
package/dist/cjs/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_utils_solanaErrorCause = require("./utils/solanaErrorCause.js");
2
3
  const require_SolanaProvider = require("./SolanaProvider.js");
3
4
  const require_types = require("./types.js");
4
5
  const require_utils_KeypairWalletAdapter = require("./utils/KeypairWalletAdapter.js");
5
6
  let _solana_kit = require("@solana/kit");
6
7
  exports.KeypairWalletAdapter = require_utils_KeypairWalletAdapter.KeypairWalletAdapter;
7
8
  exports.SolanaProvider = require_SolanaProvider.SolanaProvider;
9
+ exports.SolanaTransactionDetailsError = require_utils_solanaErrorCause.SolanaTransactionDetailsError;
8
10
  exports.isSolanaProvider = require_types.isSolanaProvider;
9
11
  Object.defineProperty(exports, "toAddress", {
10
12
  enumerable: true,
@@ -23,6 +23,7 @@ const isJitoRpc = async (rpcUrl) => {
23
23
  const ensureSolanaRpcs = async (client) => {
24
24
  const rpcUrls = await client.getRpcUrlsByChainId(_lifi_sdk.ChainId.SOL);
25
25
  for (const rpcUrl of rpcUrls) if (!solanaRpcs.has(rpcUrl)) solanaRpcs.set(rpcUrl, (0, _solana_kit.createSolanaRpc)(rpcUrl));
26
+ return rpcUrls;
26
27
  };
27
28
  /**
28
29
  * Detects and caches Jito-capable RPCs by checking if they support the getTipAccounts method.
@@ -31,22 +32,21 @@ const ensureSolanaRpcs = async (client) => {
31
32
  const ensureJitoRpcs = async (client) => {
32
33
  const rpcUrls = await client.getRpcUrlsByChainId(_lifi_sdk.ChainId.SOL);
33
34
  for (const rpcUrl of rpcUrls) if (!jitoRpcs.has(rpcUrl) && await isJitoRpc(rpcUrl)) jitoRpcs.set(rpcUrl, require_rpc_jito_createJitoRpc.createJitoRpc(rpcUrl));
35
+ return rpcUrls;
34
36
  };
35
37
  /**
36
38
  * Wrapper around getting the Solana RPCs
37
39
  * @returns - Solana RPCs
38
40
  */
39
41
  const getSolanaRpcs = async (client) => {
40
- await ensureSolanaRpcs(client);
41
- return Array.from(solanaRpcs.values());
42
+ return (await ensureSolanaRpcs(client)).map((rpcUrl) => solanaRpcs.get(rpcUrl)).filter((rpc) => Boolean(rpc));
42
43
  };
43
44
  /**
44
45
  * Wrapper around getting the Jito RPCs
45
46
  * @returns - Jito RPCs
46
47
  */
47
48
  const getJitoRpcs = async (client) => {
48
- await ensureJitoRpcs(client);
49
- return Array.from(jitoRpcs.values());
49
+ return (await ensureJitoRpcs(client)).map((rpcUrl) => jitoRpcs.get(rpcUrl)).filter((rpc) => Boolean(rpc));
50
50
  };
51
51
  //#endregion
52
52
  exports.getJitoRpcs = getJitoRpcs;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","names":["LruMap","createJitoRpc","ChainId"],"sources":["../../../src/rpc/registry.ts"],"sourcesContent":["import { ChainId, LruMap, type SDKClient } from '@lifi/sdk'\nimport { createSolanaRpc } from '@solana/kit'\nimport { createJitoRpc } from './jito/createJitoRpc.js'\nimport type { JitoRpcType, SolanaRpcType } from './types.js'\n\nconst solanaRpcs = new LruMap<SolanaRpcType>(12)\nconst jitoRpcs = new LruMap<JitoRpcType>(12)\n\n/**\n * Checks if an RPC URL supports Jito methods by calling getTipAccounts.\n */\nexport const isJitoRpc = async (rpcUrl: string): Promise<boolean> => {\n try {\n const rpc = createJitoRpc(rpcUrl)\n await rpc.getTipAccounts().send()\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Initializes Solana RPCs for all available RPC URLs if they haven't been cached yet.\n * @param client - The SDK client used to fetch RPC URLs.\n */\nconst ensureSolanaRpcs = async (client: SDKClient): Promise<void> => {\n const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)\n for (const rpcUrl of rpcUrls) {\n if (!solanaRpcs.has(rpcUrl)) {\n solanaRpcs.set(rpcUrl, createSolanaRpc(rpcUrl))\n }\n }\n}\n\n/**\n * Detects and caches Jito-capable RPCs by checking if they support the getTipAccounts method.\n * @param client - The SDK client used to fetch RPC URLs.\n */\nconst ensureJitoRpcs = async (client: SDKClient): Promise<void> => {\n const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)\n for (const rpcUrl of rpcUrls) {\n if (!jitoRpcs.has(rpcUrl) && (await isJitoRpc(rpcUrl))) {\n jitoRpcs.set(rpcUrl, createJitoRpc(rpcUrl))\n }\n }\n}\n\n/**\n * Wrapper around getting the Solana RPCs\n * @returns - Solana RPCs\n */\nexport const getSolanaRpcs = async (\n client: SDKClient\n): Promise<SolanaRpcType[]> => {\n await ensureSolanaRpcs(client)\n return Array.from(solanaRpcs.values())\n}\n\n/**\n * Wrapper around getting the Jito RPCs\n * @returns - Jito RPCs\n */\nexport const getJitoRpcs = async (\n client: SDKClient\n): Promise<JitoRpcType[]> => {\n await ensureJitoRpcs(client)\n return Array.from(jitoRpcs.values())\n}\n"],"mappings":";;;;;AAKA,MAAM,aAAa,IAAIA,UAAAA,OAAsB,EAAE;AAC/C,MAAM,WAAW,IAAIA,UAAAA,OAAoB,EAAE;;;;AAK3C,MAAa,YAAY,OAAO,WAAqC;CACnE,IAAI;EAEF,MADYC,+BAAAA,cAAc,MAClB,EAAE,eAAe,EAAE,KAAK;EAChC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,MAAM,mBAAmB,OAAO,WAAqC;CACnE,MAAM,UAAU,MAAM,OAAO,oBAAoBC,UAAAA,QAAQ,GAAG;CAC5D,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,WAAW,IAAI,MAAM,GACxB,WAAW,IAAI,SAAA,GAAA,YAAA,iBAAwB,MAAM,CAAC;AAGpD;;;;;AAMA,MAAM,iBAAiB,OAAO,WAAqC;CACjE,MAAM,UAAU,MAAM,OAAO,oBAAoBA,UAAAA,QAAQ,GAAG;CAC5D,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,SAAS,IAAI,MAAM,KAAM,MAAM,UAAU,MAAM,GAClD,SAAS,IAAI,QAAQD,+BAAAA,cAAc,MAAM,CAAC;AAGhD;;;;;AAMA,MAAa,gBAAgB,OAC3B,WAC6B;CAC7B,MAAM,iBAAiB,MAAM;CAC7B,OAAO,MAAM,KAAK,WAAW,OAAO,CAAC;AACvC;;;;;AAMA,MAAa,cAAc,OACzB,WAC2B;CAC3B,MAAM,eAAe,MAAM;CAC3B,OAAO,MAAM,KAAK,SAAS,OAAO,CAAC;AACrC"}
1
+ {"version":3,"file":"registry.js","names":["LruMap","createJitoRpc","ChainId"],"sources":["../../../src/rpc/registry.ts"],"sourcesContent":["import { ChainId, LruMap, type SDKClient } from '@lifi/sdk'\nimport { createSolanaRpc } from '@solana/kit'\nimport { createJitoRpc } from './jito/createJitoRpc.js'\nimport type { JitoRpcType, SolanaRpcType } from './types.js'\n\nconst solanaRpcs = new LruMap<SolanaRpcType>(12)\nconst jitoRpcs = new LruMap<JitoRpcType>(12)\n\n/**\n * Checks if an RPC URL supports Jito methods by calling getTipAccounts.\n */\nexport const isJitoRpc = async (rpcUrl: string): Promise<boolean> => {\n try {\n const rpc = createJitoRpc(rpcUrl)\n await rpc.getTipAccounts().send()\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Initializes Solana RPCs for all available RPC URLs if they haven't been cached yet.\n * @param client - The SDK client used to fetch RPC URLs.\n */\nconst ensureSolanaRpcs = async (client: SDKClient): Promise<string[]> => {\n const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)\n for (const rpcUrl of rpcUrls) {\n if (!solanaRpcs.has(rpcUrl)) {\n solanaRpcs.set(rpcUrl, createSolanaRpc(rpcUrl))\n }\n }\n return rpcUrls\n}\n\n/**\n * Detects and caches Jito-capable RPCs by checking if they support the getTipAccounts method.\n * @param client - The SDK client used to fetch RPC URLs.\n */\nconst ensureJitoRpcs = async (client: SDKClient): Promise<string[]> => {\n const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)\n for (const rpcUrl of rpcUrls) {\n if (!jitoRpcs.has(rpcUrl) && (await isJitoRpc(rpcUrl))) {\n jitoRpcs.set(rpcUrl, createJitoRpc(rpcUrl))\n }\n }\n return rpcUrls\n}\n\n/**\n * Wrapper around getting the Solana RPCs\n * @returns - Solana RPCs\n */\nexport const getSolanaRpcs = async (\n client: SDKClient\n): Promise<SolanaRpcType[]> => {\n const rpcUrls = await ensureSolanaRpcs(client)\n return rpcUrls\n .map((rpcUrl) => solanaRpcs.get(rpcUrl))\n .filter((rpc): rpc is SolanaRpcType => Boolean(rpc))\n}\n\n/**\n * Wrapper around getting the Jito RPCs\n * @returns - Jito RPCs\n */\nexport const getJitoRpcs = async (\n client: SDKClient\n): Promise<JitoRpcType[]> => {\n const rpcUrls = await ensureJitoRpcs(client)\n return rpcUrls\n .map((rpcUrl) => jitoRpcs.get(rpcUrl))\n .filter((rpc): rpc is JitoRpcType => Boolean(rpc))\n}\n"],"mappings":";;;;;AAKA,MAAM,aAAa,IAAIA,UAAAA,OAAsB,EAAE;AAC/C,MAAM,WAAW,IAAIA,UAAAA,OAAoB,EAAE;;;;AAK3C,MAAa,YAAY,OAAO,WAAqC;CACnE,IAAI;EAEF,MADYC,+BAAAA,cAAc,MAClB,EAAE,eAAe,EAAE,KAAK;EAChC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,MAAM,mBAAmB,OAAO,WAAyC;CACvE,MAAM,UAAU,MAAM,OAAO,oBAAoBC,UAAAA,QAAQ,GAAG;CAC5D,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,WAAW,IAAI,MAAM,GACxB,WAAW,IAAI,SAAA,GAAA,YAAA,iBAAwB,MAAM,CAAC;CAGlD,OAAO;AACT;;;;;AAMA,MAAM,iBAAiB,OAAO,WAAyC;CACrE,MAAM,UAAU,MAAM,OAAO,oBAAoBA,UAAAA,QAAQ,GAAG;CAC5D,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,SAAS,IAAI,MAAM,KAAM,MAAM,UAAU,MAAM,GAClD,SAAS,IAAI,QAAQD,+BAAAA,cAAc,MAAM,CAAC;CAG9C,OAAO;AACT;;;;;AAMA,MAAa,gBAAgB,OAC3B,WAC6B;CAE7B,QAAO,MADe,iBAAiB,MAAM,GAE1C,KAAK,WAAW,WAAW,IAAI,MAAM,CAAC,EACtC,QAAQ,QAA8B,QAAQ,GAAG,CAAC;AACvD;;;;;AAMA,MAAa,cAAc,OACzB,WAC2B;CAE3B,QAAO,MADe,eAAe,MAAM,GAExC,KAAK,WAAW,SAAS,IAAI,MAAM,CAAC,EACpC,QAAQ,QAA4B,QAAQ,GAAG,CAAC;AACrD"}
@@ -0,0 +1,15 @@
1
+ //#region src/utils/solanaErrorCause.d.ts
2
+ declare const safeStringifyBigInt: (value: unknown) => string;
3
+ /**
4
+ * Carries the structured payload of a failed Solana RPC simulation or
5
+ * confirmation so consumers can inspect `err` and `logs` directly from
6
+ * the thrown `TransactionError`'s `cause`, without re-simulating.
7
+ */
8
+ declare class SolanaTransactionDetailsError extends Error {
9
+ readonly err: unknown;
10
+ readonly logs: readonly string[] | null;
11
+ constructor(err: unknown, logs?: readonly string[] | null);
12
+ }
13
+ //#endregion
14
+ export { SolanaTransactionDetailsError, safeStringifyBigInt };
15
+ //# sourceMappingURL=solanaErrorCause.d.ts.map
@@ -0,0 +1,24 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/utils/solanaErrorCause.ts
3
+ const safeStringifyBigInt = (value) => JSON.stringify(value, (_, v) => typeof v === "bigint" ? v.toString() : v);
4
+ const formatSolanaErr = (err) => typeof err === "object" && err !== null ? safeStringifyBigInt(err) : String(err);
5
+ /**
6
+ * Carries the structured payload of a failed Solana RPC simulation or
7
+ * confirmation so consumers can inspect `err` and `logs` directly from
8
+ * the thrown `TransactionError`'s `cause`, without re-simulating.
9
+ */
10
+ var SolanaTransactionDetailsError = class extends Error {
11
+ err;
12
+ logs;
13
+ constructor(err, logs = null) {
14
+ super(formatSolanaErr(err));
15
+ this.name = "SolanaTransactionDetailsError";
16
+ this.err = err;
17
+ this.logs = logs;
18
+ }
19
+ };
20
+ //#endregion
21
+ exports.SolanaTransactionDetailsError = SolanaTransactionDetailsError;
22
+ exports.safeStringifyBigInt = safeStringifyBigInt;
23
+
24
+ //# sourceMappingURL=solanaErrorCause.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solanaErrorCause.js","names":[],"sources":["../../../src/utils/solanaErrorCause.ts"],"sourcesContent":["export const safeStringifyBigInt = (value: unknown): string =>\n JSON.stringify(value, (_, v) => (typeof v === 'bigint' ? v.toString() : v))\n\nconst formatSolanaErr = (err: unknown): string =>\n typeof err === 'object' && err !== null\n ? safeStringifyBigInt(err)\n : String(err)\n\n/**\n * Carries the structured payload of a failed Solana RPC simulation or\n * confirmation so consumers can inspect `err` and `logs` directly from\n * the thrown `TransactionError`'s `cause`, without re-simulating.\n */\nexport class SolanaTransactionDetailsError extends Error {\n readonly err: unknown\n readonly logs: readonly string[] | null\n\n constructor(err: unknown, logs: readonly string[] | null = null) {\n super(formatSolanaErr(err))\n this.name = 'SolanaTransactionDetailsError'\n this.err = err\n this.logs = logs\n }\n}\n"],"mappings":";;AAAA,MAAa,uBAAuB,UAClC,KAAK,UAAU,QAAQ,GAAG,MAAO,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CAAE;AAE5E,MAAM,mBAAmB,QACvB,OAAO,QAAQ,YAAY,QAAQ,OAC/B,oBAAoB,GAAG,IACvB,OAAO,GAAG;;;;;;AAOhB,IAAa,gCAAb,cAAmD,MAAM;CACvD;CACA;CAEA,YAAY,KAAc,OAAiC,MAAM;EAC/D,MAAM,gBAAgB,GAAG,CAAC;EAC1B,KAAK,OAAO;EACZ,KAAK,MAAM;EACX,KAAK,OAAO;CACd;AACF"}
@@ -1,6 +1,6 @@
1
1
  //#region src/version.d.ts
2
2
  declare const name = "@lifi/sdk-provider-solana";
3
- declare const version = "4.0.0-beta.11";
3
+ declare const version = "4.0.0-beta.12";
4
4
  //#endregion
5
5
  export { name, version };
6
6
  //# sourceMappingURL=version.d.ts.map
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  //#region src/version.ts
3
3
  const name = "@lifi/sdk-provider-solana";
4
- const version = "4.0.0-beta.11";
4
+ const version = "4.0.0-beta.12";
5
5
  //#endregion
6
6
  exports.name = name;
7
7
  exports.version = version;
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","names":[],"sources":["../../src/version.ts"],"sourcesContent":["export const name = '@lifi/sdk-provider-solana'\nexport const version = '4.0.0-beta.11'\n"],"mappings":";;AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
1
+ {"version":3,"file":"version.js","names":[],"sources":["../../src/version.ts"],"sourcesContent":["export const name = '@lifi/sdk-provider-solana'\nexport const version = '4.0.0-beta.12'\n"],"mappings":";;AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
@@ -35,7 +35,7 @@ const getSolanaBalanceDefault = async (client, _chainId, tokens, walletAddress)
35
35
  const walletTokenAmounts = [...tokenProgramOk ? tokenAccountsByOwner.value.value : [], ...token2022ProgramOk ? token2022AccountsByOwner.value.value : []].reduce((tokenAmounts, value) => {
36
36
  const tokenAccount = value.account.data.parsed.info;
37
37
  const amount = BigInt(tokenAccount.tokenAmount.amount);
38
- if (amount > 0n) tokenAmounts[tokenAccount.mint] = amount;
38
+ if (amount > 0n) tokenAmounts[tokenAccount.mint] = (tokenAmounts[tokenAccount.mint] ?? 0n) + amount;
39
39
  return tokenAmounts;
40
40
  }, {});
41
41
  const splZeroIsKnown = tokenProgramOk && token2022ProgramOk;
@@ -1 +1 @@
1
- {"version":3,"file":"getSolanaBalance.js","names":[],"sources":["../../../src/actions/getSolanaBalance.ts"],"sourcesContent":["import type { SDKClient } from '@lifi/sdk'\nimport {\n type ChainId,\n type Token,\n type TokenAmount,\n withDedupe,\n} from '@lifi/sdk'\nimport { address, type JsonParsedTokenAccount } from '@solana/kit'\n\nimport { callSolanaRpcsWithRetry } from '../rpc/utils.js'\n\nconst SolSystemProgram = '11111111111111111111111111111111'\nconst TokenProgramId = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'\nconst Token2022ProgramId = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'\n\nexport const getSolanaBalance = async (\n client: SDKClient,\n walletAddress: string,\n tokens: Token[]\n): Promise<TokenAmount[]> => {\n if (tokens.length === 0) {\n return []\n }\n const { chainId } = tokens[0]\n for (const token of tokens) {\n if (token.chainId !== chainId) {\n console.warn('Requested tokens have to be on the same chain.')\n }\n }\n\n return getSolanaBalanceDefault(client, chainId, tokens, walletAddress)\n}\n\nconst getSolanaBalanceDefault = async (\n client: SDKClient,\n _chainId: ChainId,\n tokens: Token[],\n walletAddress: string\n): Promise<TokenAmount[]> => {\n // Convert addresses to Solana Kit's address type\n const accountAddress = address(walletAddress)\n const tokenProgramAddress = address(TokenProgramId)\n const token2022ProgramAddress = address(Token2022ProgramId)\n\n // Use Solana Kit's RPC API with the retry wrapper\n const [slot, balance, tokenAccountsByOwner, token2022AccountsByOwner] =\n await Promise.allSettled([\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc.getSlot({ commitment: 'confirmed' }).send()\n ),\n { id: `${getSolanaBalanceDefault.name}.getSlot` }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc.getBalance(accountAddress, { commitment: 'confirmed' }).send()\n ),\n { id: `${getSolanaBalanceDefault.name}.getBalance` }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc\n .getTokenAccountsByOwner(\n accountAddress,\n {\n programId: tokenProgramAddress,\n },\n {\n commitment: 'confirmed',\n encoding: 'jsonParsed',\n }\n )\n .send()\n ),\n {\n id: `${getSolanaBalanceDefault.name}.getTokenAccountsByOwner.${TokenProgramId}`,\n }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc\n .getTokenAccountsByOwner(\n accountAddress,\n {\n programId: token2022ProgramAddress,\n },\n {\n commitment: 'confirmed',\n encoding: 'jsonParsed',\n }\n )\n .send()\n ),\n {\n id: `${getSolanaBalanceDefault.name}.getTokenAccountsByOwner.${Token2022ProgramId}`,\n }\n ),\n ])\n const blockNumber = slot.status === 'fulfilled' ? BigInt(slot.value) : 0n\n const nativeBalanceOk = balance.status === 'fulfilled'\n const solBalance = nativeBalanceOk ? BigInt(balance.value.value) : 0n\n const tokenProgramOk = tokenAccountsByOwner.status === 'fulfilled'\n const token2022ProgramOk = token2022AccountsByOwner.status === 'fulfilled'\n\n const walletTokenAmounts = [\n ...(tokenProgramOk ? tokenAccountsByOwner.value.value : []),\n ...(token2022ProgramOk ? token2022AccountsByOwner.value.value : []),\n ].reduce(\n (tokenAmounts: Record<string, bigint>, value) => {\n const tokenAccount: JsonParsedTokenAccount =\n value.account.data.parsed.info\n const amount = BigInt(tokenAccount.tokenAmount.amount)\n if (amount > 0n) {\n tokenAmounts[tokenAccount.mint] = amount\n }\n return tokenAmounts\n },\n {} as Record<string, bigint>\n )\n\n // We can only confidently report 0n for an SPL mint when both Token and\n // Token2022 program queries succeeded — otherwise the mint may live in the\n // program whose query failed (e.g. PYUSD on Token2022).\n const splZeroIsKnown = tokenProgramOk && token2022ProgramOk\n\n const tokenAmounts: TokenAmount[] = tokens.map((token) => {\n const isNative = token.address === SolSystemProgram\n if (isNative) {\n if (!nativeBalanceOk) {\n return { ...token, blockNumber }\n }\n return { ...token, amount: solBalance, blockNumber }\n }\n const found = walletTokenAmounts[token.address]\n if (found !== undefined) {\n return { ...token, amount: found, blockNumber }\n }\n if (splZeroIsKnown) {\n return { ...token, amount: 0n, blockNumber }\n }\n return { ...token, blockNumber }\n })\n return tokenAmounts\n}\n"],"mappings":";;;;AAWA,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;AACvB,MAAM,qBAAqB;AAE3B,MAAa,mBAAmB,OAC9B,QACA,eACA,WAC2B;CAC3B,IAAI,OAAO,WAAW,GACpB,OAAO,CAAC;CAEV,MAAM,EAAE,YAAY,OAAO;CAC3B,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,YAAY,SACpB,QAAQ,KAAK,gDAAgD;CAIjE,OAAO,wBAAwB,QAAQ,SAAS,QAAQ,aAAa;AACvE;AAEA,MAAM,0BAA0B,OAC9B,QACA,UACA,QACA,kBAC2B;CAE3B,MAAM,iBAAiB,QAAQ,aAAa;CAC5C,MAAM,sBAAsB,QAAQ,cAAc;CAClD,MAAM,0BAA0B,QAAQ,kBAAkB;CAG1D,MAAM,CAAC,MAAM,SAAS,sBAAsB,4BAC1C,MAAM,QAAQ,WAAW;EACvB,iBAEI,wBAAwB,SAAS,QAC/B,IAAI,QAAQ,EAAE,YAAY,YAAY,CAAC,EAAE,KAAK,CAChD,GACF,EAAE,IAAI,GAAG,wBAAwB,KAAK,UAAU,CAClD;EACA,iBAEI,wBAAwB,SAAS,QAC/B,IAAI,WAAW,gBAAgB,EAAE,YAAY,YAAY,CAAC,EAAE,KAAK,CACnE,GACF,EAAE,IAAI,GAAG,wBAAwB,KAAK,aAAa,CACrD;EACA,iBAEI,wBAAwB,SAAS,QAC/B,IACG,wBACC,gBACA,EACE,WAAW,oBACb,GACA;GACE,YAAY;GACZ,UAAU;EACZ,CACF,EACC,KAAK,CACV,GACF,EACE,IAAI,GAAG,wBAAwB,KAAK,2BAA2B,iBACjE,CACF;EACA,iBAEI,wBAAwB,SAAS,QAC/B,IACG,wBACC,gBACA,EACE,WAAW,wBACb,GACA;GACE,YAAY;GACZ,UAAU;EACZ,CACF,EACC,KAAK,CACV,GACF,EACE,IAAI,GAAG,wBAAwB,KAAK,2BAA2B,qBACjE,CACF;CACF,CAAC;CACH,MAAM,cAAc,KAAK,WAAW,cAAc,OAAO,KAAK,KAAK,IAAI;CACvE,MAAM,kBAAkB,QAAQ,WAAW;CAC3C,MAAM,aAAa,kBAAkB,OAAO,QAAQ,MAAM,KAAK,IAAI;CACnE,MAAM,iBAAiB,qBAAqB,WAAW;CACvD,MAAM,qBAAqB,yBAAyB,WAAW;CAE/D,MAAM,qBAAqB,CACzB,GAAI,iBAAiB,qBAAqB,MAAM,QAAQ,CAAC,GACzD,GAAI,qBAAqB,yBAAyB,MAAM,QAAQ,CAAC,CACnE,EAAE,QACC,cAAsC,UAAU;EAC/C,MAAM,eACJ,MAAM,QAAQ,KAAK,OAAO;EAC5B,MAAM,SAAS,OAAO,aAAa,YAAY,MAAM;EACrD,IAAI,SAAS,IACX,aAAa,aAAa,QAAQ;EAEpC,OAAO;CACT,GACA,CAAC,CACH;CAKA,MAAM,iBAAiB,kBAAkB;CAmBzC,OAjBoC,OAAO,KAAK,UAAU;EAExD,IADiB,MAAM,YAAY,kBACrB;GACZ,IAAI,CAAC,iBACH,OAAO;IAAE,GAAG;IAAO;GAAY;GAEjC,OAAO;IAAE,GAAG;IAAO,QAAQ;IAAY;GAAY;EACrD;EACA,MAAM,QAAQ,mBAAmB,MAAM;EACvC,IAAI,UAAU,KAAA,GACZ,OAAO;GAAE,GAAG;GAAO,QAAQ;GAAO;EAAY;EAEhD,IAAI,gBACF,OAAO;GAAE,GAAG;GAAO,QAAQ;GAAI;EAAY;EAE7C,OAAO;GAAE,GAAG;GAAO;EAAY;CACjC,CACkB;AACpB"}
1
+ {"version":3,"file":"getSolanaBalance.js","names":[],"sources":["../../../src/actions/getSolanaBalance.ts"],"sourcesContent":["import type { SDKClient } from '@lifi/sdk'\nimport {\n type ChainId,\n type Token,\n type TokenAmount,\n withDedupe,\n} from '@lifi/sdk'\nimport { address, type JsonParsedTokenAccount } from '@solana/kit'\n\nimport { callSolanaRpcsWithRetry } from '../rpc/utils.js'\n\nconst SolSystemProgram = '11111111111111111111111111111111'\nconst TokenProgramId = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'\nconst Token2022ProgramId = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'\n\nexport const getSolanaBalance = async (\n client: SDKClient,\n walletAddress: string,\n tokens: Token[]\n): Promise<TokenAmount[]> => {\n if (tokens.length === 0) {\n return []\n }\n const { chainId } = tokens[0]\n for (const token of tokens) {\n if (token.chainId !== chainId) {\n console.warn('Requested tokens have to be on the same chain.')\n }\n }\n\n return getSolanaBalanceDefault(client, chainId, tokens, walletAddress)\n}\n\nconst getSolanaBalanceDefault = async (\n client: SDKClient,\n _chainId: ChainId,\n tokens: Token[],\n walletAddress: string\n): Promise<TokenAmount[]> => {\n // Convert addresses to Solana Kit's address type\n const accountAddress = address(walletAddress)\n const tokenProgramAddress = address(TokenProgramId)\n const token2022ProgramAddress = address(Token2022ProgramId)\n\n // Use Solana Kit's RPC API with the retry wrapper\n const [slot, balance, tokenAccountsByOwner, token2022AccountsByOwner] =\n await Promise.allSettled([\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc.getSlot({ commitment: 'confirmed' }).send()\n ),\n { id: `${getSolanaBalanceDefault.name}.getSlot` }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc.getBalance(accountAddress, { commitment: 'confirmed' }).send()\n ),\n { id: `${getSolanaBalanceDefault.name}.getBalance` }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc\n .getTokenAccountsByOwner(\n accountAddress,\n {\n programId: tokenProgramAddress,\n },\n {\n commitment: 'confirmed',\n encoding: 'jsonParsed',\n }\n )\n .send()\n ),\n {\n id: `${getSolanaBalanceDefault.name}.getTokenAccountsByOwner.${TokenProgramId}`,\n }\n ),\n withDedupe(\n () =>\n callSolanaRpcsWithRetry(client, (rpc) =>\n rpc\n .getTokenAccountsByOwner(\n accountAddress,\n {\n programId: token2022ProgramAddress,\n },\n {\n commitment: 'confirmed',\n encoding: 'jsonParsed',\n }\n )\n .send()\n ),\n {\n id: `${getSolanaBalanceDefault.name}.getTokenAccountsByOwner.${Token2022ProgramId}`,\n }\n ),\n ])\n const blockNumber = slot.status === 'fulfilled' ? BigInt(slot.value) : 0n\n const nativeBalanceOk = balance.status === 'fulfilled'\n const solBalance = nativeBalanceOk ? BigInt(balance.value.value) : 0n\n const tokenProgramOk = tokenAccountsByOwner.status === 'fulfilled'\n const token2022ProgramOk = token2022AccountsByOwner.status === 'fulfilled'\n\n const walletTokenAmounts = [\n ...(tokenProgramOk ? tokenAccountsByOwner.value.value : []),\n ...(token2022ProgramOk ? token2022AccountsByOwner.value.value : []),\n ].reduce(\n (tokenAmounts: Record<string, bigint>, value) => {\n const tokenAccount: JsonParsedTokenAccount =\n value.account.data.parsed.info\n const amount = BigInt(tokenAccount.tokenAmount.amount)\n if (amount > 0n) {\n tokenAmounts[tokenAccount.mint] =\n (tokenAmounts[tokenAccount.mint] ?? 0n) + amount\n }\n return tokenAmounts\n },\n {} as Record<string, bigint>\n )\n\n // We can only confidently report 0n for an SPL mint when both Token and\n // Token2022 program queries succeeded — otherwise the mint may live in the\n // program whose query failed (e.g. PYUSD on Token2022).\n const splZeroIsKnown = tokenProgramOk && token2022ProgramOk\n\n const tokenAmounts: TokenAmount[] = tokens.map((token) => {\n const isNative = token.address === SolSystemProgram\n if (isNative) {\n if (!nativeBalanceOk) {\n return { ...token, blockNumber }\n }\n return { ...token, amount: solBalance, blockNumber }\n }\n const found = walletTokenAmounts[token.address]\n if (found !== undefined) {\n return { ...token, amount: found, blockNumber }\n }\n if (splZeroIsKnown) {\n return { ...token, amount: 0n, blockNumber }\n }\n return { ...token, blockNumber }\n })\n return tokenAmounts\n}\n"],"mappings":";;;;AAWA,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;AACvB,MAAM,qBAAqB;AAE3B,MAAa,mBAAmB,OAC9B,QACA,eACA,WAC2B;CAC3B,IAAI,OAAO,WAAW,GACpB,OAAO,CAAC;CAEV,MAAM,EAAE,YAAY,OAAO;CAC3B,KAAK,MAAM,SAAS,QAClB,IAAI,MAAM,YAAY,SACpB,QAAQ,KAAK,gDAAgD;CAIjE,OAAO,wBAAwB,QAAQ,SAAS,QAAQ,aAAa;AACvE;AAEA,MAAM,0BAA0B,OAC9B,QACA,UACA,QACA,kBAC2B;CAE3B,MAAM,iBAAiB,QAAQ,aAAa;CAC5C,MAAM,sBAAsB,QAAQ,cAAc;CAClD,MAAM,0BAA0B,QAAQ,kBAAkB;CAG1D,MAAM,CAAC,MAAM,SAAS,sBAAsB,4BAC1C,MAAM,QAAQ,WAAW;EACvB,iBAEI,wBAAwB,SAAS,QAC/B,IAAI,QAAQ,EAAE,YAAY,YAAY,CAAC,EAAE,KAAK,CAChD,GACF,EAAE,IAAI,GAAG,wBAAwB,KAAK,UAAU,CAClD;EACA,iBAEI,wBAAwB,SAAS,QAC/B,IAAI,WAAW,gBAAgB,EAAE,YAAY,YAAY,CAAC,EAAE,KAAK,CACnE,GACF,EAAE,IAAI,GAAG,wBAAwB,KAAK,aAAa,CACrD;EACA,iBAEI,wBAAwB,SAAS,QAC/B,IACG,wBACC,gBACA,EACE,WAAW,oBACb,GACA;GACE,YAAY;GACZ,UAAU;EACZ,CACF,EACC,KAAK,CACV,GACF,EACE,IAAI,GAAG,wBAAwB,KAAK,2BAA2B,iBACjE,CACF;EACA,iBAEI,wBAAwB,SAAS,QAC/B,IACG,wBACC,gBACA,EACE,WAAW,wBACb,GACA;GACE,YAAY;GACZ,UAAU;EACZ,CACF,EACC,KAAK,CACV,GACF,EACE,IAAI,GAAG,wBAAwB,KAAK,2BAA2B,qBACjE,CACF;CACF,CAAC;CACH,MAAM,cAAc,KAAK,WAAW,cAAc,OAAO,KAAK,KAAK,IAAI;CACvE,MAAM,kBAAkB,QAAQ,WAAW;CAC3C,MAAM,aAAa,kBAAkB,OAAO,QAAQ,MAAM,KAAK,IAAI;CACnE,MAAM,iBAAiB,qBAAqB,WAAW;CACvD,MAAM,qBAAqB,yBAAyB,WAAW;CAE/D,MAAM,qBAAqB,CACzB,GAAI,iBAAiB,qBAAqB,MAAM,QAAQ,CAAC,GACzD,GAAI,qBAAqB,yBAAyB,MAAM,QAAQ,CAAC,CACnE,EAAE,QACC,cAAsC,UAAU;EAC/C,MAAM,eACJ,MAAM,QAAQ,KAAK,OAAO;EAC5B,MAAM,SAAS,OAAO,aAAa,YAAY,MAAM;EACrD,IAAI,SAAS,IACX,aAAa,aAAa,SACvB,aAAa,aAAa,SAAS,MAAM;EAE9C,OAAO;CACT,GACA,CAAC,CACH;CAKA,MAAM,iBAAiB,kBAAkB;CAmBzC,OAjBoC,OAAO,KAAK,UAAU;EAExD,IADiB,MAAM,YAAY,kBACrB;GACZ,IAAI,CAAC,iBACH,OAAO;IAAE,GAAG;IAAO;GAAY;GAEjC,OAAO;IAAE,GAAG;IAAO,QAAQ;IAAY;GAAY;EACrD;EACA,MAAM,QAAQ,mBAAmB,MAAM;EACvC,IAAI,UAAU,KAAA,GACZ,OAAO;GAAE,GAAG;GAAO,QAAQ;GAAO;EAAY;EAEhD,IAAI,gBACF,OAAO;GAAE,GAAG;GAAO,QAAQ;GAAI;EAAY;EAE7C,OAAO;GAAE,GAAG;GAAO;EAAY;CACjC,CACkB;AACpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"sendAndConfirmBundle.d.ts","names":[],"sources":["../../../src/actions/sendAndConfirmBundle.ts"],"mappings":";;;;KAWK,eAAA;EACH,IAAA;EACA,aAAA;EACA,GAAA,EAAK,kBAAA;EACL,kBAAA,EAAoB,UAAA;EACpB,MAAA,EAAQ,QAAA;IAAW,GAAA,EAAK,kBAAA;EAAA,KAAsB,QAAA;IAAW,EAAA;EAAA;AAAA;AAAA,KAG/C,YAAA;EACV,QAAA;EACA,YAAA,EAAc,SAAA;EACd,gBAAA,GAAmB,eAAA;AAAA;;;;;;;;;iBAWC,oBAAA,CACpB,MAAA,EAAQ,SAAA,EACR,kBAAA,EAAoB,WAAA,KACnB,OAAA,CAAQ,YAAA"}
1
+ {"version":3,"file":"sendAndConfirmBundle.d.ts","names":[],"sources":["../../../src/actions/sendAndConfirmBundle.ts"],"mappings":";;;;KAWK,eAAA;EACH,IAAA;EACA,aAAA;EACA,GAAA,EAAK,kBAAA;EACL,kBAAA,EAAoB,UAAA;EACpB,MAAA,EAAQ,QAAA;IAAW,GAAA,EAAK,kBAAA;EAAA,KAAsB,QAAA;IAAW,EAAA;EAAA;AAAA;AAAA,KAG/C,YAAA;EACV,QAAA;EACA,YAAA,EAAc,SAAA;EACd,gBAAA,GAAmB,eAAA;AAAA;;;;;;;;;iBAaC,oBAAA,CACpB,MAAA,EAAQ,SAAA,EACR,kBAAA,EAAoB,WAAA,KACnB,OAAA,CAAQ,YAAA"}
@@ -2,6 +2,7 @@ import { getJitoRpcs } from "../rpc/registry.js";
2
2
  import { sleep } from "@lifi/sdk";
3
3
  import { getBase64EncodedWireTransaction } from "@solana/kit";
4
4
  //#region src/actions/sendAndConfirmBundle.ts
5
+ const NULL_BUNDLE_RESULT = /* @__PURE__ */ new Error("Bundle was not confirmed by this RPC");
5
6
  /**
6
7
  * Send and confirm a bundle of transactions using Jito.
7
8
  * Automatically selects a Jito-enabled RPC connection and polls for confirmation
@@ -50,7 +51,11 @@ async function sendAndConfirmBundle(client, signedTransactions) {
50
51
  throw error;
51
52
  }
52
53
  });
53
- const result = await Promise.any(confirmPromises).catch(() => null);
54
+ const result = await Promise.any(confirmPromises.map(async (promise) => {
55
+ const bundleResult = await promise;
56
+ if (!bundleResult) throw NULL_BUNDLE_RESULT;
57
+ return bundleResult;
58
+ })).catch(() => null);
54
59
  if (!abortController.signal.aborted) abortController.abort();
55
60
  if (!result) throw new Error("Failed to send and confirm bundle");
56
61
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"sendAndConfirmBundle.js","names":[],"sources":["../../../src/actions/sendAndConfirmBundle.ts"],"sourcesContent":["import { type SDKClient, sleep } from '@lifi/sdk'\nimport {\n type Commitment,\n getBase64EncodedWireTransaction,\n type Signature,\n type Transaction,\n type TransactionError,\n} from '@solana/kit'\n\nimport { getJitoRpcs } from '../rpc/registry.js'\n\ntype SignatureStatus = {\n slot: bigint\n confirmations: bigint | null\n err: TransactionError | null\n confirmationStatus: Commitment | null\n status: Readonly<{ Err: TransactionError }> | Readonly<{ Ok: null }>\n}\n\nexport type BundleResult = {\n bundleId: string\n txSignatures: Signature[]\n signatureResults: (SignatureStatus | null)[]\n}\n\n/**\n * Send and confirm a bundle of transactions using Jito.\n * Automatically selects a Jito-enabled RPC connection and polls for confirmation\n * across multiple Jito RPCs in parallel.\n * @param client - The SDK client.\n * @param signedTransactions - Array of signed transactions to bundle.\n * @returns BundleResult containing Bundle ID, transaction signatures, and confirmation results.\n */\nexport async function sendAndConfirmBundle(\n client: SDKClient,\n signedTransactions: Transaction[]\n): Promise<BundleResult> {\n const jitoRpcs = await getJitoRpcs(client)\n\n if (jitoRpcs.length === 0) {\n throw new Error(\n 'No Jito-enabled RPC connection available for bundle submission'\n )\n }\n\n // Serialize transactions to base64\n const serializedTransactions = signedTransactions.map((tx) =>\n getBase64EncodedWireTransaction(tx)\n )\n\n const abortController = new AbortController()\n\n const confirmPromises = jitoRpcs.map(async (jitoRpc) => {\n try {\n // Send bundle to Jito\n let bundleId: string\n try {\n bundleId = await jitoRpc.sendBundle(serializedTransactions).send()\n } catch (_) {\n return null\n }\n\n const [{ value: blockhashResult }, initialBlockHeight] =\n await Promise.all([\n jitoRpc\n .getLatestBlockhash({\n commitment: 'confirmed',\n })\n .send(),\n jitoRpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send(),\n ])\n\n let currentBlockHeight = initialBlockHeight\n\n while (\n currentBlockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted\n ) {\n const statusResponse = await jitoRpc\n .getBundleStatuses([bundleId])\n .send()\n\n const bundleStatus = statusResponse.value[0]\n\n // Check if bundle is confirmed or finalized\n if (\n bundleStatus &&\n (bundleStatus.confirmation_status === 'confirmed' ||\n bundleStatus.confirmation_status === 'finalized')\n ) {\n // Bundle confirmed! Extract transaction signatures from bundle status\n const txSignatures = bundleStatus.transactions\n\n // Fetch individual signature results from Jito RPC\n const sigResponse = await jitoRpc\n .getSignatureStatuses(txSignatures)\n .send()\n\n if (!sigResponse?.value || !Array.isArray(sigResponse.value)) {\n // Keep polling if can't find signature results\n await sleep(400)\n continue\n }\n\n // Immediately abort all other connections when we find a result\n abortController.abort()\n return {\n bundleId,\n txSignatures,\n signatureResults: sigResponse.value,\n }\n }\n\n await sleep(400)\n if (!abortController.signal.aborted) {\n currentBlockHeight = await jitoRpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send()\n }\n }\n\n return null\n } catch (error) {\n if (abortController.signal.aborted) {\n return null // Don't treat abortion as an error\n }\n throw error\n }\n })\n\n // Wait for first successful confirmation\n const result = await Promise.any(confirmPromises).catch(() => null)\n\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n\n if (!result) {\n throw new Error('Failed to send and confirm bundle')\n }\n\n return result\n}\n"],"mappings":";;;;;;;;;;;;AAiCA,eAAsB,qBACpB,QACA,oBACuB;CACvB,MAAM,WAAW,MAAM,YAAY,MAAM;CAEzC,IAAI,SAAS,WAAW,GACtB,MAAM,IAAI,MACR,gEACF;CAIF,MAAM,yBAAyB,mBAAmB,KAAK,OACrD,gCAAgC,EAAE,CACpC;CAEA,MAAM,kBAAkB,IAAI,gBAAgB;CAE5C,MAAM,kBAAkB,SAAS,IAAI,OAAO,YAAY;EACtD,IAAI;GAEF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,QAAQ,WAAW,sBAAsB,EAAE,KAAK;GACnE,SAAS,GAAG;IACV,OAAO;GACT;GAEA,MAAM,CAAC,EAAE,OAAO,mBAAmB,sBACjC,MAAM,QAAQ,IAAI,CAChB,QACG,mBAAmB,EAClB,YAAY,YACd,CAAC,EACA,KAAK,GACR,QACG,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK,CACV,CAAC;GAEH,IAAI,qBAAqB;GAEzB,OACE,qBAAqB,gBAAgB,wBACrC,CAAC,gBAAgB,OAAO,SACxB;IAKA,MAAM,gBAAe,MAJQ,QAC1B,kBAAkB,CAAC,QAAQ,CAAC,EAC5B,KAAK,GAE4B,MAAM;IAG1C,IACE,iBACC,aAAa,wBAAwB,eACpC,aAAa,wBAAwB,cACvC;KAEA,MAAM,eAAe,aAAa;KAGlC,MAAM,cAAc,MAAM,QACvB,qBAAqB,YAAY,EACjC,KAAK;KAER,IAAI,CAAC,aAAa,SAAS,CAAC,MAAM,QAAQ,YAAY,KAAK,GAAG;MAE5D,MAAM,MAAM,GAAG;MACf;KACF;KAGA,gBAAgB,MAAM;KACtB,OAAO;MACL;MACA;MACA,kBAAkB,YAAY;KAChC;IACF;IAEA,MAAM,MAAM,GAAG;IACf,IAAI,CAAC,gBAAgB,OAAO,SAC1B,qBAAqB,MAAM,QACxB,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK;GAEZ;GAEA,OAAO;EACT,SAAS,OAAO;GACd,IAAI,gBAAgB,OAAO,SACzB,OAAO;GAET,MAAM;EACR;CACF,CAAC;CAGD,MAAM,SAAS,MAAM,QAAQ,IAAI,eAAe,EAAE,YAAY,IAAI;CAElE,IAAI,CAAC,gBAAgB,OAAO,SAC1B,gBAAgB,MAAM;CAGxB,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mCAAmC;CAGrD,OAAO;AACT"}
1
+ {"version":3,"file":"sendAndConfirmBundle.js","names":[],"sources":["../../../src/actions/sendAndConfirmBundle.ts"],"sourcesContent":["import { type SDKClient, sleep } from '@lifi/sdk'\nimport {\n type Commitment,\n getBase64EncodedWireTransaction,\n type Signature,\n type Transaction,\n type TransactionError,\n} from '@solana/kit'\n\nimport { getJitoRpcs } from '../rpc/registry.js'\n\ntype SignatureStatus = {\n slot: bigint\n confirmations: bigint | null\n err: TransactionError | null\n confirmationStatus: Commitment | null\n status: Readonly<{ Err: TransactionError }> | Readonly<{ Ok: null }>\n}\n\nexport type BundleResult = {\n bundleId: string\n txSignatures: Signature[]\n signatureResults: (SignatureStatus | null)[]\n}\n\nconst NULL_BUNDLE_RESULT = new Error('Bundle was not confirmed by this RPC')\n\n/**\n * Send and confirm a bundle of transactions using Jito.\n * Automatically selects a Jito-enabled RPC connection and polls for confirmation\n * across multiple Jito RPCs in parallel.\n * @param client - The SDK client.\n * @param signedTransactions - Array of signed transactions to bundle.\n * @returns BundleResult containing Bundle ID, transaction signatures, and confirmation results.\n */\nexport async function sendAndConfirmBundle(\n client: SDKClient,\n signedTransactions: Transaction[]\n): Promise<BundleResult> {\n const jitoRpcs = await getJitoRpcs(client)\n\n if (jitoRpcs.length === 0) {\n throw new Error(\n 'No Jito-enabled RPC connection available for bundle submission'\n )\n }\n\n // Serialize transactions to base64\n const serializedTransactions = signedTransactions.map((tx) =>\n getBase64EncodedWireTransaction(tx)\n )\n\n const abortController = new AbortController()\n\n const confirmPromises = jitoRpcs.map(async (jitoRpc) => {\n try {\n // Send bundle to Jito\n let bundleId: string\n try {\n bundleId = await jitoRpc.sendBundle(serializedTransactions).send()\n } catch (_) {\n return null\n }\n\n const [{ value: blockhashResult }, initialBlockHeight] =\n await Promise.all([\n jitoRpc\n .getLatestBlockhash({\n commitment: 'confirmed',\n })\n .send(),\n jitoRpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send(),\n ])\n\n let currentBlockHeight = initialBlockHeight\n\n while (\n currentBlockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted\n ) {\n const statusResponse = await jitoRpc\n .getBundleStatuses([bundleId])\n .send()\n\n const bundleStatus = statusResponse.value[0]\n\n // Check if bundle is confirmed or finalized\n if (\n bundleStatus &&\n (bundleStatus.confirmation_status === 'confirmed' ||\n bundleStatus.confirmation_status === 'finalized')\n ) {\n // Bundle confirmed! Extract transaction signatures from bundle status\n const txSignatures = bundleStatus.transactions\n\n // Fetch individual signature results from Jito RPC\n const sigResponse = await jitoRpc\n .getSignatureStatuses(txSignatures)\n .send()\n\n if (!sigResponse?.value || !Array.isArray(sigResponse.value)) {\n // Keep polling if can't find signature results\n await sleep(400)\n continue\n }\n\n // Immediately abort all other connections when we find a result\n abortController.abort()\n return {\n bundleId,\n txSignatures,\n signatureResults: sigResponse.value,\n }\n }\n\n await sleep(400)\n if (!abortController.signal.aborted) {\n currentBlockHeight = await jitoRpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send()\n }\n }\n\n return null\n } catch (error) {\n if (abortController.signal.aborted) {\n return null // Don't treat abortion as an error\n }\n throw error\n }\n })\n\n // Wait for first successful confirmation\n const result = await Promise.any(\n confirmPromises.map(async (promise) => {\n const bundleResult = await promise\n if (!bundleResult) {\n throw NULL_BUNDLE_RESULT\n }\n return bundleResult\n })\n ).catch(() => null)\n\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n\n if (!result) {\n throw new Error('Failed to send and confirm bundle')\n }\n\n return result\n}\n"],"mappings":";;;;AAyBA,MAAM,qCAAqB,IAAI,MAAM,sCAAsC;;;;;;;;;AAU3E,eAAsB,qBACpB,QACA,oBACuB;CACvB,MAAM,WAAW,MAAM,YAAY,MAAM;CAEzC,IAAI,SAAS,WAAW,GACtB,MAAM,IAAI,MACR,gEACF;CAIF,MAAM,yBAAyB,mBAAmB,KAAK,OACrD,gCAAgC,EAAE,CACpC;CAEA,MAAM,kBAAkB,IAAI,gBAAgB;CAE5C,MAAM,kBAAkB,SAAS,IAAI,OAAO,YAAY;EACtD,IAAI;GAEF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,QAAQ,WAAW,sBAAsB,EAAE,KAAK;GACnE,SAAS,GAAG;IACV,OAAO;GACT;GAEA,MAAM,CAAC,EAAE,OAAO,mBAAmB,sBACjC,MAAM,QAAQ,IAAI,CAChB,QACG,mBAAmB,EAClB,YAAY,YACd,CAAC,EACA,KAAK,GACR,QACG,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK,CACV,CAAC;GAEH,IAAI,qBAAqB;GAEzB,OACE,qBAAqB,gBAAgB,wBACrC,CAAC,gBAAgB,OAAO,SACxB;IAKA,MAAM,gBAAe,MAJQ,QAC1B,kBAAkB,CAAC,QAAQ,CAAC,EAC5B,KAAK,GAE4B,MAAM;IAG1C,IACE,iBACC,aAAa,wBAAwB,eACpC,aAAa,wBAAwB,cACvC;KAEA,MAAM,eAAe,aAAa;KAGlC,MAAM,cAAc,MAAM,QACvB,qBAAqB,YAAY,EACjC,KAAK;KAER,IAAI,CAAC,aAAa,SAAS,CAAC,MAAM,QAAQ,YAAY,KAAK,GAAG;MAE5D,MAAM,MAAM,GAAG;MACf;KACF;KAGA,gBAAgB,MAAM;KACtB,OAAO;MACL;MACA;MACA,kBAAkB,YAAY;KAChC;IACF;IAEA,MAAM,MAAM,GAAG;IACf,IAAI,CAAC,gBAAgB,OAAO,SAC1B,qBAAqB,MAAM,QACxB,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK;GAEZ;GAEA,OAAO;EACT,SAAS,OAAO;GACd,IAAI,gBAAgB,OAAO,SACzB,OAAO;GAET,MAAM;EACR;CACF,CAAC;CAGD,MAAM,SAAS,MAAM,QAAQ,IAC3B,gBAAgB,IAAI,OAAO,YAAY;EACrC,MAAM,eAAe,MAAM;EAC3B,IAAI,CAAC,cACH,MAAM;EAER,OAAO;CACT,CAAC,CACH,EAAE,YAAY,IAAI;CAElB,IAAI,CAAC,gBAAgB,OAAO,SAC1B,gBAAgB,MAAM;CAGxB,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,mCAAmC;CAGrD,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"sendAndConfirmTransaction.d.ts","names":[],"sources":["../../../src/actions/sendAndConfirmTransaction.ts"],"mappings":";;;;KAUK,eAAA;EACH,IAAA;EACA,aAAA;EACA,GAAA,EAAK,kBAAA;EACL,kBAAA,EAAoB,UAAA;EACpB,MAAA,EAAQ,QAAA;IAAW,GAAA,EAAK,kBAAA;EAAA,KAAsB,QAAA;IAAW,EAAA;EAAA;AAAA;AAAA,KAGtD,0BAAA;EACH,eAAA,EAAiB,eAAA;EACjB,WAAA;AAAA;;;;;;;;iBAUoB,yBAAA,CACpB,MAAA,EAAQ,SAAA,EACR,iBAAA,EAAmB,WAAA,GAClB,OAAA,CAAQ,0BAAA"}
1
+ {"version":3,"file":"sendAndConfirmTransaction.d.ts","names":[],"sources":["../../../src/actions/sendAndConfirmTransaction.ts"],"mappings":";;;;KAUK,eAAA;EACH,IAAA;EACA,aAAA;EACA,GAAA,EAAK,kBAAA;EACL,kBAAA,EAAoB,UAAA;EACpB,MAAA,EAAQ,QAAA;IAAW,GAAA,EAAK,kBAAA;EAAA,KAAsB,QAAA;IAAW,EAAA;EAAA;AAAA;AAAA,KAGtD,0BAAA;EACH,eAAA,EAAiB,eAAA;EACjB,WAAA;AAAA;;;;;;;;iBAcoB,yBAAA,CACpB,MAAA,EAAQ,SAAA,EACR,iBAAA,EAAmB,WAAA,GAClB,OAAA,CAAQ,0BAAA"}
@@ -2,6 +2,7 @@ import { getSolanaRpcs } from "../rpc/registry.js";
2
2
  import { sleep } from "@lifi/sdk";
3
3
  import { getBase64EncodedWireTransaction, getSignatureFromTransaction } from "@solana/kit";
4
4
  //#region src/actions/sendAndConfirmTransaction.ts
5
+ const NULL_CONFIRMATION_RESULT = /* @__PURE__ */ new Error("Transaction was not confirmed by this RPC");
5
6
  /**
6
7
  * Sends a Solana transaction to multiple RPC endpoints and returns the confirmation
7
8
  * as soon as any of them confirm the transaction.
@@ -57,7 +58,11 @@ async function sendAndConfirmTransaction(client, signedTransaction) {
57
58
  throw error;
58
59
  }
59
60
  });
60
- const signatureResult = await Promise.any(confirmPromises).catch(() => null);
61
+ const signatureResult = await Promise.any(confirmPromises.map(async (promise) => {
62
+ const result = await promise;
63
+ if (!result) throw NULL_CONFIRMATION_RESULT;
64
+ return result;
65
+ })).catch(() => null);
61
66
  if (!abortController.signal.aborted) abortController.abort();
62
67
  return {
63
68
  signatureResult,
@@ -1 +1 @@
1
- {"version":3,"file":"sendAndConfirmTransaction.js","names":[],"sources":["../../../src/actions/sendAndConfirmTransaction.ts"],"sourcesContent":["import { type SDKClient, sleep } from '@lifi/sdk'\nimport {\n type Commitment,\n getBase64EncodedWireTransaction,\n getSignatureFromTransaction,\n type Transaction,\n type TransactionError,\n} from '@solana/kit'\nimport { getSolanaRpcs } from '../rpc/registry.js'\n\ntype SignatureStatus = {\n slot: bigint\n confirmations: bigint | null\n err: TransactionError | null\n confirmationStatus: Commitment | null\n status: Readonly<{ Err: TransactionError }> | Readonly<{ Ok: null }>\n}\n\ntype ConfirmedTransactionResult = {\n signatureResult: SignatureStatus | null\n txSignature: string\n}\n\n/**\n * Sends a Solana transaction to multiple RPC endpoints and returns the confirmation\n * as soon as any of them confirm the transaction.\n * @param client - The SDK client.\n * @param signedTransaction - The signed transaction to send.\n * @returns - The confirmation result of the transaction.\n */\nexport async function sendAndConfirmTransaction(\n client: SDKClient,\n signedTransaction: Transaction\n): Promise<ConfirmedTransactionResult> {\n const solanaRpcs = await getSolanaRpcs(client)\n\n const signedTxSerialized = getBase64EncodedWireTransaction(signedTransaction)\n // Create transaction hash (signature)\n const txSignature = getSignatureFromTransaction(signedTransaction)\n\n if (!txSignature) {\n throw new Error('Transaction signature is missing.')\n }\n\n const rawTransactionOptions = {\n // We can skip preflight check after the first transaction has been sent\n // https://solana.com/docs/advanced/retry#the-cost-of-skipping-preflight\n skipPreflight: true,\n // Setting max retries to 0 as we are handling retries manually\n maxRetries: BigInt(0),\n // https://solana.com/docs/advanced/confirmation#use-an-appropriate-preflight-commitment-level\n preflightCommitment: 'confirmed' as Commitment,\n encoding: 'base64' as const,\n }\n\n const abortController = new AbortController()\n\n const confirmPromises = solanaRpcs.map(async (rpc) => {\n try {\n // Send initial transaction for this RPC\n try {\n await rpc\n .sendTransaction(signedTxSerialized, rawTransactionOptions)\n .send()\n } catch (_) {\n // Continue with confirmation even if initial send fails\n }\n\n const [{ value: blockhashResult }, initialBlockHeight] =\n await Promise.all([\n rpc\n .getLatestBlockhash({\n commitment: 'confirmed',\n })\n .send(),\n rpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send(),\n ])\n\n let signatureResult: SignatureStatus | null = null\n let blockHeight = initialBlockHeight\n const pollingPromise = (async () => {\n while (\n blockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted\n ) {\n const statusResponse = await rpc\n .getSignatureStatuses([txSignature])\n .send()\n\n const status = statusResponse.value[0]\n if (\n status &&\n (status.confirmationStatus === 'confirmed' ||\n status.confirmationStatus === 'finalized')\n ) {\n signatureResult = status\n // Immediately abort all other RPCs when we find a result\n abortController.abort()\n return status\n }\n\n await sleep(400)\n }\n return null\n })()\n\n const sendingPromise = (async () => {\n while (\n blockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted &&\n !signatureResult\n ) {\n try {\n await rpc\n .sendTransaction(signedTxSerialized, rawTransactionOptions)\n .send()\n } catch (_) {\n // Continue trying even if individual sends fail\n }\n\n await sleep(1000)\n if (!abortController.signal.aborted) {\n blockHeight = await rpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send()\n }\n }\n return null\n })()\n\n // Wait for polling to find the result\n const result = await Promise.race([pollingPromise, sendingPromise])\n return result\n } catch (error) {\n if (abortController.signal.aborted) {\n return null // Don't treat abortion as an error\n }\n throw error\n }\n })\n\n const signatureResult = await Promise.any(confirmPromises).catch(() => null)\n\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n\n return { signatureResult, txSignature }\n}\n"],"mappings":";;;;;;;;;;;AA8BA,eAAsB,0BACpB,QACA,mBACqC;CACrC,MAAM,aAAa,MAAM,cAAc,MAAM;CAE7C,MAAM,qBAAqB,gCAAgC,iBAAiB;CAE5E,MAAM,cAAc,4BAA4B,iBAAiB;CAEjE,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,mCAAmC;CAGrD,MAAM,wBAAwB;EAG5B,eAAe;EAEf,YAAY,OAAO,CAAC;EAEpB,qBAAqB;EACrB,UAAU;CACZ;CAEA,MAAM,kBAAkB,IAAI,gBAAgB;CAE5C,MAAM,kBAAkB,WAAW,IAAI,OAAO,QAAQ;EACpD,IAAI;GAEF,IAAI;IACF,MAAM,IACH,gBAAgB,oBAAoB,qBAAqB,EACzD,KAAK;GACV,SAAS,GAAG,CAEZ;GAEA,MAAM,CAAC,EAAE,OAAO,mBAAmB,sBACjC,MAAM,QAAQ,IAAI,CAChB,IACG,mBAAmB,EAClB,YAAY,YACd,CAAC,EACA,KAAK,GACR,IACG,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK,CACV,CAAC;GAEH,IAAI,kBAA0C;GAC9C,IAAI,cAAc;GAClB,MAAM,kBAAkB,YAAY;IAClC,OACE,cAAc,gBAAgB,wBAC9B,CAAC,gBAAgB,OAAO,SACxB;KAKA,MAAM,UAAS,MAJc,IAC1B,qBAAqB,CAAC,WAAW,CAAC,EAClC,KAAK,GAEsB,MAAM;KACpC,IACE,WACC,OAAO,uBAAuB,eAC7B,OAAO,uBAAuB,cAChC;MACA,kBAAkB;MAElB,gBAAgB,MAAM;MACtB,OAAO;KACT;KAEA,MAAM,MAAM,GAAG;IACjB;IACA,OAAO;GACT,GAAG;GAEH,MAAM,kBAAkB,YAAY;IAClC,OACE,cAAc,gBAAgB,wBAC9B,CAAC,gBAAgB,OAAO,WACxB,CAAC,iBACD;KACA,IAAI;MACF,MAAM,IACH,gBAAgB,oBAAoB,qBAAqB,EACzD,KAAK;KACV,SAAS,GAAG,CAEZ;KAEA,MAAM,MAAM,GAAI;KAChB,IAAI,CAAC,gBAAgB,OAAO,SAC1B,cAAc,MAAM,IACjB,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK;IAEZ;IACA,OAAO;GACT,GAAG;GAIH,OAAO,MADc,QAAQ,KAAK,CAAC,gBAAgB,cAAc,CAAC;EAEpE,SAAS,OAAO;GACd,IAAI,gBAAgB,OAAO,SACzB,OAAO;GAET,MAAM;EACR;CACF,CAAC;CAED,MAAM,kBAAkB,MAAM,QAAQ,IAAI,eAAe,EAAE,YAAY,IAAI;CAE3E,IAAI,CAAC,gBAAgB,OAAO,SAC1B,gBAAgB,MAAM;CAGxB,OAAO;EAAE;EAAiB;CAAY;AACxC"}
1
+ {"version":3,"file":"sendAndConfirmTransaction.js","names":[],"sources":["../../../src/actions/sendAndConfirmTransaction.ts"],"sourcesContent":["import { type SDKClient, sleep } from '@lifi/sdk'\nimport {\n type Commitment,\n getBase64EncodedWireTransaction,\n getSignatureFromTransaction,\n type Transaction,\n type TransactionError,\n} from '@solana/kit'\nimport { getSolanaRpcs } from '../rpc/registry.js'\n\ntype SignatureStatus = {\n slot: bigint\n confirmations: bigint | null\n err: TransactionError | null\n confirmationStatus: Commitment | null\n status: Readonly<{ Err: TransactionError }> | Readonly<{ Ok: null }>\n}\n\ntype ConfirmedTransactionResult = {\n signatureResult: SignatureStatus | null\n txSignature: string\n}\n\nconst NULL_CONFIRMATION_RESULT = new Error(\n 'Transaction was not confirmed by this RPC'\n)\n\n/**\n * Sends a Solana transaction to multiple RPC endpoints and returns the confirmation\n * as soon as any of them confirm the transaction.\n * @param client - The SDK client.\n * @param signedTransaction - The signed transaction to send.\n * @returns - The confirmation result of the transaction.\n */\nexport async function sendAndConfirmTransaction(\n client: SDKClient,\n signedTransaction: Transaction\n): Promise<ConfirmedTransactionResult> {\n const solanaRpcs = await getSolanaRpcs(client)\n\n const signedTxSerialized = getBase64EncodedWireTransaction(signedTransaction)\n // Create transaction hash (signature)\n const txSignature = getSignatureFromTransaction(signedTransaction)\n\n if (!txSignature) {\n throw new Error('Transaction signature is missing.')\n }\n\n const rawTransactionOptions = {\n // We can skip preflight check after the first transaction has been sent\n // https://solana.com/docs/advanced/retry#the-cost-of-skipping-preflight\n skipPreflight: true,\n // Setting max retries to 0 as we are handling retries manually\n maxRetries: BigInt(0),\n // https://solana.com/docs/advanced/confirmation#use-an-appropriate-preflight-commitment-level\n preflightCommitment: 'confirmed' as Commitment,\n encoding: 'base64' as const,\n }\n\n const abortController = new AbortController()\n\n const confirmPromises = solanaRpcs.map(async (rpc) => {\n try {\n // Send initial transaction for this RPC\n try {\n await rpc\n .sendTransaction(signedTxSerialized, rawTransactionOptions)\n .send()\n } catch (_) {\n // Continue with confirmation even if initial send fails\n }\n\n const [{ value: blockhashResult }, initialBlockHeight] =\n await Promise.all([\n rpc\n .getLatestBlockhash({\n commitment: 'confirmed',\n })\n .send(),\n rpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send(),\n ])\n\n let signatureResult: SignatureStatus | null = null\n let blockHeight = initialBlockHeight\n const pollingPromise = (async () => {\n while (\n blockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted\n ) {\n const statusResponse = await rpc\n .getSignatureStatuses([txSignature])\n .send()\n\n const status = statusResponse.value[0]\n if (\n status &&\n (status.confirmationStatus === 'confirmed' ||\n status.confirmationStatus === 'finalized')\n ) {\n signatureResult = status\n // Immediately abort all other RPCs when we find a result\n abortController.abort()\n return status\n }\n\n await sleep(400)\n }\n return null\n })()\n\n const sendingPromise = (async () => {\n while (\n blockHeight < blockhashResult.lastValidBlockHeight &&\n !abortController.signal.aborted &&\n !signatureResult\n ) {\n try {\n await rpc\n .sendTransaction(signedTxSerialized, rawTransactionOptions)\n .send()\n } catch (_) {\n // Continue trying even if individual sends fail\n }\n\n await sleep(1000)\n if (!abortController.signal.aborted) {\n blockHeight = await rpc\n .getBlockHeight({\n commitment: 'confirmed',\n })\n .send()\n }\n }\n return null\n })()\n\n // Wait for polling to find the result\n const result = await Promise.race([pollingPromise, sendingPromise])\n return result\n } catch (error) {\n if (abortController.signal.aborted) {\n return null // Don't treat abortion as an error\n }\n throw error\n }\n })\n\n const signatureResult = await Promise.any(\n confirmPromises.map(async (promise) => {\n const result = await promise\n if (!result) {\n throw NULL_CONFIRMATION_RESULT\n }\n return result\n })\n ).catch(() => null)\n\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n\n return { signatureResult, txSignature }\n}\n"],"mappings":";;;;AAuBA,MAAM,2CAA2B,IAAI,MACnC,2CACF;;;;;;;;AASA,eAAsB,0BACpB,QACA,mBACqC;CACrC,MAAM,aAAa,MAAM,cAAc,MAAM;CAE7C,MAAM,qBAAqB,gCAAgC,iBAAiB;CAE5E,MAAM,cAAc,4BAA4B,iBAAiB;CAEjE,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,mCAAmC;CAGrD,MAAM,wBAAwB;EAG5B,eAAe;EAEf,YAAY,OAAO,CAAC;EAEpB,qBAAqB;EACrB,UAAU;CACZ;CAEA,MAAM,kBAAkB,IAAI,gBAAgB;CAE5C,MAAM,kBAAkB,WAAW,IAAI,OAAO,QAAQ;EACpD,IAAI;GAEF,IAAI;IACF,MAAM,IACH,gBAAgB,oBAAoB,qBAAqB,EACzD,KAAK;GACV,SAAS,GAAG,CAEZ;GAEA,MAAM,CAAC,EAAE,OAAO,mBAAmB,sBACjC,MAAM,QAAQ,IAAI,CAChB,IACG,mBAAmB,EAClB,YAAY,YACd,CAAC,EACA,KAAK,GACR,IACG,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK,CACV,CAAC;GAEH,IAAI,kBAA0C;GAC9C,IAAI,cAAc;GAClB,MAAM,kBAAkB,YAAY;IAClC,OACE,cAAc,gBAAgB,wBAC9B,CAAC,gBAAgB,OAAO,SACxB;KAKA,MAAM,UAAS,MAJc,IAC1B,qBAAqB,CAAC,WAAW,CAAC,EAClC,KAAK,GAEsB,MAAM;KACpC,IACE,WACC,OAAO,uBAAuB,eAC7B,OAAO,uBAAuB,cAChC;MACA,kBAAkB;MAElB,gBAAgB,MAAM;MACtB,OAAO;KACT;KAEA,MAAM,MAAM,GAAG;IACjB;IACA,OAAO;GACT,GAAG;GAEH,MAAM,kBAAkB,YAAY;IAClC,OACE,cAAc,gBAAgB,wBAC9B,CAAC,gBAAgB,OAAO,WACxB,CAAC,iBACD;KACA,IAAI;MACF,MAAM,IACH,gBAAgB,oBAAoB,qBAAqB,EACzD,KAAK;KACV,SAAS,GAAG,CAEZ;KAEA,MAAM,MAAM,GAAI;KAChB,IAAI,CAAC,gBAAgB,OAAO,SAC1B,cAAc,MAAM,IACjB,eAAe,EACd,YAAY,YACd,CAAC,EACA,KAAK;IAEZ;IACA,OAAO;GACT,GAAG;GAIH,OAAO,MADc,QAAQ,KAAK,CAAC,gBAAgB,cAAc,CAAC;EAEpE,SAAS,OAAO;GACd,IAAI,gBAAgB,OAAO,SACzB,OAAO;GAET,MAAM;EACR;CACF,CAAC;CAED,MAAM,kBAAkB,MAAM,QAAQ,IACpC,gBAAgB,IAAI,OAAO,YAAY;EACrC,MAAM,SAAS,MAAM;EACrB,IAAI,CAAC,QACH,MAAM;EAER,OAAO;CACT,CAAC,CACH,EAAE,YAAY,IAAI;CAElB,IAAI,CAAC,gBAAgB,OAAO,SAC1B,gBAAgB,MAAM;CAGxB,OAAO;EAAE;EAAiB;CAAY;AACxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"SolanaJitoWaitForTransactionTask.d.ts","names":[],"sources":["../../../../src/core/tasks/SolanaJitoWaitForTransactionTask.ts"],"mappings":";;;;cASa,gCAAA,SAAyC,qBAAA;EACpD,GAAA,CAAU,OAAA,EAAS,yBAAA,GAA4B,OAAA,CAAQ,UAAA;AAAA"}
1
+ {"version":3,"file":"SolanaJitoWaitForTransactionTask.d.ts","names":[],"sources":["../../../../src/core/tasks/SolanaJitoWaitForTransactionTask.ts"],"mappings":";;;;cAUa,gCAAA,SAAyC,qBAAA;EACpD,GAAA,CAAU,OAAA,EAAS,yBAAA,GAA4B,OAAA,CAAQ,UAAA;AAAA"}
@@ -1,4 +1,5 @@
1
1
  import { sendAndConfirmBundle } from "../../actions/sendAndConfirmBundle.js";
2
+ import { SolanaTransactionDetailsError } from "../../utils/solanaErrorCause.js";
2
3
  import { BaseStepExecutionTask, LiFiErrorCode, TransactionError } from "@lifi/sdk";
3
4
  //#region src/core/tasks/SolanaJitoWaitForTransactionTask.ts
4
5
  var SolanaJitoWaitForTransactionTask = class extends BaseStepExecutionTask {
@@ -7,12 +8,13 @@ var SolanaJitoWaitForTransactionTask = class extends BaseStepExecutionTask {
7
8
  const signedTransactions = contextSignedTransactions ?? [];
8
9
  const action = statusManager.findAction(step, isBridgeExecution ? "CROSS_CHAIN" : "SWAP");
9
10
  if (!action) throw new TransactionError(LiFiErrorCode.TransactionUnprepared, "Unable to prepare transaction. Action not found.");
11
+ if (!signedTransactions.length) throw new TransactionError(LiFiErrorCode.TransactionUnprepared, "Unable to prepare transaction. Signed transactions are not found.");
10
12
  const bundleResult = await sendAndConfirmBundle(client, signedTransactions);
11
13
  if (!bundleResult.signatureResults.every((result) => result !== null)) throw new TransactionError(LiFiErrorCode.TransactionFailed, "Bundle confirmation failed: Not all transactions were confirmed.");
12
14
  const failedResult = bundleResult.signatureResults.find((result) => result?.err);
13
15
  if (failedResult?.err) {
14
- const reason = typeof failedResult.err === "object" ? JSON.stringify(failedResult.err) : String(failedResult.err);
15
- throw new TransactionError(LiFiErrorCode.TransactionFailed, `Transaction failed: ${reason}`);
16
+ const cause = new SolanaTransactionDetailsError(failedResult.err);
17
+ throw new TransactionError(LiFiErrorCode.TransactionFailed, `Transaction failed: ${cause.message}`, cause);
16
18
  }
17
19
  const confirmedTransaction = {
18
20
  txSignature: bundleResult.txSignatures[0],
@@ -1 +1 @@
1
- {"version":3,"file":"SolanaJitoWaitForTransactionTask.js","names":[],"sources":["../../../../src/core/tasks/SolanaJitoWaitForTransactionTask.ts"],"sourcesContent":["import {\n BaseStepExecutionTask,\n LiFiErrorCode,\n type TaskResult,\n TransactionError,\n} from '@lifi/sdk'\nimport { sendAndConfirmBundle } from '../../actions/sendAndConfirmBundle.js'\nimport type { SolanaStepExecutorContext } from '../../types.js'\n\nexport class SolanaJitoWaitForTransactionTask extends BaseStepExecutionTask {\n async run(context: SolanaStepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n fromChain,\n isBridgeExecution,\n signedTransactions: contextSignedTransactions,\n } = context\n\n const signedTransactions = contextSignedTransactions ?? []\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n // Use Jito bundle for transaction submission\n const bundleResult = await sendAndConfirmBundle(client, signedTransactions)\n\n const allConfirmed = bundleResult.signatureResults.every(\n (result) => result !== null\n )\n\n if (!allConfirmed) {\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n 'Bundle confirmation failed: Not all transactions were confirmed.'\n )\n }\n\n // Check for errors in any of the transactions\n const failedResult = bundleResult.signatureResults.find(\n (result) => result?.err\n )\n if (failedResult?.err) {\n const reason =\n typeof failedResult.err === 'object'\n ? JSON.stringify(failedResult.err)\n : String(failedResult.err)\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n `Transaction failed: ${reason}`\n )\n }\n\n const confirmedTransaction = {\n txSignature: bundleResult.txSignatures[0],\n bundleId: bundleResult.bundleId,\n }\n\n // Transaction has been confirmed and we can update the action\n statusManager.updateAction(step, action.type, 'PENDING', {\n txHash: confirmedTransaction.txSignature,\n txLink: `${fromChain.metamask.blockExplorerUrls[0]}tx/${confirmedTransaction.txSignature}`,\n })\n\n if (isBridgeExecution) {\n statusManager.updateAction(step, action.type, 'DONE')\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;AASA,IAAa,mCAAb,cAAsD,sBAAsB;CAC1E,MAAM,IAAI,SAAyD;EACjE,MAAM,EACJ,QACA,MACA,eACA,WACA,mBACA,oBAAoB,8BAClB;EAEJ,MAAM,qBAAqB,6BAA6B,CAAC;EAEzD,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EACA,IAAI,CAAC,QACH,MAAM,IAAI,iBACR,cAAc,uBACd,kDACF;EAIF,MAAM,eAAe,MAAM,qBAAqB,QAAQ,kBAAkB;EAM1E,IAAI,CAJiB,aAAa,iBAAiB,OAChD,WAAW,WAAW,IAGT,GACd,MAAM,IAAI,iBACR,cAAc,mBACd,kEACF;EAIF,MAAM,eAAe,aAAa,iBAAiB,MAChD,WAAW,QAAQ,GACtB;EACA,IAAI,cAAc,KAAK;GACrB,MAAM,SACJ,OAAO,aAAa,QAAQ,WACxB,KAAK,UAAU,aAAa,GAAG,IAC/B,OAAO,aAAa,GAAG;GAC7B,MAAM,IAAI,iBACR,cAAc,mBACd,uBAAuB,QACzB;EACF;EAEA,MAAM,uBAAuB;GAC3B,aAAa,aAAa,aAAa;GACvC,UAAU,aAAa;EACzB;EAGA,cAAc,aAAa,MAAM,OAAO,MAAM,WAAW;GACvD,QAAQ,qBAAqB;GAC7B,QAAQ,GAAG,UAAU,SAAS,kBAAkB,GAAG,KAAK,qBAAqB;EAC/E,CAAC;EAED,IAAI,mBACF,cAAc,aAAa,MAAM,OAAO,MAAM,MAAM;EAGtD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
1
+ {"version":3,"file":"SolanaJitoWaitForTransactionTask.js","names":[],"sources":["../../../../src/core/tasks/SolanaJitoWaitForTransactionTask.ts"],"sourcesContent":["import {\n BaseStepExecutionTask,\n LiFiErrorCode,\n type TaskResult,\n TransactionError,\n} from '@lifi/sdk'\nimport { sendAndConfirmBundle } from '../../actions/sendAndConfirmBundle.js'\nimport type { SolanaStepExecutorContext } from '../../types.js'\nimport { SolanaTransactionDetailsError } from '../../utils/solanaErrorCause.js'\n\nexport class SolanaJitoWaitForTransactionTask extends BaseStepExecutionTask {\n async run(context: SolanaStepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n fromChain,\n isBridgeExecution,\n signedTransactions: contextSignedTransactions,\n } = context\n\n const signedTransactions = contextSignedTransactions ?? []\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (!signedTransactions.length) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Signed transactions are not found.'\n )\n }\n\n // Use Jito bundle for transaction submission\n const bundleResult = await sendAndConfirmBundle(client, signedTransactions)\n\n const allConfirmed = bundleResult.signatureResults.every(\n (result) => result !== null\n )\n\n if (!allConfirmed) {\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n 'Bundle confirmation failed: Not all transactions were confirmed.'\n )\n }\n\n // Check for errors in any of the transactions\n const failedResult = bundleResult.signatureResults.find(\n (result) => result?.err\n )\n if (failedResult?.err) {\n const cause = new SolanaTransactionDetailsError(failedResult.err)\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n `Transaction failed: ${cause.message}`,\n cause\n )\n }\n\n const confirmedTransaction = {\n txSignature: bundleResult.txSignatures[0],\n bundleId: bundleResult.bundleId,\n }\n\n // Transaction has been confirmed and we can update the action\n statusManager.updateAction(step, action.type, 'PENDING', {\n txHash: confirmedTransaction.txSignature,\n txLink: `${fromChain.metamask.blockExplorerUrls[0]}tx/${confirmedTransaction.txSignature}`,\n })\n\n if (isBridgeExecution) {\n statusManager.updateAction(step, action.type, 'DONE')\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;AAUA,IAAa,mCAAb,cAAsD,sBAAsB;CAC1E,MAAM,IAAI,SAAyD;EACjE,MAAM,EACJ,QACA,MACA,eACA,WACA,mBACA,oBAAoB,8BAClB;EAEJ,MAAM,qBAAqB,6BAA6B,CAAC;EAEzD,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EACA,IAAI,CAAC,QACH,MAAM,IAAI,iBACR,cAAc,uBACd,kDACF;EAGF,IAAI,CAAC,mBAAmB,QACtB,MAAM,IAAI,iBACR,cAAc,uBACd,mEACF;EAIF,MAAM,eAAe,MAAM,qBAAqB,QAAQ,kBAAkB;EAM1E,IAAI,CAJiB,aAAa,iBAAiB,OAChD,WAAW,WAAW,IAGT,GACd,MAAM,IAAI,iBACR,cAAc,mBACd,kEACF;EAIF,MAAM,eAAe,aAAa,iBAAiB,MAChD,WAAW,QAAQ,GACtB;EACA,IAAI,cAAc,KAAK;GACrB,MAAM,QAAQ,IAAI,8BAA8B,aAAa,GAAG;GAChE,MAAM,IAAI,iBACR,cAAc,mBACd,uBAAuB,MAAM,WAC7B,KACF;EACF;EAEA,MAAM,uBAAuB;GAC3B,aAAa,aAAa,aAAa;GACvC,UAAU,aAAa;EACzB;EAGA,cAAc,aAAa,MAAM,OAAO,MAAM,WAAW;GACvD,QAAQ,qBAAqB;GAC7B,QAAQ,GAAG,UAAU,SAAS,kBAAkB,GAAG,KAAK,qBAAqB;EAC/E,CAAC;EAED,IAAI,mBACF,cAAc,aAAa,MAAM,OAAO,MAAM,MAAM;EAGtD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"SolanaStandardWaitForTransactionTask.d.ts","names":[],"sources":["../../../../src/core/tasks/SolanaStandardWaitForTransactionTask.ts"],"mappings":";;;;cAWa,oCAAA,SAA6C,qBAAA;EACxD,GAAA,CAAU,OAAA,EAAS,yBAAA,GAA4B,OAAA,CAAQ,UAAA;AAAA"}
1
+ {"version":3,"file":"SolanaStandardWaitForTransactionTask.d.ts","names":[],"sources":["../../../../src/core/tasks/SolanaStandardWaitForTransactionTask.ts"],"mappings":";;;;cAYa,oCAAA,SAA6C,qBAAA;EACxD,GAAA,CAAU,OAAA,EAAS,yBAAA,GAA4B,OAAA,CAAQ,UAAA;AAAA"}
@@ -1,4 +1,5 @@
1
1
  import { callSolanaRpcsWithRetry } from "../../rpc/utils.js";
2
+ import { SolanaTransactionDetailsError } from "../../utils/solanaErrorCause.js";
2
3
  import { sendAndConfirmTransaction } from "../../actions/sendAndConfirmTransaction.js";
3
4
  import { BaseStepExecutionTask, LiFiErrorCode, TransactionError } from "@lifi/sdk";
4
5
  import { getBase64EncodedWireTransaction } from "@solana/kit";
@@ -19,15 +20,15 @@ var SolanaStandardWaitForTransactionTask = class extends BaseStepExecutionTask {
19
20
  encoding: "base64"
20
21
  }).send());
21
22
  if (simulationResult.value.err) {
22
- const errorMessage = typeof simulationResult.value.err === "object" ? JSON.stringify(simulationResult.value.err, (_, v) => typeof v === "bigint" ? v.toString() : v) : simulationResult.value.err;
23
- throw new TransactionError(LiFiErrorCode.TransactionSimulationFailed, `Transaction simulation failed: ${errorMessage}`, new Error(errorMessage));
23
+ const cause = new SolanaTransactionDetailsError(simulationResult.value.err, simulationResult.value.logs);
24
+ throw new TransactionError(LiFiErrorCode.TransactionSimulationFailed, `Transaction simulation failed: ${cause.message}`, cause);
24
25
  }
25
26
  }
26
27
  const result = await sendAndConfirmTransaction(client, signedTransaction);
27
28
  if (!result.signatureResult) throw new TransactionError(LiFiErrorCode.TransactionExpired, "Transaction has expired: The block height has exceeded the maximum allowed limit.");
28
29
  if (result.signatureResult.err) {
29
- const reason = typeof result.signatureResult.err === "object" ? JSON.stringify(result.signatureResult.err, (_, v) => typeof v === "bigint" ? v.toString() : v) : result.signatureResult.err;
30
- throw new TransactionError(LiFiErrorCode.TransactionFailed, `Transaction failed: ${reason}`);
30
+ const cause = new SolanaTransactionDetailsError(result.signatureResult.err);
31
+ throw new TransactionError(LiFiErrorCode.TransactionFailed, `Transaction failed: ${cause.message}`, cause);
31
32
  }
32
33
  const confirmedTransaction = { txSignature: result.txSignature };
33
34
  statusManager.updateAction(step, action.type, "PENDING", {
@@ -1 +1 @@
1
- {"version":3,"file":"SolanaStandardWaitForTransactionTask.js","names":[],"sources":["../../../../src/core/tasks/SolanaStandardWaitForTransactionTask.ts"],"sourcesContent":["import {\n BaseStepExecutionTask,\n LiFiErrorCode,\n type TaskResult,\n TransactionError,\n} from '@lifi/sdk'\nimport { getBase64EncodedWireTransaction } from '@solana/kit'\nimport { sendAndConfirmTransaction } from '../../actions/sendAndConfirmTransaction.js'\nimport { callSolanaRpcsWithRetry } from '../../rpc/utils.js'\nimport type { SolanaStepExecutorContext } from '../../types.js'\n\nexport class SolanaStandardWaitForTransactionTask extends BaseStepExecutionTask {\n async run(context: SolanaStepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n fromChain,\n isBridgeExecution,\n signedTransactions: contextSignedTransactions,\n } = context\n\n const signedTransactions = contextSignedTransactions ?? []\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (!signedTransactions.length) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Signed transactions are not found.'\n )\n }\n\n // Use regular transaction submission\n const signedTransaction = signedTransactions[0]\n\n const encodedTransaction =\n getBase64EncodedWireTransaction(signedTransaction)\n\n if (!context.skipSimulation) {\n const simulationResult = await callSolanaRpcsWithRetry(\n client,\n (connection) =>\n connection\n .simulateTransaction(encodedTransaction, {\n commitment: 'confirmed',\n replaceRecentBlockhash: true,\n encoding: 'base64',\n })\n .send()\n )\n\n if (simulationResult.value.err) {\n const errorMessage =\n typeof simulationResult.value.err === 'object'\n ? JSON.stringify(simulationResult.value.err, (_, v) =>\n typeof v === 'bigint' ? v.toString() : v\n )\n : simulationResult.value.err\n throw new TransactionError(\n LiFiErrorCode.TransactionSimulationFailed,\n `Transaction simulation failed: ${errorMessage}`,\n new Error(errorMessage)\n )\n }\n }\n\n const result = await sendAndConfirmTransaction(client, signedTransaction)\n\n if (!result.signatureResult) {\n throw new TransactionError(\n LiFiErrorCode.TransactionExpired,\n 'Transaction has expired: The block height has exceeded the maximum allowed limit.'\n )\n }\n\n if (result.signatureResult.err) {\n const reason =\n typeof result.signatureResult.err === 'object'\n ? JSON.stringify(result.signatureResult.err, (_, v) =>\n typeof v === 'bigint' ? v.toString() : v\n )\n : result.signatureResult.err\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n `Transaction failed: ${reason}`\n )\n }\n\n const confirmedTransaction = {\n txSignature: result.txSignature,\n }\n\n // Transaction has been confirmed and we can update the action\n statusManager.updateAction(step, action.type, 'PENDING', {\n txHash: confirmedTransaction.txSignature,\n txLink: `${fromChain.metamask.blockExplorerUrls[0]}tx/${confirmedTransaction.txSignature}`,\n })\n\n if (isBridgeExecution) {\n statusManager.updateAction(step, action.type, 'DONE')\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;AAWA,IAAa,uCAAb,cAA0D,sBAAsB;CAC9E,MAAM,IAAI,SAAyD;EACjE,MAAM,EACJ,QACA,MACA,eACA,WACA,mBACA,oBAAoB,8BAClB;EAEJ,MAAM,qBAAqB,6BAA6B,CAAC;EAEzD,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EACA,IAAI,CAAC,QACH,MAAM,IAAI,iBACR,cAAc,uBACd,kDACF;EAGF,IAAI,CAAC,mBAAmB,QACtB,MAAM,IAAI,iBACR,cAAc,uBACd,mEACF;EAIF,MAAM,oBAAoB,mBAAmB;EAE7C,MAAM,qBACJ,gCAAgC,iBAAiB;EAEnD,IAAI,CAAC,QAAQ,gBAAgB;GAC3B,MAAM,mBAAmB,MAAM,wBAC7B,SACC,eACC,WACG,oBAAoB,oBAAoB;IACvC,YAAY;IACZ,wBAAwB;IACxB,UAAU;GACZ,CAAC,EACA,KAAK,CACZ;GAEA,IAAI,iBAAiB,MAAM,KAAK;IAC9B,MAAM,eACJ,OAAO,iBAAiB,MAAM,QAAQ,WAClC,KAAK,UAAU,iBAAiB,MAAM,MAAM,GAAG,MAC7C,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CACzC,IACA,iBAAiB,MAAM;IAC7B,MAAM,IAAI,iBACR,cAAc,6BACd,kCAAkC,gBAClC,IAAI,MAAM,YAAY,CACxB;GACF;EACF;EAEA,MAAM,SAAS,MAAM,0BAA0B,QAAQ,iBAAiB;EAExE,IAAI,CAAC,OAAO,iBACV,MAAM,IAAI,iBACR,cAAc,oBACd,mFACF;EAGF,IAAI,OAAO,gBAAgB,KAAK;GAC9B,MAAM,SACJ,OAAO,OAAO,gBAAgB,QAAQ,WAClC,KAAK,UAAU,OAAO,gBAAgB,MAAM,GAAG,MAC7C,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CACzC,IACA,OAAO,gBAAgB;GAC7B,MAAM,IAAI,iBACR,cAAc,mBACd,uBAAuB,QACzB;EACF;EAEA,MAAM,uBAAuB,EAC3B,aAAa,OAAO,YACtB;EAGA,cAAc,aAAa,MAAM,OAAO,MAAM,WAAW;GACvD,QAAQ,qBAAqB;GAC7B,QAAQ,GAAG,UAAU,SAAS,kBAAkB,GAAG,KAAK,qBAAqB;EAC/E,CAAC;EAED,IAAI,mBACF,cAAc,aAAa,MAAM,OAAO,MAAM,MAAM;EAGtD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
1
+ {"version":3,"file":"SolanaStandardWaitForTransactionTask.js","names":[],"sources":["../../../../src/core/tasks/SolanaStandardWaitForTransactionTask.ts"],"sourcesContent":["import {\n BaseStepExecutionTask,\n LiFiErrorCode,\n type TaskResult,\n TransactionError,\n} from '@lifi/sdk'\nimport { getBase64EncodedWireTransaction } from '@solana/kit'\nimport { sendAndConfirmTransaction } from '../../actions/sendAndConfirmTransaction.js'\nimport { callSolanaRpcsWithRetry } from '../../rpc/utils.js'\nimport type { SolanaStepExecutorContext } from '../../types.js'\nimport { SolanaTransactionDetailsError } from '../../utils/solanaErrorCause.js'\n\nexport class SolanaStandardWaitForTransactionTask extends BaseStepExecutionTask {\n async run(context: SolanaStepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n fromChain,\n isBridgeExecution,\n signedTransactions: contextSignedTransactions,\n } = context\n\n const signedTransactions = contextSignedTransactions ?? []\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (!signedTransactions.length) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Signed transactions are not found.'\n )\n }\n\n // Use regular transaction submission\n const signedTransaction = signedTransactions[0]\n\n const encodedTransaction =\n getBase64EncodedWireTransaction(signedTransaction)\n\n if (!context.skipSimulation) {\n const simulationResult = await callSolanaRpcsWithRetry(\n client,\n (connection) =>\n connection\n .simulateTransaction(encodedTransaction, {\n commitment: 'confirmed',\n replaceRecentBlockhash: true,\n encoding: 'base64',\n })\n .send()\n )\n\n if (simulationResult.value.err) {\n const cause = new SolanaTransactionDetailsError(\n simulationResult.value.err,\n simulationResult.value.logs\n )\n throw new TransactionError(\n LiFiErrorCode.TransactionSimulationFailed,\n `Transaction simulation failed: ${cause.message}`,\n cause\n )\n }\n }\n\n const result = await sendAndConfirmTransaction(client, signedTransaction)\n\n if (!result.signatureResult) {\n throw new TransactionError(\n LiFiErrorCode.TransactionExpired,\n 'Transaction has expired: The block height has exceeded the maximum allowed limit.'\n )\n }\n\n if (result.signatureResult.err) {\n const cause = new SolanaTransactionDetailsError(\n result.signatureResult.err\n )\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n `Transaction failed: ${cause.message}`,\n cause\n )\n }\n\n const confirmedTransaction = {\n txSignature: result.txSignature,\n }\n\n // Transaction has been confirmed and we can update the action\n statusManager.updateAction(step, action.type, 'PENDING', {\n txHash: confirmedTransaction.txSignature,\n txLink: `${fromChain.metamask.blockExplorerUrls[0]}tx/${confirmedTransaction.txSignature}`,\n })\n\n if (isBridgeExecution) {\n statusManager.updateAction(step, action.type, 'DONE')\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;;AAYA,IAAa,uCAAb,cAA0D,sBAAsB;CAC9E,MAAM,IAAI,SAAyD;EACjE,MAAM,EACJ,QACA,MACA,eACA,WACA,mBACA,oBAAoB,8BAClB;EAEJ,MAAM,qBAAqB,6BAA6B,CAAC;EAEzD,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EACA,IAAI,CAAC,QACH,MAAM,IAAI,iBACR,cAAc,uBACd,kDACF;EAGF,IAAI,CAAC,mBAAmB,QACtB,MAAM,IAAI,iBACR,cAAc,uBACd,mEACF;EAIF,MAAM,oBAAoB,mBAAmB;EAE7C,MAAM,qBACJ,gCAAgC,iBAAiB;EAEnD,IAAI,CAAC,QAAQ,gBAAgB;GAC3B,MAAM,mBAAmB,MAAM,wBAC7B,SACC,eACC,WACG,oBAAoB,oBAAoB;IACvC,YAAY;IACZ,wBAAwB;IACxB,UAAU;GACZ,CAAC,EACA,KAAK,CACZ;GAEA,IAAI,iBAAiB,MAAM,KAAK;IAC9B,MAAM,QAAQ,IAAI,8BAChB,iBAAiB,MAAM,KACvB,iBAAiB,MAAM,IACzB;IACA,MAAM,IAAI,iBACR,cAAc,6BACd,kCAAkC,MAAM,WACxC,KACF;GACF;EACF;EAEA,MAAM,SAAS,MAAM,0BAA0B,QAAQ,iBAAiB;EAExE,IAAI,CAAC,OAAO,iBACV,MAAM,IAAI,iBACR,cAAc,oBACd,mFACF;EAGF,IAAI,OAAO,gBAAgB,KAAK;GAC9B,MAAM,QAAQ,IAAI,8BAChB,OAAO,gBAAgB,GACzB;GACA,MAAM,IAAI,iBACR,cAAc,mBACd,uBAAuB,MAAM,WAC7B,KACF;EACF;EAEA,MAAM,uBAAuB,EAC3B,aAAa,OAAO,YACtB;EAGA,cAAc,aAAa,MAAM,OAAO,MAAM,WAAW;GACvD,QAAQ,qBAAqB;GAC7B,QAAQ,GAAG,UAAU,SAAS,kBAAkB,GAAG,KAAK,qBAAqB;EAC/E,CAAC;EAED,IAAI,mBACF,cAAc,aAAa,MAAM,OAAO,MAAM,MAAM;EAGtD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
@@ -1,5 +1,6 @@
1
1
  import { SolanaProviderOptions, SolanaSDKProvider, isSolanaProvider } from "./types.js";
2
2
  import { SolanaProvider } from "./SolanaProvider.js";
3
3
  import { KeypairWalletAdapter } from "./utils/KeypairWalletAdapter.js";
4
+ import { SolanaTransactionDetailsError } from "./utils/solanaErrorCause.js";
4
5
  import { address as toAddress } from "@solana/kit";
5
- export { KeypairWalletAdapter, SolanaProvider, type SolanaProviderOptions, type SolanaSDKProvider, isSolanaProvider, toAddress };
6
+ export { KeypairWalletAdapter, SolanaProvider, type SolanaProviderOptions, type SolanaSDKProvider, SolanaTransactionDetailsError, isSolanaProvider, toAddress };
package/dist/esm/index.js CHANGED
@@ -1,5 +1,6 @@
1
+ import { SolanaTransactionDetailsError } from "./utils/solanaErrorCause.js";
1
2
  import { SolanaProvider } from "./SolanaProvider.js";
2
3
  import { isSolanaProvider } from "./types.js";
3
4
  import { KeypairWalletAdapter } from "./utils/KeypairWalletAdapter.js";
4
5
  import { address as toAddress } from "@solana/kit";
5
- export { KeypairWalletAdapter, SolanaProvider, isSolanaProvider, toAddress };
6
+ export { KeypairWalletAdapter, SolanaProvider, SolanaTransactionDetailsError, isSolanaProvider, toAddress };
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","names":[],"sources":["../../../src/rpc/registry.ts"],"mappings":";;;;;;AAWA;cAAa,SAAA,GAAmB,MAAA,aAAiB,OAAA;;;AAAA;AAwCjD;cAAa,aAAA,GACX,MAAA,EAAQ,SAAA,KACP,OAAA,CAAQ,aAAA;;;;;cASE,WAAA,GACX,MAAA,EAAQ,SAAA,KACP,OAAA,CAAQ,WAAA"}
1
+ {"version":3,"file":"registry.d.ts","names":[],"sources":["../../../src/rpc/registry.ts"],"mappings":";;;;;;AAWA;cAAa,SAAA,GAAmB,MAAA,aAAiB,OAAA;;;AAAA;AA0CjD;cAAa,aAAA,GACX,MAAA,EAAQ,SAAA,KACP,OAAA,CAAQ,aAAA;;;;;cAWE,WAAA,GACX,MAAA,EAAQ,SAAA,KACP,OAAA,CAAQ,WAAA"}
@@ -22,6 +22,7 @@ const isJitoRpc = async (rpcUrl) => {
22
22
  const ensureSolanaRpcs = async (client) => {
23
23
  const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL);
24
24
  for (const rpcUrl of rpcUrls) if (!solanaRpcs.has(rpcUrl)) solanaRpcs.set(rpcUrl, createSolanaRpc(rpcUrl));
25
+ return rpcUrls;
25
26
  };
26
27
  /**
27
28
  * Detects and caches Jito-capable RPCs by checking if they support the getTipAccounts method.
@@ -30,22 +31,21 @@ const ensureSolanaRpcs = async (client) => {
30
31
  const ensureJitoRpcs = async (client) => {
31
32
  const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL);
32
33
  for (const rpcUrl of rpcUrls) if (!jitoRpcs.has(rpcUrl) && await isJitoRpc(rpcUrl)) jitoRpcs.set(rpcUrl, createJitoRpc(rpcUrl));
34
+ return rpcUrls;
33
35
  };
34
36
  /**
35
37
  * Wrapper around getting the Solana RPCs
36
38
  * @returns - Solana RPCs
37
39
  */
38
40
  const getSolanaRpcs = async (client) => {
39
- await ensureSolanaRpcs(client);
40
- return Array.from(solanaRpcs.values());
41
+ return (await ensureSolanaRpcs(client)).map((rpcUrl) => solanaRpcs.get(rpcUrl)).filter((rpc) => Boolean(rpc));
41
42
  };
42
43
  /**
43
44
  * Wrapper around getting the Jito RPCs
44
45
  * @returns - Jito RPCs
45
46
  */
46
47
  const getJitoRpcs = async (client) => {
47
- await ensureJitoRpcs(client);
48
- return Array.from(jitoRpcs.values());
48
+ return (await ensureJitoRpcs(client)).map((rpcUrl) => jitoRpcs.get(rpcUrl)).filter((rpc) => Boolean(rpc));
49
49
  };
50
50
  //#endregion
51
51
  export { getJitoRpcs, getSolanaRpcs, isJitoRpc };
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","names":[],"sources":["../../../src/rpc/registry.ts"],"sourcesContent":["import { ChainId, LruMap, type SDKClient } from '@lifi/sdk'\nimport { createSolanaRpc } from '@solana/kit'\nimport { createJitoRpc } from './jito/createJitoRpc.js'\nimport type { JitoRpcType, SolanaRpcType } from './types.js'\n\nconst solanaRpcs = new LruMap<SolanaRpcType>(12)\nconst jitoRpcs = new LruMap<JitoRpcType>(12)\n\n/**\n * Checks if an RPC URL supports Jito methods by calling getTipAccounts.\n */\nexport const isJitoRpc = async (rpcUrl: string): Promise<boolean> => {\n try {\n const rpc = createJitoRpc(rpcUrl)\n await rpc.getTipAccounts().send()\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Initializes Solana RPCs for all available RPC URLs if they haven't been cached yet.\n * @param client - The SDK client used to fetch RPC URLs.\n */\nconst ensureSolanaRpcs = async (client: SDKClient): Promise<void> => {\n const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)\n for (const rpcUrl of rpcUrls) {\n if (!solanaRpcs.has(rpcUrl)) {\n solanaRpcs.set(rpcUrl, createSolanaRpc(rpcUrl))\n }\n }\n}\n\n/**\n * Detects and caches Jito-capable RPCs by checking if they support the getTipAccounts method.\n * @param client - The SDK client used to fetch RPC URLs.\n */\nconst ensureJitoRpcs = async (client: SDKClient): Promise<void> => {\n const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)\n for (const rpcUrl of rpcUrls) {\n if (!jitoRpcs.has(rpcUrl) && (await isJitoRpc(rpcUrl))) {\n jitoRpcs.set(rpcUrl, createJitoRpc(rpcUrl))\n }\n }\n}\n\n/**\n * Wrapper around getting the Solana RPCs\n * @returns - Solana RPCs\n */\nexport const getSolanaRpcs = async (\n client: SDKClient\n): Promise<SolanaRpcType[]> => {\n await ensureSolanaRpcs(client)\n return Array.from(solanaRpcs.values())\n}\n\n/**\n * Wrapper around getting the Jito RPCs\n * @returns - Jito RPCs\n */\nexport const getJitoRpcs = async (\n client: SDKClient\n): Promise<JitoRpcType[]> => {\n await ensureJitoRpcs(client)\n return Array.from(jitoRpcs.values())\n}\n"],"mappings":";;;;AAKA,MAAM,aAAa,IAAI,OAAsB,EAAE;AAC/C,MAAM,WAAW,IAAI,OAAoB,EAAE;;;;AAK3C,MAAa,YAAY,OAAO,WAAqC;CACnE,IAAI;EAEF,MADY,cAAc,MAClB,EAAE,eAAe,EAAE,KAAK;EAChC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,MAAM,mBAAmB,OAAO,WAAqC;CACnE,MAAM,UAAU,MAAM,OAAO,oBAAoB,QAAQ,GAAG;CAC5D,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,WAAW,IAAI,MAAM,GACxB,WAAW,IAAI,QAAQ,gBAAgB,MAAM,CAAC;AAGpD;;;;;AAMA,MAAM,iBAAiB,OAAO,WAAqC;CACjE,MAAM,UAAU,MAAM,OAAO,oBAAoB,QAAQ,GAAG;CAC5D,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,SAAS,IAAI,MAAM,KAAM,MAAM,UAAU,MAAM,GAClD,SAAS,IAAI,QAAQ,cAAc,MAAM,CAAC;AAGhD;;;;;AAMA,MAAa,gBAAgB,OAC3B,WAC6B;CAC7B,MAAM,iBAAiB,MAAM;CAC7B,OAAO,MAAM,KAAK,WAAW,OAAO,CAAC;AACvC;;;;;AAMA,MAAa,cAAc,OACzB,WAC2B;CAC3B,MAAM,eAAe,MAAM;CAC3B,OAAO,MAAM,KAAK,SAAS,OAAO,CAAC;AACrC"}
1
+ {"version":3,"file":"registry.js","names":[],"sources":["../../../src/rpc/registry.ts"],"sourcesContent":["import { ChainId, LruMap, type SDKClient } from '@lifi/sdk'\nimport { createSolanaRpc } from '@solana/kit'\nimport { createJitoRpc } from './jito/createJitoRpc.js'\nimport type { JitoRpcType, SolanaRpcType } from './types.js'\n\nconst solanaRpcs = new LruMap<SolanaRpcType>(12)\nconst jitoRpcs = new LruMap<JitoRpcType>(12)\n\n/**\n * Checks if an RPC URL supports Jito methods by calling getTipAccounts.\n */\nexport const isJitoRpc = async (rpcUrl: string): Promise<boolean> => {\n try {\n const rpc = createJitoRpc(rpcUrl)\n await rpc.getTipAccounts().send()\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Initializes Solana RPCs for all available RPC URLs if they haven't been cached yet.\n * @param client - The SDK client used to fetch RPC URLs.\n */\nconst ensureSolanaRpcs = async (client: SDKClient): Promise<string[]> => {\n const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)\n for (const rpcUrl of rpcUrls) {\n if (!solanaRpcs.has(rpcUrl)) {\n solanaRpcs.set(rpcUrl, createSolanaRpc(rpcUrl))\n }\n }\n return rpcUrls\n}\n\n/**\n * Detects and caches Jito-capable RPCs by checking if they support the getTipAccounts method.\n * @param client - The SDK client used to fetch RPC URLs.\n */\nconst ensureJitoRpcs = async (client: SDKClient): Promise<string[]> => {\n const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)\n for (const rpcUrl of rpcUrls) {\n if (!jitoRpcs.has(rpcUrl) && (await isJitoRpc(rpcUrl))) {\n jitoRpcs.set(rpcUrl, createJitoRpc(rpcUrl))\n }\n }\n return rpcUrls\n}\n\n/**\n * Wrapper around getting the Solana RPCs\n * @returns - Solana RPCs\n */\nexport const getSolanaRpcs = async (\n client: SDKClient\n): Promise<SolanaRpcType[]> => {\n const rpcUrls = await ensureSolanaRpcs(client)\n return rpcUrls\n .map((rpcUrl) => solanaRpcs.get(rpcUrl))\n .filter((rpc): rpc is SolanaRpcType => Boolean(rpc))\n}\n\n/**\n * Wrapper around getting the Jito RPCs\n * @returns - Jito RPCs\n */\nexport const getJitoRpcs = async (\n client: SDKClient\n): Promise<JitoRpcType[]> => {\n const rpcUrls = await ensureJitoRpcs(client)\n return rpcUrls\n .map((rpcUrl) => jitoRpcs.get(rpcUrl))\n .filter((rpc): rpc is JitoRpcType => Boolean(rpc))\n}\n"],"mappings":";;;;AAKA,MAAM,aAAa,IAAI,OAAsB,EAAE;AAC/C,MAAM,WAAW,IAAI,OAAoB,EAAE;;;;AAK3C,MAAa,YAAY,OAAO,WAAqC;CACnE,IAAI;EAEF,MADY,cAAc,MAClB,EAAE,eAAe,EAAE,KAAK;EAChC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,MAAM,mBAAmB,OAAO,WAAyC;CACvE,MAAM,UAAU,MAAM,OAAO,oBAAoB,QAAQ,GAAG;CAC5D,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,WAAW,IAAI,MAAM,GACxB,WAAW,IAAI,QAAQ,gBAAgB,MAAM,CAAC;CAGlD,OAAO;AACT;;;;;AAMA,MAAM,iBAAiB,OAAO,WAAyC;CACrE,MAAM,UAAU,MAAM,OAAO,oBAAoB,QAAQ,GAAG;CAC5D,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,SAAS,IAAI,MAAM,KAAM,MAAM,UAAU,MAAM,GAClD,SAAS,IAAI,QAAQ,cAAc,MAAM,CAAC;CAG9C,OAAO;AACT;;;;;AAMA,MAAa,gBAAgB,OAC3B,WAC6B;CAE7B,QAAO,MADe,iBAAiB,MAAM,GAE1C,KAAK,WAAW,WAAW,IAAI,MAAM,CAAC,EACtC,QAAQ,QAA8B,QAAQ,GAAG,CAAC;AACvD;;;;;AAMA,MAAa,cAAc,OACzB,WAC2B;CAE3B,QAAO,MADe,eAAe,MAAM,GAExC,KAAK,WAAW,SAAS,IAAI,MAAM,CAAC,EACpC,QAAQ,QAA4B,QAAQ,GAAG,CAAC;AACrD"}
@@ -0,0 +1,15 @@
1
+ //#region src/utils/solanaErrorCause.d.ts
2
+ declare const safeStringifyBigInt: (value: unknown) => string;
3
+ /**
4
+ * Carries the structured payload of a failed Solana RPC simulation or
5
+ * confirmation so consumers can inspect `err` and `logs` directly from
6
+ * the thrown `TransactionError`'s `cause`, without re-simulating.
7
+ */
8
+ declare class SolanaTransactionDetailsError extends Error {
9
+ readonly err: unknown;
10
+ readonly logs: readonly string[] | null;
11
+ constructor(err: unknown, logs?: readonly string[] | null);
12
+ }
13
+ //#endregion
14
+ export { SolanaTransactionDetailsError, safeStringifyBigInt };
15
+ //# sourceMappingURL=solanaErrorCause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solanaErrorCause.d.ts","names":[],"sources":["../../../src/utils/solanaErrorCause.ts"],"mappings":";cAAa,mBAAA,GAAuB,KAAA;AAApC;;;;AAAoC;AAApC,cAaa,6BAAA,SAAsC,KAAA;EAAA,SACxC,GAAA;EAAA,SACA,IAAA;EAET,WAAA,CAAY,GAAA,WAAc,IAAA;AAAA"}
@@ -0,0 +1,22 @@
1
+ //#region src/utils/solanaErrorCause.ts
2
+ const safeStringifyBigInt = (value) => JSON.stringify(value, (_, v) => typeof v === "bigint" ? v.toString() : v);
3
+ const formatSolanaErr = (err) => typeof err === "object" && err !== null ? safeStringifyBigInt(err) : String(err);
4
+ /**
5
+ * Carries the structured payload of a failed Solana RPC simulation or
6
+ * confirmation so consumers can inspect `err` and `logs` directly from
7
+ * the thrown `TransactionError`'s `cause`, without re-simulating.
8
+ */
9
+ var SolanaTransactionDetailsError = class extends Error {
10
+ err;
11
+ logs;
12
+ constructor(err, logs = null) {
13
+ super(formatSolanaErr(err));
14
+ this.name = "SolanaTransactionDetailsError";
15
+ this.err = err;
16
+ this.logs = logs;
17
+ }
18
+ };
19
+ //#endregion
20
+ export { SolanaTransactionDetailsError, safeStringifyBigInt };
21
+
22
+ //# sourceMappingURL=solanaErrorCause.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solanaErrorCause.js","names":[],"sources":["../../../src/utils/solanaErrorCause.ts"],"sourcesContent":["export const safeStringifyBigInt = (value: unknown): string =>\n JSON.stringify(value, (_, v) => (typeof v === 'bigint' ? v.toString() : v))\n\nconst formatSolanaErr = (err: unknown): string =>\n typeof err === 'object' && err !== null\n ? safeStringifyBigInt(err)\n : String(err)\n\n/**\n * Carries the structured payload of a failed Solana RPC simulation or\n * confirmation so consumers can inspect `err` and `logs` directly from\n * the thrown `TransactionError`'s `cause`, without re-simulating.\n */\nexport class SolanaTransactionDetailsError extends Error {\n readonly err: unknown\n readonly logs: readonly string[] | null\n\n constructor(err: unknown, logs: readonly string[] | null = null) {\n super(formatSolanaErr(err))\n this.name = 'SolanaTransactionDetailsError'\n this.err = err\n this.logs = logs\n }\n}\n"],"mappings":";AAAA,MAAa,uBAAuB,UAClC,KAAK,UAAU,QAAQ,GAAG,MAAO,OAAO,MAAM,WAAW,EAAE,SAAS,IAAI,CAAE;AAE5E,MAAM,mBAAmB,QACvB,OAAO,QAAQ,YAAY,QAAQ,OAC/B,oBAAoB,GAAG,IACvB,OAAO,GAAG;;;;;;AAOhB,IAAa,gCAAb,cAAmD,MAAM;CACvD;CACA;CAEA,YAAY,KAAc,OAAiC,MAAM;EAC/D,MAAM,gBAAgB,GAAG,CAAC;EAC1B,KAAK,OAAO;EACZ,KAAK,MAAM;EACX,KAAK,OAAO;CACd;AACF"}
@@ -1,6 +1,6 @@
1
1
  //#region src/version.d.ts
2
2
  declare const name = "@lifi/sdk-provider-solana";
3
- declare const version = "4.0.0-beta.11";
3
+ declare const version = "4.0.0-beta.12";
4
4
  //#endregion
5
5
  export { name, version };
6
6
  //# sourceMappingURL=version.d.ts.map
@@ -1,6 +1,6 @@
1
1
  //#region src/version.ts
2
2
  const name = "@lifi/sdk-provider-solana";
3
- const version = "4.0.0-beta.11";
3
+ const version = "4.0.0-beta.12";
4
4
  //#endregion
5
5
  export { name, version };
6
6
 
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","names":[],"sources":["../../src/version.ts"],"sourcesContent":["export const name = '@lifi/sdk-provider-solana'\nexport const version = '4.0.0-beta.11'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
1
+ {"version":3,"file":"version.js","names":[],"sources":["../../src/version.ts"],"sourcesContent":["export const name = '@lifi/sdk-provider-solana'\nexport const version = '4.0.0-beta.12'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifi/sdk-provider-solana",
3
- "version": "4.0.0-beta.11",
3
+ "version": "4.0.0-beta.12",
4
4
  "description": "LI.FI Solana SDK Provider for Any-to-Any Cross-Chain-Swap",
5
5
  "homepage": "https://github.com/lifinance/sdk",
6
6
  "bugs": {
@@ -37,6 +37,7 @@
37
37
  "access": "public"
38
38
  },
39
39
  "files": [
40
+ "CHANGELOG.md",
40
41
  "dist/**",
41
42
  "!dist/**/*.tsbuildinfo",
42
43
  "src/**/*.ts",
@@ -115,7 +115,8 @@ const getSolanaBalanceDefault = async (
115
115
  value.account.data.parsed.info
116
116
  const amount = BigInt(tokenAccount.tokenAmount.amount)
117
117
  if (amount > 0n) {
118
- tokenAmounts[tokenAccount.mint] = amount
118
+ tokenAmounts[tokenAccount.mint] =
119
+ (tokenAmounts[tokenAccount.mint] ?? 0n) + amount
119
120
  }
120
121
  return tokenAmounts
121
122
  },
@@ -23,6 +23,8 @@ export type BundleResult = {
23
23
  signatureResults: (SignatureStatus | null)[]
24
24
  }
25
25
 
26
+ const NULL_BUNDLE_RESULT = new Error('Bundle was not confirmed by this RPC')
27
+
26
28
  /**
27
29
  * Send and confirm a bundle of transactions using Jito.
28
30
  * Automatically selects a Jito-enabled RPC connection and polls for confirmation
@@ -135,7 +137,15 @@ export async function sendAndConfirmBundle(
135
137
  })
136
138
 
137
139
  // Wait for first successful confirmation
138
- const result = await Promise.any(confirmPromises).catch(() => null)
140
+ const result = await Promise.any(
141
+ confirmPromises.map(async (promise) => {
142
+ const bundleResult = await promise
143
+ if (!bundleResult) {
144
+ throw NULL_BUNDLE_RESULT
145
+ }
146
+ return bundleResult
147
+ })
148
+ ).catch(() => null)
139
149
 
140
150
  if (!abortController.signal.aborted) {
141
151
  abortController.abort()
@@ -21,6 +21,10 @@ type ConfirmedTransactionResult = {
21
21
  txSignature: string
22
22
  }
23
23
 
24
+ const NULL_CONFIRMATION_RESULT = new Error(
25
+ 'Transaction was not confirmed by this RPC'
26
+ )
27
+
24
28
  /**
25
29
  * Sends a Solana transaction to multiple RPC endpoints and returns the confirmation
26
30
  * as soon as any of them confirm the transaction.
@@ -145,7 +149,15 @@ export async function sendAndConfirmTransaction(
145
149
  }
146
150
  })
147
151
 
148
- const signatureResult = await Promise.any(confirmPromises).catch(() => null)
152
+ const signatureResult = await Promise.any(
153
+ confirmPromises.map(async (promise) => {
154
+ const result = await promise
155
+ if (!result) {
156
+ throw NULL_CONFIRMATION_RESULT
157
+ }
158
+ return result
159
+ })
160
+ ).catch(() => null)
149
161
 
150
162
  if (!abortController.signal.aborted) {
151
163
  abortController.abort()
@@ -6,6 +6,7 @@ import {
6
6
  } from '@lifi/sdk'
7
7
  import { sendAndConfirmBundle } from '../../actions/sendAndConfirmBundle.js'
8
8
  import type { SolanaStepExecutorContext } from '../../types.js'
9
+ import { SolanaTransactionDetailsError } from '../../utils/solanaErrorCause.js'
9
10
 
10
11
  export class SolanaJitoWaitForTransactionTask extends BaseStepExecutionTask {
11
12
  async run(context: SolanaStepExecutorContext): Promise<TaskResult> {
@@ -31,6 +32,13 @@ export class SolanaJitoWaitForTransactionTask extends BaseStepExecutionTask {
31
32
  )
32
33
  }
33
34
 
35
+ if (!signedTransactions.length) {
36
+ throw new TransactionError(
37
+ LiFiErrorCode.TransactionUnprepared,
38
+ 'Unable to prepare transaction. Signed transactions are not found.'
39
+ )
40
+ }
41
+
34
42
  // Use Jito bundle for transaction submission
35
43
  const bundleResult = await sendAndConfirmBundle(client, signedTransactions)
36
44
 
@@ -50,13 +58,11 @@ export class SolanaJitoWaitForTransactionTask extends BaseStepExecutionTask {
50
58
  (result) => result?.err
51
59
  )
52
60
  if (failedResult?.err) {
53
- const reason =
54
- typeof failedResult.err === 'object'
55
- ? JSON.stringify(failedResult.err)
56
- : String(failedResult.err)
61
+ const cause = new SolanaTransactionDetailsError(failedResult.err)
57
62
  throw new TransactionError(
58
63
  LiFiErrorCode.TransactionFailed,
59
- `Transaction failed: ${reason}`
64
+ `Transaction failed: ${cause.message}`,
65
+ cause
60
66
  )
61
67
  }
62
68
 
@@ -8,6 +8,7 @@ import { getBase64EncodedWireTransaction } from '@solana/kit'
8
8
  import { sendAndConfirmTransaction } from '../../actions/sendAndConfirmTransaction.js'
9
9
  import { callSolanaRpcsWithRetry } from '../../rpc/utils.js'
10
10
  import type { SolanaStepExecutorContext } from '../../types.js'
11
+ import { SolanaTransactionDetailsError } from '../../utils/solanaErrorCause.js'
11
12
 
12
13
  export class SolanaStandardWaitForTransactionTask extends BaseStepExecutionTask {
13
14
  async run(context: SolanaStepExecutorContext): Promise<TaskResult> {
@@ -60,16 +61,14 @@ export class SolanaStandardWaitForTransactionTask extends BaseStepExecutionTask
60
61
  )
61
62
 
62
63
  if (simulationResult.value.err) {
63
- const errorMessage =
64
- typeof simulationResult.value.err === 'object'
65
- ? JSON.stringify(simulationResult.value.err, (_, v) =>
66
- typeof v === 'bigint' ? v.toString() : v
67
- )
68
- : simulationResult.value.err
64
+ const cause = new SolanaTransactionDetailsError(
65
+ simulationResult.value.err,
66
+ simulationResult.value.logs
67
+ )
69
68
  throw new TransactionError(
70
69
  LiFiErrorCode.TransactionSimulationFailed,
71
- `Transaction simulation failed: ${errorMessage}`,
72
- new Error(errorMessage)
70
+ `Transaction simulation failed: ${cause.message}`,
71
+ cause
73
72
  )
74
73
  }
75
74
  }
@@ -84,15 +83,13 @@ export class SolanaStandardWaitForTransactionTask extends BaseStepExecutionTask
84
83
  }
85
84
 
86
85
  if (result.signatureResult.err) {
87
- const reason =
88
- typeof result.signatureResult.err === 'object'
89
- ? JSON.stringify(result.signatureResult.err, (_, v) =>
90
- typeof v === 'bigint' ? v.toString() : v
91
- )
92
- : result.signatureResult.err
86
+ const cause = new SolanaTransactionDetailsError(
87
+ result.signatureResult.err
88
+ )
93
89
  throw new TransactionError(
94
90
  LiFiErrorCode.TransactionFailed,
95
- `Transaction failed: ${reason}`
91
+ `Transaction failed: ${cause.message}`,
92
+ cause
96
93
  )
97
94
  }
98
95
 
package/src/index.ts CHANGED
@@ -7,3 +7,4 @@ export type {
7
7
  } from './types.js'
8
8
  export { isSolanaProvider } from './types.js'
9
9
  export { KeypairWalletAdapter } from './utils/KeypairWalletAdapter.js'
10
+ export { SolanaTransactionDetailsError } from './utils/solanaErrorCause.js'
@@ -23,26 +23,28 @@ export const isJitoRpc = async (rpcUrl: string): Promise<boolean> => {
23
23
  * Initializes Solana RPCs for all available RPC URLs if they haven't been cached yet.
24
24
  * @param client - The SDK client used to fetch RPC URLs.
25
25
  */
26
- const ensureSolanaRpcs = async (client: SDKClient): Promise<void> => {
26
+ const ensureSolanaRpcs = async (client: SDKClient): Promise<string[]> => {
27
27
  const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)
28
28
  for (const rpcUrl of rpcUrls) {
29
29
  if (!solanaRpcs.has(rpcUrl)) {
30
30
  solanaRpcs.set(rpcUrl, createSolanaRpc(rpcUrl))
31
31
  }
32
32
  }
33
+ return rpcUrls
33
34
  }
34
35
 
35
36
  /**
36
37
  * Detects and caches Jito-capable RPCs by checking if they support the getTipAccounts method.
37
38
  * @param client - The SDK client used to fetch RPC URLs.
38
39
  */
39
- const ensureJitoRpcs = async (client: SDKClient): Promise<void> => {
40
+ const ensureJitoRpcs = async (client: SDKClient): Promise<string[]> => {
40
41
  const rpcUrls = await client.getRpcUrlsByChainId(ChainId.SOL)
41
42
  for (const rpcUrl of rpcUrls) {
42
43
  if (!jitoRpcs.has(rpcUrl) && (await isJitoRpc(rpcUrl))) {
43
44
  jitoRpcs.set(rpcUrl, createJitoRpc(rpcUrl))
44
45
  }
45
46
  }
47
+ return rpcUrls
46
48
  }
47
49
 
48
50
  /**
@@ -52,8 +54,10 @@ const ensureJitoRpcs = async (client: SDKClient): Promise<void> => {
52
54
  export const getSolanaRpcs = async (
53
55
  client: SDKClient
54
56
  ): Promise<SolanaRpcType[]> => {
55
- await ensureSolanaRpcs(client)
56
- return Array.from(solanaRpcs.values())
57
+ const rpcUrls = await ensureSolanaRpcs(client)
58
+ return rpcUrls
59
+ .map((rpcUrl) => solanaRpcs.get(rpcUrl))
60
+ .filter((rpc): rpc is SolanaRpcType => Boolean(rpc))
57
61
  }
58
62
 
59
63
  /**
@@ -63,6 +67,8 @@ export const getSolanaRpcs = async (
63
67
  export const getJitoRpcs = async (
64
68
  client: SDKClient
65
69
  ): Promise<JitoRpcType[]> => {
66
- await ensureJitoRpcs(client)
67
- return Array.from(jitoRpcs.values())
70
+ const rpcUrls = await ensureJitoRpcs(client)
71
+ return rpcUrls
72
+ .map((rpcUrl) => jitoRpcs.get(rpcUrl))
73
+ .filter((rpc): rpc is JitoRpcType => Boolean(rpc))
68
74
  }
@@ -0,0 +1,24 @@
1
+ export const safeStringifyBigInt = (value: unknown): string =>
2
+ JSON.stringify(value, (_, v) => (typeof v === 'bigint' ? v.toString() : v))
3
+
4
+ const formatSolanaErr = (err: unknown): string =>
5
+ typeof err === 'object' && err !== null
6
+ ? safeStringifyBigInt(err)
7
+ : String(err)
8
+
9
+ /**
10
+ * Carries the structured payload of a failed Solana RPC simulation or
11
+ * confirmation so consumers can inspect `err` and `logs` directly from
12
+ * the thrown `TransactionError`'s `cause`, without re-simulating.
13
+ */
14
+ export class SolanaTransactionDetailsError extends Error {
15
+ readonly err: unknown
16
+ readonly logs: readonly string[] | null
17
+
18
+ constructor(err: unknown, logs: readonly string[] | null = null) {
19
+ super(formatSolanaErr(err))
20
+ this.name = 'SolanaTransactionDetailsError'
21
+ this.err = err
22
+ this.logs = logs
23
+ }
24
+ }
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/sdk-provider-solana'
2
- export const version = '4.0.0-beta.11'
2
+ export const version = '4.0.0-beta.12'