@ape.swap/bonds-sdk 7.0.2 → 7.0.3

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 (26) hide show
  1. package/dist/adapters/solana/index.js +1 -1
  2. package/dist/adapters/solana/index.js.map +1 -1
  3. package/dist/adapters/solana/state/fetchUsersBondsSOL.d.ts +5 -1
  4. package/dist/adapters/solana/state/fetchUsersBondsSOL.js +152 -154
  5. package/dist/adapters/solana/state/fetchUsersBondsSOL.js.map +1 -1
  6. package/dist/adapters/solana/state/fetchUsersBondsSOLMulticall.d.ts +12 -0
  7. package/dist/adapters/solana/state/fetchUsersBondsSOLMulticall.js +162 -0
  8. package/dist/adapters/solana/state/fetchUsersBondsSOLMulticall.js.map +1 -0
  9. package/dist/adapters/solana/utils/calculatePendingRewards.d.ts +2 -0
  10. package/dist/adapters/solana/utils/calculatePendingRewards.js +53 -0
  11. package/dist/adapters/solana/utils/calculatePendingRewards.js.map +1 -0
  12. package/dist/adapters/solana/utils/fetchUserAssetsSOL.d.ts +3 -0
  13. package/dist/adapters/solana/utils/fetchUserAssetsSOL.js +49 -0
  14. package/dist/adapters/solana/utils/fetchUserAssetsSOL.js.map +1 -0
  15. package/dist/adapters/solana/utils/getSolanaAccount.d.ts +5 -0
  16. package/dist/adapters/solana/utils/getSolanaAccount.js +26 -23
  17. package/dist/adapters/solana/utils/getSolanaAccount.js.map +1 -1
  18. package/dist/adapters/solana/utils/solanaMulticall.js +4 -2
  19. package/dist/adapters/solana/utils/solanaMulticall.js.map +1 -1
  20. package/dist/adapters/types.d.ts +1 -1
  21. package/dist/config/constants/networks.d.ts +1 -1
  22. package/dist/config/constants/networks.js +9 -7
  23. package/dist/config/constants/networks.js.map +1 -1
  24. package/dist/state/bonds/useUserBonds.js +1 -1
  25. package/dist/state/bonds/useUserBonds.js.map +1 -1
  26. package/package.json +1 -1
@@ -24,7 +24,7 @@ const solanaAdapter = {
24
24
  // api-v2 NFT metadata like EVM ones.
25
25
  usesRealtimeApi: true,
26
26
  fetchBondsData: (_chainId, bonds, tokenPrices, apiUrl) => fetchBondsDataSOL(bonds, apiUrl, tokenPrices),
27
- fetchUserBonds: (_chainId, account, bonds, tokenPrices) => fetchUsersBondsSOL(account, bonds, tokenPrices),
27
+ fetchUserBonds: (_chainId, account, bonds, tokenPrices, apiUrl) => fetchUsersBondsSOL(account, bonds, tokenPrices, apiUrl),
28
28
  enrichUserBondsViaApi: true,
29
29
  getTokenBalance: (_chainId, currency, account) => getTokenBalanceSolana(currency, account),
30
30
  getTokensBalances: (_chainId, account, tokens) => getTokensBalanceSolana(account, tokens),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/adapters/solana/index.ts"],"sourcesContent":["// Must stay the first import: sets globalThis.Buffer before any Solana library module is evaluated.\nimport './utils/polyfills'\nimport { ChainId } from '@ape.swap/apeswap-lists'\nimport type { ChainAdapter } from '../types'\nimport SolanaAccountBridge from './state/SolanaAccountBridge'\nimport { fetchBondsDataSOL } from './state/fetchBondsDataSOL'\nimport { fetchUsersBondsSOL } from './state/fetchUsersBondsSOL'\nimport { getTokenBalanceSolana, getTokensBalanceSolana } from './state/useCurrencyBalanceSolana'\nimport BuyComponentSolana from './views/BuyComponentSolana'\nimport UserBondRowSolana from './views/UserBondRowSolana'\nimport ActionsSolana from './views/ActionsSolana'\nimport TransferActionSolana from './views/TransferActionSolana'\nimport ConnectToSolanaBanner from './views/ConnectToSolanaBanner'\n\n/**\n * Solana bonds. Requires `@solana/web3.js`, `@solana/kit`, `@solana/spl-token` and `@solana/wallet-adapter-react`,\n * and the consumer must mount Solana's `ConnectionProvider` + `WalletProvider` (see docs/PLAN_CHAIN_ADAPTERS.md,\n * section 3.2). Sets the `Buffer` global the Solana libraries need (./polyfills).\n */\nexport const solanaAdapter: ChainAdapter = {\n ecosystem: 'sol',\n supportsChain: (chainId) => chainId === ChainId.SOL,\n AccountBridge: SolanaAccountBridge,\n\n // Solana bonds come from the realtime API; fetchBondsData is only the on-chain fallback. Bills are enriched with\n // api-v2 NFT metadata like EVM ones.\n usesRealtimeApi: true,\n fetchBondsData: (_chainId, bonds, tokenPrices, apiUrl) => fetchBondsDataSOL(bonds, apiUrl, tokenPrices),\n fetchUserBonds: (_chainId, account, bonds, tokenPrices) => fetchUsersBondsSOL(account, bonds, tokenPrices),\n enrichUserBondsViaApi: true,\n getTokenBalance: (_chainId, currency, account) => getTokenBalanceSolana(currency, account),\n getTokensBalances: (_chainId, account, tokens) => getTokensBalanceSolana(account, tokens),\n\n BuyComponent: BuyComponentSolana,\n UserBondRow: UserBondRowSolana,\n BondActions: ActionsSolana,\n TransferAction: TransferActionSolana,\n // No ClaimAllAction: Claim All didn't support Solana before the adapter split either.\n ConnectBanner: ConnectToSolanaBanner,\n}\n"],"names":["TransferActionSolana","ConnectToSolanaBanner"],"mappings":";;;;;;;;;;;;AAAA;AAcA;;;;AAIG;AACI,MAAM,aAAa,GAAiB;AACzC,IAAA,SAAS,EAAE,KAAK;IAChB,aAAa,EAAE,CAAC,OAAO,KAAK,OAAO,KAAK,OAAO,CAAC,GAAG;AACnD,IAAA,aAAa,EAAE,mBAAmB;;;AAIlC,IAAA,eAAe,EAAE,IAAI;AACrB,IAAA,cAAc,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC;AACvG,IAAA,cAAc,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,KAAK,kBAAkB,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC;AAC1G,IAAA,qBAAqB,EAAE,IAAI;AAC3B,IAAA,eAAe,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,KAAK,qBAAqB,CAAC,QAAQ,EAAE,OAAO,CAAC;AAC1F,IAAA,iBAAiB,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,KAAK,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC;AAEzF,IAAA,YAAY,EAAE,kBAAkB;AAChC,IAAA,WAAW,EAAE,iBAAiB;AAC9B,IAAA,WAAW,EAAE,aAAa;AAC1B,IAAA,cAAc,EAAEA,cAAoB;;AAEpC,IAAA,aAAa,EAAEC,mBAAqB;;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/adapters/solana/index.ts"],"sourcesContent":["// Must stay the first import: sets globalThis.Buffer before any Solana library module is evaluated.\nimport './utils/polyfills'\nimport { ChainId } from '@ape.swap/apeswap-lists'\nimport type { ChainAdapter } from '../types'\nimport SolanaAccountBridge from './state/SolanaAccountBridge'\nimport { fetchBondsDataSOL } from './state/fetchBondsDataSOL'\nimport { fetchUsersBondsSOL } from './state/fetchUsersBondsSOL'\nimport { getTokenBalanceSolana, getTokensBalanceSolana } from './state/useCurrencyBalanceSolana'\nimport BuyComponentSolana from './views/BuyComponentSolana'\nimport UserBondRowSolana from './views/UserBondRowSolana'\nimport ActionsSolana from './views/ActionsSolana'\nimport TransferActionSolana from './views/TransferActionSolana'\nimport ConnectToSolanaBanner from './views/ConnectToSolanaBanner'\n\n/**\n * Solana bonds. Requires `@solana/web3.js`, `@solana/kit`, `@solana/spl-token` and `@solana/wallet-adapter-react`,\n * and the consumer must mount Solana's `ConnectionProvider` + `WalletProvider` (see docs/PLAN_CHAIN_ADAPTERS.md,\n * section 3.2). Sets the `Buffer` global the Solana libraries need (./polyfills).\n */\nexport const solanaAdapter: ChainAdapter = {\n ecosystem: 'sol',\n supportsChain: (chainId) => chainId === ChainId.SOL,\n AccountBridge: SolanaAccountBridge,\n\n // Solana bonds come from the realtime API; fetchBondsData is only the on-chain fallback. Bills are enriched with\n // api-v2 NFT metadata like EVM ones.\n usesRealtimeApi: true,\n fetchBondsData: (_chainId, bonds, tokenPrices, apiUrl) => fetchBondsDataSOL(bonds, apiUrl, tokenPrices),\n fetchUserBonds: (_chainId, account, bonds, tokenPrices, apiUrl) =>\n fetchUsersBondsSOL(account, bonds, tokenPrices, apiUrl),\n enrichUserBondsViaApi: true,\n getTokenBalance: (_chainId, currency, account) => getTokenBalanceSolana(currency, account),\n getTokensBalances: (_chainId, account, tokens) => getTokensBalanceSolana(account, tokens),\n\n BuyComponent: BuyComponentSolana,\n UserBondRow: UserBondRowSolana,\n BondActions: ActionsSolana,\n TransferAction: TransferActionSolana,\n // No ClaimAllAction: Claim All didn't support Solana before the adapter split either.\n ConnectBanner: ConnectToSolanaBanner,\n}\n"],"names":["TransferActionSolana","ConnectToSolanaBanner"],"mappings":";;;;;;;;;;;;AAAA;AAcA;;;;AAIG;AACI,MAAM,aAAa,GAAiB;AACzC,IAAA,SAAS,EAAE,KAAK;IAChB,aAAa,EAAE,CAAC,OAAO,KAAK,OAAO,KAAK,OAAO,CAAC,GAAG;AACnD,IAAA,aAAa,EAAE,mBAAmB;;;AAIlC,IAAA,eAAe,EAAE,IAAI;AACrB,IAAA,cAAc,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC;IACvG,cAAc,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAC5D,kBAAkB,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC;AACzD,IAAA,qBAAqB,EAAE,IAAI;AAC3B,IAAA,eAAe,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,KAAK,qBAAqB,CAAC,QAAQ,EAAE,OAAO,CAAC;AAC1F,IAAA,iBAAiB,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,KAAK,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC;AAEzF,IAAA,YAAY,EAAE,kBAAkB;AAChC,IAAA,WAAW,EAAE,iBAAiB;AAC9B,IAAA,WAAW,EAAE,aAAa;AAC1B,IAAA,cAAc,EAAEA,cAAoB;;AAEpC,IAAA,aAAa,EAAEC,mBAAqB;;;;;"}
@@ -1,4 +1,8 @@
1
1
  import { BillsConfig, ChainId } from '@ape.swap/apeswap-lists';
2
2
  import { UserOwnedBillsData } from '../../../types/yourbonds';
3
3
  import { TokenPrices } from '../../../state/tokenPrices/useTokenPrices';
4
- export declare function fetchUsersBondsSOL(user: string, bondData: BillsConfig[], tokenPrices: Partial<Record<ChainId, TokenPrices[]>>): Promise<UserOwnedBillsData[]>;
4
+ /**
5
+ * Fetches the user's Solana bonds with the multicall strategy (batched account reads and simulations). If it fails
6
+ * the error is reported to the API and no bonds are returned.
7
+ */
8
+ export declare function fetchUsersBondsSOL(user: string, bondData: BillsConfig[], tokenPrices: Partial<Record<ChainId, TokenPrices[]>>, apiUrl: string): Promise<UserOwnedBillsData[]>;
@@ -1,165 +1,163 @@
1
- import { ChainId } from '@ape.swap/apeswap-lists';
2
- import { PublicKey, Connection, TransactionInstruction, Transaction } from '@solana/web3.js';
3
- import { getBondAccountSolana } from '../utils/getSolanaAccount.js';
4
- import { getU64Codec } from '@solana/kit';
5
- import { PROGRAM_ID, CLAIMABLE_PAYOUT_DISCRIMINATOR } from '../constants/solanaConstants.js';
6
- import { Buffer } from 'buffer';
7
- import { getRPC, HELIUS_SOL_RPC } from '../../../config/constants/networks.js';
8
- import { apiGet } from '../../../utils/httpClient.js';
9
- import { PRICE_API_URL } from '../../../config/constants/variables.js';
10
-
11
- async function fetchUsersBondsSOL(user, bondData, tokenPrices) {
12
- if (!user)
13
- return [];
1
+ import { reportError } from '../../../utils/reportError.js';
2
+ import { fetchUsersBondsSOLMulticall } from './fetchUsersBondsSOLMulticall.js';
3
+
4
+ /**
5
+ * Fetches the user's Solana bonds with the multicall strategy (batched account reads and simulations). If it fails
6
+ * the error is reported to the API and no bonds are returned.
7
+ */
8
+ async function fetchUsersBondsSOL(user, bondData, tokenPrices, apiUrl) {
14
9
  try {
15
- console.time('fetchUserBondsSOL');
16
- const results = [];
17
- const userBase58 = new PublicKey(user).toBase58();
18
- const connection = new Connection(getRPC(ChainId.SOL), 'confirmed');
19
- const collections = bondData.map((bond) => bond.billNnftAddress[ChainId.SOL]);
20
- const allOptions = {
21
- method: 'POST',
22
- headers: { 'Content-Type': 'application/json' },
23
- body: JSON.stringify({
24
- jsonrpc: '2.0',
25
- id: '1',
26
- method: 'searchAssets',
27
- params: {
28
- ownerAddress: userBase58,
29
- collections,
30
- },
31
- }),
32
- };
33
- const allResponse = await fetch(HELIUS_SOL_RPC, allOptions);
34
- const allData = await allResponse.json();
35
- const allUserAssets = allData.result.items;
36
- for (const bond of bondData) {
37
- if (bond.chainId !== ChainId.SOL || !bond.contractAddress[ChainId.SOL])
38
- continue;
39
- const bondIssuancePda = new PublicKey(bond.contractAddress[ChainId.SOL]);
40
- const userBills = [];
41
- const collection = bond.billNnftAddress[ChainId.SOL];
42
- let principalTokenPrice = tokenPrices?.[ChainId.SOL]?.find?.((t) => t?.tokenAddress?.toLowerCase() === bond?.lpToken?.address?.[ChainId.SOL]?.toLowerCase())?.price;
43
- let payoutTokenPrice = tokenPrices?.[ChainId.SOL]?.find?.((t) => t?.tokenAddress?.toLowerCase() === bond?.earnToken?.address?.[ChainId.SOL]?.toLowerCase())?.price;
44
- if (principalTokenPrice === undefined) {
45
- const res = await apiGet(`${PRICE_API_URL}/realtime/price?token=${bond?.lpToken?.address?.[bond?.chainId]}&chain=${bond?.chainId}`);
46
- console.log('Individual call for: ', bond.lpToken.symbol, '. Price: ', res.price);
47
- principalTokenPrice = parseFloat(res.price ?? '0');
48
- }
49
- if (payoutTokenPrice === undefined) {
50
- const res = await apiGet(`${PRICE_API_URL}/realtime/price?token=${bond?.earnToken?.address?.[bond?.chainId]}&chain=${bond?.chainId}`);
51
- console.log('Individual call for: ', bond.earnToken.symbol, '. Price: ', res.price);
52
- payoutTokenPrice = parseFloat(res.price ?? '0');
53
- }
54
- const userAssets = allUserAssets.filter((asset) => {
55
- return asset.authorities[0].address.toLowerCase() === bond.contractAddress[ChainId.SOL]?.toLowerCase();
56
- });
57
- for (const asset of userAssets) {
58
- const nftMint = new PublicKey(asset.id);
59
- const [bondPda] = PublicKey.findProgramAddressSync([new TextEncoder().encode('bond'), bondIssuancePda.toBuffer(), nftMint.toBuffer()], PROGRAM_ID);
60
- if (!bondPda) {
61
- console.error(`Could not fetch bond PDA for mint ${nftMint.toBase58()}`);
62
- continue;
63
- }
64
- try {
65
- const [bondTermPda] = PublicKey.findProgramAddressSync([new TextEncoder().encode('bond_term'), bondIssuancePda.toBuffer()], PROGRAM_ID);
66
- const [bondAccounts, pendingRewards] = await Promise.all([
67
- getBondAccountSolana([bondPda.toBase58()]),
68
- calculatePendingRewards(connection, bondPda, bondTermPda, bondIssuancePda, nftMint),
69
- ]);
70
- if (!bondAccounts?.length) {
71
- console.error(`Could not fetch bond account for mint ${nftMint.toBase58()}`);
72
- continue;
73
- }
74
- const bondAccount = bondAccounts[0];
75
- const id = asset.content.metadata.name;
76
- const idTrimmed = id.split('-')[1]?.trim();
77
- userBills.push({
78
- address: bondPda.toBase58(),
79
- id: idTrimmed,
80
- asset: nftMint.toBase58(),
81
- // vesting: bondAccount.vesting.toString(),
82
- vestingTerm: bondAccount.vestingTerm.toString(),
83
- payout: (bondAccount.payout - bondAccount.payoutClaimed).toString(),
84
- totalPayout: bondAccount.payout.toString(),
85
- truePricePaid: bondAccount.truePricePaid.toString(),
86
- lastBlockTimestamp: bondAccount.lastClaimTimestamp.toString(),
87
- vestingStartTimestamp: bondAccount.vestingStartTimestamp.toString(),
88
- pendingRewards,
89
- billNftAddress: collection,
90
- payoutClaimed: bondAccount.payoutClaimed.toString(),
91
- bond: {
92
- ...bond,
93
- payoutTokenPrice: payoutTokenPrice?.toString(),
94
- principalTokenPrice: principalTokenPrice?.toString(),
95
- },
96
- });
97
- // ULTRA HOT FIX :( until we can figure out a multicall strategy
98
- await new Promise((r) => setTimeout(r, 10));
99
- }
100
- catch (e) {
101
- console.log(e);
102
- console.error(`Could not fetch bond PDA for mint ${nftMint.toBase58()}`);
103
- }
104
- }
105
- results.push({
106
- index: bond.index,
107
- userOwnedBills: userBills,
108
- });
109
- }
110
- console.timeEnd('fetchUserBondsSOL');
111
- return results;
10
+ return await fetchUsersBondsSOLMulticall(user, bondData, tokenPrices);
112
11
  }
113
- catch (err) {
114
- console.error('Error fetching assets by collection:', err);
12
+ catch (error) {
13
+ reportError({
14
+ apiUrl,
15
+ error,
16
+ account: user,
17
+ extraInfo: { type: 'user bonds fetching failed solana' },
18
+ });
19
+ // TODO: a failure is returned as "no bonds", so the user can't tell it apart from an empty account (and Query
20
+ // caches it as a success). Surface failed chains to the user instead, and let Query retry.
115
21
  return [];
116
22
  }
117
23
  }
118
- async function calculatePendingRewards(connection, bondPda, bondTermPda, bondIssuancePda, nftMint) {
119
- try {
120
- const u64Codec = getU64Codec();
121
- const currentTimestamp = Math.floor(Date.now() / 1000);
122
- const encodedTimestamp = u64Codec.encode(BigInt(currentTimestamp));
123
- // Instruction data
124
- const instructionData = Buffer.concat([CLAIMABLE_PAYOUT_DISCRIMINATOR, Buffer.from(encodedTimestamp)]);
125
- // Accounts
126
- const keys = [
127
- { pubkey: bondPda, isSigner: false, isWritable: true },
128
- { pubkey: bondTermPda, isSigner: false, isWritable: false },
129
- { pubkey: bondIssuancePda, isSigner: false, isWritable: false },
130
- { pubkey: nftMint, isSigner: false, isWritable: false },
131
- ];
132
- // Instruction
133
- const instruction = new TransactionInstruction({
134
- keys,
135
- programId: PROGRAM_ID,
136
- data: instructionData,
137
- });
138
- // Transaction
139
- const transaction = new Transaction();
140
- const userPublicKey = new PublicKey('11111111111111111111111111111112'); // Dummy public key for fee payer
141
- transaction.feePayer = userPublicKey;
142
- transaction.add(instruction);
143
- // Simulate
144
- const simulation = await connection.simulateTransaction(transaction);
145
- // Extract return value
146
- const returnData = simulation.value.returnData;
147
- if (returnData) {
148
- const [data, encoding] = returnData.data;
149
- if (encoding !== 'base64') {
150
- throw new Error(`Unexpected return data encoding: ${encoding}`);
151
- }
152
- const decodedData = Buffer.from(data, 'base64');
153
- return decodedData.readBigUInt64LE(0).toString();
24
+ // Previous implementation, kept for reference only: one RPC round trip per owned NFT (~10x slower). It must never be
25
+ // used as a fallback. Restoring it needs its imports back:
26
+ // Connection/PublicKey (@solana/web3.js), HeliusAsset, UserBill, getBondAccountSolana, PROGRAM_ID,
27
+ // calculatePendingRewards, fetchUserAssetsSOL, getRPC, apiGet and PRICE_API_URL.
28
+ /*
29
+ import { Connection, PublicKey } from '@solana/web3.js'
30
+ import { UserBill } from '../../../types/yourbonds'
31
+ import { HeliusAsset } from '../types/helius'
32
+ import { getBondAccountSolana } from '../utils/getSolanaAccount'
33
+ import { PROGRAM_ID } from '../constants/solanaConstants'
34
+ import { calculatePendingRewards } from '../utils/calculatePendingRewards'
35
+ import { fetchUserAssetsSOL } from '../utils/fetchUserAssetsSOL'
36
+ import { getRPC } from '../../../config/constants/networks'
37
+ import { apiGet } from '../../../utils/httpClient'
38
+ import { PRICE_API_URL } from '../../../config/constants/variables'
39
+
40
+ export async function fetchUsersBondsSOLSequential(
41
+ user: string,
42
+ bondData: BillsConfig[],
43
+ tokenPrices: Partial<Record<ChainId, TokenPrices[]>>,
44
+ prefetchedAssets?: HeliusAsset[],
45
+ ): Promise<UserOwnedBillsData[]> {
46
+ if (!user) return []
47
+ try {
48
+ console.time('fetchUserBondsSOL')
49
+ const results: UserOwnedBillsData[] = []
50
+
51
+ const connection = new Connection(getRPC(ChainId.SOL), 'confirmed')
52
+
53
+ const collections: string[] = bondData.map((bond) => bond.billNnftAddress[ChainId.SOL]!)
54
+
55
+ const allUserAssets: HeliusAsset[] = prefetchedAssets ?? (await fetchUserAssetsSOL(user, collections))
56
+
57
+ for (const bond of bondData) {
58
+ if (bond.chainId !== ChainId.SOL || !bond.contractAddress[ChainId.SOL]) continue
59
+ const bondIssuancePda = new PublicKey(bond.contractAddress[ChainId.SOL])
60
+ const userBills: UserBill[] = []
61
+ const collection = bond.billNnftAddress[ChainId.SOL]
62
+
63
+ let principalTokenPrice = tokenPrices?.[ChainId.SOL]?.find?.(
64
+ (t) => t?.tokenAddress?.toLowerCase() === bond?.lpToken?.address?.[ChainId.SOL]?.toLowerCase(),
65
+ )?.price
66
+ let payoutTokenPrice = tokenPrices?.[ChainId.SOL]?.find?.(
67
+ (t) => t?.tokenAddress?.toLowerCase() === bond?.earnToken?.address?.[ChainId.SOL]?.toLowerCase(),
68
+ )?.price
69
+
70
+ if (principalTokenPrice === undefined) {
71
+ const res = await apiGet<{ price: string }>(
72
+ `${PRICE_API_URL}/realtime/price?token=${bond?.lpToken?.address?.[bond?.chainId]}&chain=${bond?.chainId}`,
73
+ )
74
+ console.log('Individual call for: ', bond.lpToken.symbol, '. Price: ', res.price)
75
+ principalTokenPrice = parseFloat(res.price ?? '0')
76
+ }
77
+ if (payoutTokenPrice === undefined) {
78
+ const res = await apiGet<{ price: string }>(
79
+ `${PRICE_API_URL}/realtime/price?token=${bond?.earnToken?.address?.[bond?.chainId]}&chain=${bond?.chainId}`,
80
+ )
81
+ console.log('Individual call for: ', bond.earnToken.symbol, '. Price: ', res.price)
82
+ payoutTokenPrice = parseFloat(res.price ?? '0')
83
+ }
84
+
85
+ const userAssets = allUserAssets.filter((asset) => {
86
+ return asset.authorities[0].address.toLowerCase() === bond.contractAddress[ChainId.SOL]?.toLowerCase()
87
+ })
88
+
89
+ for (const asset of userAssets) {
90
+ const nftMint = new PublicKey(asset.id)
91
+
92
+ const [bondPda] = PublicKey.findProgramAddressSync(
93
+ [new TextEncoder().encode('bond'), bondIssuancePda.toBuffer(), nftMint.toBuffer()],
94
+ PROGRAM_ID,
95
+ )
96
+
97
+ if (!bondPda) {
98
+ console.error(`Could not fetch bond PDA for mint ${nftMint.toBase58()}`)
99
+ continue
154
100
  }
155
- // Fallback
156
- return '0';
157
- }
158
- catch (error) {
159
- console.error('Error calculating pending rewards:', error);
160
- return '0';
101
+
102
+ try {
103
+ const [bondTermPda] = PublicKey.findProgramAddressSync(
104
+ [new TextEncoder().encode('bond_term'), bondIssuancePda.toBuffer()],
105
+ PROGRAM_ID,
106
+ )
107
+
108
+ const [bondAccounts, pendingRewards] = await Promise.all([
109
+ getBondAccountSolana([bondPda.toBase58()]),
110
+ calculatePendingRewards(connection, bondPda, bondTermPda, bondIssuancePda, nftMint),
111
+ ])
112
+
113
+ if (!bondAccounts?.length) {
114
+ console.error(`Could not fetch bond account for mint ${nftMint.toBase58()}`)
115
+ continue
116
+ }
117
+
118
+ const bondAccount = bondAccounts[0]
119
+ const id = asset.content.metadata.name
120
+ const idTrimmed: string = id.split('-')[1]?.trim()
121
+ userBills.push({
122
+ address: bondPda.toBase58(),
123
+ id: idTrimmed,
124
+ asset: nftMint.toBase58(),
125
+ // vesting: bondAccount.vesting.toString(),
126
+ vestingTerm: bondAccount.vestingTerm.toString(),
127
+ payout: (bondAccount.payout - bondAccount.payoutClaimed).toString(),
128
+ totalPayout: bondAccount.payout.toString(),
129
+ truePricePaid: bondAccount.truePricePaid.toString(),
130
+ lastBlockTimestamp: bondAccount.lastClaimTimestamp.toString(),
131
+ vestingStartTimestamp: bondAccount.vestingStartTimestamp.toString(),
132
+ pendingRewards,
133
+ billNftAddress: collection as string,
134
+ payoutClaimed: bondAccount.payoutClaimed.toString(),
135
+ bond: {
136
+ ...bond,
137
+ payoutTokenPrice: payoutTokenPrice?.toString(),
138
+ principalTokenPrice: principalTokenPrice?.toString(),
139
+ },
140
+ })
141
+ // ULTRA HOT FIX :( until we can figure out a multicall strategy
142
+ await new Promise((r) => setTimeout(r, 10))
143
+ } catch (e) {
144
+ console.log(e)
145
+ console.error(`Could not fetch bond PDA for mint ${nftMint.toBase58()}`)
146
+ }
147
+ }
148
+ results.push({
149
+ index: bond.index,
150
+ userOwnedBills: userBills,
151
+ })
161
152
  }
153
+ console.timeEnd('fetchUserBondsSOL')
154
+ return results
155
+ } catch (err) {
156
+ console.error('Error fetching assets by collection:', err)
157
+ return []
158
+ }
162
159
  }
160
+ */
163
161
 
164
162
  export { fetchUsersBondsSOL };
165
163
  //# sourceMappingURL=fetchUsersBondsSOL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetchUsersBondsSOL.js","sources":["../../../../src/adapters/solana/state/fetchUsersBondsSOL.ts"],"sourcesContent":["import { BillsConfig, ChainId } from '@ape.swap/apeswap-lists'\nimport { UserBill, UserOwnedBillsData } from '../../../types/yourbonds'\nimport { Connection, PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js'\nimport { TokenPrices } from '../../../state/tokenPrices/useTokenPrices'\nimport { HeliusApiResponse, HeliusAsset } from '../types/helius'\nimport { getBondAccountSolana } from '../utils/getSolanaAccount'\nimport { getU64Codec } from '@solana/kit'\nimport { CLAIMABLE_PAYOUT_DISCRIMINATOR, PROGRAM_ID } from '../constants/solanaConstants'\nimport { Buffer } from 'buffer'\nimport { getRPC, HELIUS_SOL_RPC } from '../../../config/constants/networks'\nimport { apiGet } from '../../../utils/httpClient'\nimport { PRICE_API_URL } from '../../../config/constants/variables'\n\nexport async function fetchUsersBondsSOL(\n user: string,\n bondData: BillsConfig[],\n tokenPrices: Partial<Record<ChainId, TokenPrices[]>>,\n): Promise<UserOwnedBillsData[]> {\n if (!user) return []\n try {\n console.time('fetchUserBondsSOL')\n const results: UserOwnedBillsData[] = []\n\n const userBase58 = new PublicKey(user).toBase58()\n\n const connection = new Connection(getRPC(ChainId.SOL), 'confirmed')\n\n const collections: string[] = bondData.map((bond) => bond.billNnftAddress[ChainId.SOL]!)\n\n const allOptions = {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: '1',\n method: 'searchAssets',\n params: {\n ownerAddress: userBase58,\n collections,\n },\n }),\n }\n const allResponse = await fetch(HELIUS_SOL_RPC, allOptions)\n const allData: HeliusApiResponse = await allResponse.json()\n const allUserAssets: HeliusAsset[] = allData.result.items\n\n for (const bond of bondData) {\n if (bond.chainId !== ChainId.SOL || !bond.contractAddress[ChainId.SOL]) continue\n const bondIssuancePda = new PublicKey(bond.contractAddress[ChainId.SOL])\n const userBills: UserBill[] = []\n const collection = bond.billNnftAddress[ChainId.SOL]\n\n let principalTokenPrice = tokenPrices?.[ChainId.SOL]?.find?.(\n (t) => t?.tokenAddress?.toLowerCase() === bond?.lpToken?.address?.[ChainId.SOL]?.toLowerCase(),\n )?.price\n let payoutTokenPrice = tokenPrices?.[ChainId.SOL]?.find?.(\n (t) => t?.tokenAddress?.toLowerCase() === bond?.earnToken?.address?.[ChainId.SOL]?.toLowerCase(),\n )?.price\n\n if (principalTokenPrice === undefined) {\n const res = await apiGet<{ price: string }>(\n `${PRICE_API_URL}/realtime/price?token=${bond?.lpToken?.address?.[bond?.chainId]}&chain=${bond?.chainId}`,\n )\n console.log('Individual call for: ', bond.lpToken.symbol, '. Price: ', res.price)\n principalTokenPrice = parseFloat(res.price ?? '0')\n }\n if (payoutTokenPrice === undefined) {\n const res = await apiGet<{ price: string }>(\n `${PRICE_API_URL}/realtime/price?token=${bond?.earnToken?.address?.[bond?.chainId]}&chain=${bond?.chainId}`,\n )\n console.log('Individual call for: ', bond.earnToken.symbol, '. Price: ', res.price)\n payoutTokenPrice = parseFloat(res.price ?? '0')\n }\n\n const userAssets = allUserAssets.filter((asset) => {\n return asset.authorities[0].address.toLowerCase() === bond.contractAddress[ChainId.SOL]?.toLowerCase()\n })\n\n for (const asset of userAssets) {\n const nftMint = new PublicKey(asset.id)\n\n const [bondPda] = PublicKey.findProgramAddressSync(\n [new TextEncoder().encode('bond'), bondIssuancePda.toBuffer(), nftMint.toBuffer()],\n PROGRAM_ID,\n )\n\n if (!bondPda) {\n console.error(`Could not fetch bond PDA for mint ${nftMint.toBase58()}`)\n continue\n }\n\n try {\n const [bondTermPda] = PublicKey.findProgramAddressSync(\n [new TextEncoder().encode('bond_term'), bondIssuancePda.toBuffer()],\n PROGRAM_ID,\n )\n\n const [bondAccounts, pendingRewards] = await Promise.all([\n getBondAccountSolana([bondPda.toBase58()]),\n calculatePendingRewards(connection, bondPda, bondTermPda, bondIssuancePda, nftMint),\n ])\n\n if (!bondAccounts?.length) {\n console.error(`Could not fetch bond account for mint ${nftMint.toBase58()}`)\n continue\n }\n\n const bondAccount = bondAccounts[0]\n const id = asset.content.metadata.name\n const idTrimmed: string = id.split('-')[1]?.trim()\n userBills.push({\n address: bondPda.toBase58(),\n id: idTrimmed,\n asset: nftMint.toBase58(),\n // vesting: bondAccount.vesting.toString(),\n vestingTerm: bondAccount.vestingTerm.toString(),\n payout: (bondAccount.payout - bondAccount.payoutClaimed).toString(),\n totalPayout: bondAccount.payout.toString(),\n truePricePaid: bondAccount.truePricePaid.toString(),\n lastBlockTimestamp: bondAccount.lastClaimTimestamp.toString(),\n vestingStartTimestamp: bondAccount.vestingStartTimestamp.toString(),\n pendingRewards,\n billNftAddress: collection as string,\n payoutClaimed: bondAccount.payoutClaimed.toString(),\n bond: {\n ...bond,\n payoutTokenPrice: payoutTokenPrice?.toString(),\n principalTokenPrice: principalTokenPrice?.toString(),\n },\n })\n // ULTRA HOT FIX :( until we can figure out a multicall strategy\n await new Promise((r) => setTimeout(r, 10))\n } catch (e) {\n console.log(e)\n console.error(`Could not fetch bond PDA for mint ${nftMint.toBase58()}`)\n }\n }\n results.push({\n index: bond.index,\n userOwnedBills: userBills,\n })\n }\n console.timeEnd('fetchUserBondsSOL')\n return results\n } catch (err) {\n console.error('Error fetching assets by collection:', err)\n return []\n }\n}\n\nasync function calculatePendingRewards(\n connection: Connection,\n bondPda: PublicKey,\n bondTermPda: PublicKey,\n bondIssuancePda: PublicKey,\n nftMint: PublicKey,\n): Promise<string> {\n try {\n const u64Codec = getU64Codec()\n\n const currentTimestamp = Math.floor(Date.now() / 1000)\n\n const encodedTimestamp = u64Codec.encode(BigInt(currentTimestamp))\n\n // Instruction data\n const instructionData = Buffer.concat([CLAIMABLE_PAYOUT_DISCRIMINATOR, Buffer.from(encodedTimestamp)])\n\n // Accounts\n const keys = [\n { pubkey: bondPda, isSigner: false, isWritable: true },\n { pubkey: bondTermPda, isSigner: false, isWritable: false },\n { pubkey: bondIssuancePda, isSigner: false, isWritable: false },\n { pubkey: nftMint, isSigner: false, isWritable: false },\n ]\n\n // Instruction\n const instruction = new TransactionInstruction({\n keys,\n programId: PROGRAM_ID,\n data: instructionData,\n })\n\n // Transaction\n const transaction = new Transaction()\n const userPublicKey = new PublicKey('11111111111111111111111111111112') // Dummy public key for fee payer\n transaction.feePayer = userPublicKey\n transaction.add(instruction)\n\n // Simulate\n const simulation = await connection.simulateTransaction(transaction)\n\n // Extract return value\n const returnData = simulation.value.returnData\n if (returnData) {\n const [data, encoding] = returnData.data\n if (encoding !== 'base64') {\n throw new Error(`Unexpected return data encoding: ${encoding}`)\n }\n\n const decodedData = Buffer.from(data, 'base64')\n return decodedData.readBigUInt64LE(0).toString()\n }\n\n // Fallback\n return '0'\n } catch (error) {\n console.error('Error calculating pending rewards:', error)\n return '0'\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fetchUsersBondsSOL.js","sources":["../../../../src/adapters/solana/state/fetchUsersBondsSOL.ts"],"sourcesContent":["import { BillsConfig, ChainId } from '@ape.swap/apeswap-lists'\nimport { UserOwnedBillsData } from '../../../types/yourbonds'\nimport { TokenPrices } from '../../../state/tokenPrices/useTokenPrices'\nimport { reportError } from '../../../utils/reportError'\nimport { fetchUsersBondsSOLMulticall } from './fetchUsersBondsSOLMulticall'\n\n/**\n * Fetches the user's Solana bonds with the multicall strategy (batched account reads and simulations). If it fails\n * the error is reported to the API and no bonds are returned.\n */\nexport async function fetchUsersBondsSOL(\n user: string,\n bondData: BillsConfig[],\n tokenPrices: Partial<Record<ChainId, TokenPrices[]>>,\n apiUrl: string,\n): Promise<UserOwnedBillsData[]> {\n try {\n return await fetchUsersBondsSOLMulticall(user, bondData, tokenPrices)\n } catch (error) {\n reportError({\n apiUrl,\n error,\n account: user,\n extraInfo: { type: 'user bonds fetching failed solana' },\n })\n // TODO: a failure is returned as \"no bonds\", so the user can't tell it apart from an empty account (and Query\n // caches it as a success). Surface failed chains to the user instead, and let Query retry.\n return []\n }\n}\n\n// Previous implementation, kept for reference only: one RPC round trip per owned NFT (~10x slower). It must never be\n// used as a fallback. Restoring it needs its imports back:\n// Connection/PublicKey (@solana/web3.js), HeliusAsset, UserBill, getBondAccountSolana, PROGRAM_ID,\n// calculatePendingRewards, fetchUserAssetsSOL, getRPC, apiGet and PRICE_API_URL.\n/*\nimport { Connection, PublicKey } from '@solana/web3.js'\nimport { UserBill } from '../../../types/yourbonds'\nimport { HeliusAsset } from '../types/helius'\nimport { getBondAccountSolana } from '../utils/getSolanaAccount'\nimport { PROGRAM_ID } from '../constants/solanaConstants'\nimport { calculatePendingRewards } from '../utils/calculatePendingRewards'\nimport { fetchUserAssetsSOL } from '../utils/fetchUserAssetsSOL'\nimport { getRPC } from '../../../config/constants/networks'\nimport { apiGet } from '../../../utils/httpClient'\nimport { PRICE_API_URL } from '../../../config/constants/variables'\n\nexport async function fetchUsersBondsSOLSequential(\n user: string,\n bondData: BillsConfig[],\n tokenPrices: Partial<Record<ChainId, TokenPrices[]>>,\n prefetchedAssets?: HeliusAsset[],\n): Promise<UserOwnedBillsData[]> {\n if (!user) return []\n try {\n console.time('fetchUserBondsSOL')\n const results: UserOwnedBillsData[] = []\n\n const connection = new Connection(getRPC(ChainId.SOL), 'confirmed')\n\n const collections: string[] = bondData.map((bond) => bond.billNnftAddress[ChainId.SOL]!)\n\n const allUserAssets: HeliusAsset[] = prefetchedAssets ?? (await fetchUserAssetsSOL(user, collections))\n\n for (const bond of bondData) {\n if (bond.chainId !== ChainId.SOL || !bond.contractAddress[ChainId.SOL]) continue\n const bondIssuancePda = new PublicKey(bond.contractAddress[ChainId.SOL])\n const userBills: UserBill[] = []\n const collection = bond.billNnftAddress[ChainId.SOL]\n\n let principalTokenPrice = tokenPrices?.[ChainId.SOL]?.find?.(\n (t) => t?.tokenAddress?.toLowerCase() === bond?.lpToken?.address?.[ChainId.SOL]?.toLowerCase(),\n )?.price\n let payoutTokenPrice = tokenPrices?.[ChainId.SOL]?.find?.(\n (t) => t?.tokenAddress?.toLowerCase() === bond?.earnToken?.address?.[ChainId.SOL]?.toLowerCase(),\n )?.price\n\n if (principalTokenPrice === undefined) {\n const res = await apiGet<{ price: string }>(\n `${PRICE_API_URL}/realtime/price?token=${bond?.lpToken?.address?.[bond?.chainId]}&chain=${bond?.chainId}`,\n )\n console.log('Individual call for: ', bond.lpToken.symbol, '. Price: ', res.price)\n principalTokenPrice = parseFloat(res.price ?? '0')\n }\n if (payoutTokenPrice === undefined) {\n const res = await apiGet<{ price: string }>(\n `${PRICE_API_URL}/realtime/price?token=${bond?.earnToken?.address?.[bond?.chainId]}&chain=${bond?.chainId}`,\n )\n console.log('Individual call for: ', bond.earnToken.symbol, '. Price: ', res.price)\n payoutTokenPrice = parseFloat(res.price ?? '0')\n }\n\n const userAssets = allUserAssets.filter((asset) => {\n return asset.authorities[0].address.toLowerCase() === bond.contractAddress[ChainId.SOL]?.toLowerCase()\n })\n\n for (const asset of userAssets) {\n const nftMint = new PublicKey(asset.id)\n\n const [bondPda] = PublicKey.findProgramAddressSync(\n [new TextEncoder().encode('bond'), bondIssuancePda.toBuffer(), nftMint.toBuffer()],\n PROGRAM_ID,\n )\n\n if (!bondPda) {\n console.error(`Could not fetch bond PDA for mint ${nftMint.toBase58()}`)\n continue\n }\n\n try {\n const [bondTermPda] = PublicKey.findProgramAddressSync(\n [new TextEncoder().encode('bond_term'), bondIssuancePda.toBuffer()],\n PROGRAM_ID,\n )\n\n const [bondAccounts, pendingRewards] = await Promise.all([\n getBondAccountSolana([bondPda.toBase58()]),\n calculatePendingRewards(connection, bondPda, bondTermPda, bondIssuancePda, nftMint),\n ])\n\n if (!bondAccounts?.length) {\n console.error(`Could not fetch bond account for mint ${nftMint.toBase58()}`)\n continue\n }\n\n const bondAccount = bondAccounts[0]\n const id = asset.content.metadata.name\n const idTrimmed: string = id.split('-')[1]?.trim()\n userBills.push({\n address: bondPda.toBase58(),\n id: idTrimmed,\n asset: nftMint.toBase58(),\n // vesting: bondAccount.vesting.toString(),\n vestingTerm: bondAccount.vestingTerm.toString(),\n payout: (bondAccount.payout - bondAccount.payoutClaimed).toString(),\n totalPayout: bondAccount.payout.toString(),\n truePricePaid: bondAccount.truePricePaid.toString(),\n lastBlockTimestamp: bondAccount.lastClaimTimestamp.toString(),\n vestingStartTimestamp: bondAccount.vestingStartTimestamp.toString(),\n pendingRewards,\n billNftAddress: collection as string,\n payoutClaimed: bondAccount.payoutClaimed.toString(),\n bond: {\n ...bond,\n payoutTokenPrice: payoutTokenPrice?.toString(),\n principalTokenPrice: principalTokenPrice?.toString(),\n },\n })\n // ULTRA HOT FIX :( until we can figure out a multicall strategy\n await new Promise((r) => setTimeout(r, 10))\n } catch (e) {\n console.log(e)\n console.error(`Could not fetch bond PDA for mint ${nftMint.toBase58()}`)\n }\n }\n results.push({\n index: bond.index,\n userOwnedBills: userBills,\n })\n }\n console.timeEnd('fetchUserBondsSOL')\n return results\n } catch (err) {\n console.error('Error fetching assets by collection:', err)\n return []\n }\n}\n*/\n"],"names":[],"mappings":";;;AAMA;;;AAGG;AACI,eAAe,kBAAkB,CACtC,IAAY,EACZ,QAAuB,EACvB,WAAoD,EACpD,MAAc,EAAA;AAEd,IAAA,IAAI;QACF,OAAO,MAAM,2BAA2B,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC;IACvE;IAAE,OAAO,KAAK,EAAE;AACd,QAAA,WAAW,CAAC;YACV,MAAM;YACN,KAAK;AACL,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,SAAS,EAAE,EAAE,IAAI,EAAE,mCAAmC,EAAE;AACzD,SAAA,CAAC;;;AAGF,QAAA,OAAO,EAAE;IACX;AACF;AAEA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoIE;;;;"}
@@ -0,0 +1,12 @@
1
+ import { BillsConfig, ChainId } from '@ape.swap/apeswap-lists';
2
+ import { UserOwnedBillsData } from '../../../types/yourbonds';
3
+ import { TokenPrices } from '../../../state/tokenPrices/useTokenPrices';
4
+ import { HeliusAsset } from '../types/helius';
5
+ /**
6
+ * Multicall version of `fetchUsersBondsSOLSequential`. Throws on failure (the caller `fetchUsersBondsSOL` reports it). Same inputs and output, but:
7
+ * - all Bond accounts are read with one batched `getMultipleAccounts` (`getBondAccountsSolanaAligned`) instead of one per NFT
8
+ * - the pending-rewards simulations are packed several instructions per transaction (the program's return value of
9
+ * each instruction is read from the "Program return:" log lines) instead of one simulation per NFT
10
+ * - token prices are resolved in parallel, only for bonds the user owns
11
+ */
12
+ export declare function fetchUsersBondsSOLMulticall(user: string, bondData: BillsConfig[], tokenPrices: Partial<Record<ChainId, TokenPrices[]>>, prefetchedAssets?: HeliusAsset[]): Promise<UserOwnedBillsData[]>;
@@ -0,0 +1,162 @@
1
+ import { ChainId } from '@ape.swap/apeswap-lists';
2
+ import { Connection, PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js';
3
+ import { getU64Codec } from '@solana/kit';
4
+ import { PROGRAM_ID, CLAIMABLE_PAYOUT_DISCRIMINATOR } from '../constants/solanaConstants.js';
5
+ import { Buffer } from 'buffer';
6
+ import { getRPC } from '../../../config/constants/networks.js';
7
+ import { resolveBondTokenPrices } from '../../../state/tokenPrices/resolveTokenPrice.js';
8
+ import { fetchUserAssetsSOL } from '../utils/fetchUserAssetsSOL.js';
9
+ import { getBondAccountsSolanaAligned } from '../utils/getSolanaAccount.js';
10
+ import { calculatePendingRewards } from '../utils/calculatePendingRewards.js';
11
+
12
+ // A legacy transaction is capped at 1232 bytes; each claimable-payout instruction adds up to two unique accounts
13
+ // (bond PDA + NFT mint), so 6 per simulation leaves a comfortable margin.
14
+ const SIMULATIONS_PER_TX = 6;
15
+ const MAX_PARALLEL_SIMULATIONS = 3;
16
+ /**
17
+ * Multicall version of `fetchUsersBondsSOLSequential`. Throws on failure (the caller `fetchUsersBondsSOL` reports it). Same inputs and output, but:
18
+ * - all Bond accounts are read with one batched `getMultipleAccounts` (`getBondAccountsSolanaAligned`) instead of one per NFT
19
+ * - the pending-rewards simulations are packed several instructions per transaction (the program's return value of
20
+ * each instruction is read from the "Program return:" log lines) instead of one simulation per NFT
21
+ * - token prices are resolved in parallel, only for bonds the user owns
22
+ */
23
+ async function fetchUsersBondsSOLMulticall(user, bondData, tokenPrices, prefetchedAssets) {
24
+ if (!user)
25
+ return [];
26
+ try {
27
+ console.time('fetchUserBondsSOLMulticall');
28
+ const connection = new Connection(getRPC(ChainId.SOL), 'confirmed');
29
+ const collections = bondData.map((bond) => bond.billNnftAddress[ChainId.SOL]);
30
+ const allUserAssets = prefetchedAssets ?? (await fetchUserAssetsSOL(user, collections));
31
+ const solBonds = bondData.filter((bond) => bond.chainId === ChainId.SOL && !!bond.contractAddress[ChainId.SOL]);
32
+ // 1. Flatten every owned NFT of every bond, deriving its PDAs
33
+ const entries = [];
34
+ for (const bond of solBonds) {
35
+ const bondIssuancePda = new PublicKey(bond.contractAddress[ChainId.SOL]);
36
+ const [bondTermPda] = PublicKey.findProgramAddressSync([new TextEncoder().encode('bond_term'), bondIssuancePda.toBuffer()], PROGRAM_ID);
37
+ const userAssets = allUserAssets.filter((asset) => asset.authorities[0].address.toLowerCase() === bond.contractAddress[ChainId.SOL]?.toLowerCase());
38
+ for (const asset of userAssets) {
39
+ const nftMint = new PublicKey(asset.id);
40
+ const [bondPda] = PublicKey.findProgramAddressSync([new TextEncoder().encode('bond'), bondIssuancePda.toBuffer(), nftMint.toBuffer()], PROGRAM_ID);
41
+ entries.push({ bond, bondIssuancePda, bondTermPda, asset, nftMint, bondPda });
42
+ }
43
+ }
44
+ // 2. Bond accounts, pending rewards and token prices. Prices are only resolved for bonds the user owns NFTs of:
45
+ // the bulk price list first, then an individual request for tokens missing from it.
46
+ // The bond accounts are read directly (not through `solanaMulticall`, which swallows errors and would make a
47
+ // failed read look like "no bonds") so any failure reaches `fetchUsersBondsSOL` and gets reported.
48
+ const ownedBonds = solBonds.filter((bond) => entries.some((e) => e.bond === bond));
49
+ const [bondAccounts, pendingRewards, ownedBondPrices] = await Promise.all([
50
+ entries.length
51
+ ? getBondAccountsSolanaAligned(entries.map((e) => e.bondPda.toBase58()))
52
+ : Promise.resolve([]),
53
+ calculatePendingRewardsBatch(connection, entries),
54
+ Promise.all(ownedBonds.map((bond) => resolveBondTokenPrices(bond, tokenPrices?.[ChainId.SOL]))),
55
+ ]);
56
+ const pricesByBond = new Map(ownedBonds.map((bond, k) => [bond, ownedBondPrices[k]]));
57
+ // 3. Assemble, in the same shape and order as the sequential version
58
+ const billsByBond = new Map();
59
+ entries.forEach((entry, i) => {
60
+ const bondAccount = bondAccounts[i];
61
+ if (!bondAccount) {
62
+ console.error(`Could not fetch bond account for mint ${entry.nftMint.toBase58()}`);
63
+ return;
64
+ }
65
+ const { lpPrice: principalTokenPrice, earnTokenPrice: payoutTokenPrice } = pricesByBond.get(entry.bond);
66
+ const idTrimmed = entry.asset.content.metadata.name.split('-')[1]?.trim();
67
+ const bills = billsByBond.get(entry.bond.index) ?? [];
68
+ bills.push({
69
+ address: entry.bondPda.toBase58(),
70
+ id: idTrimmed,
71
+ asset: entry.nftMint.toBase58(),
72
+ vestingTerm: bondAccount.vestingTerm.toString(),
73
+ payout: (bondAccount.payout - bondAccount.payoutClaimed).toString(),
74
+ totalPayout: bondAccount.payout.toString(),
75
+ truePricePaid: bondAccount.truePricePaid.toString(),
76
+ lastBlockTimestamp: bondAccount.lastClaimTimestamp.toString(),
77
+ vestingStartTimestamp: bondAccount.vestingStartTimestamp.toString(),
78
+ pendingRewards: pendingRewards[i],
79
+ billNftAddress: entry.bond.billNnftAddress[ChainId.SOL],
80
+ payoutClaimed: bondAccount.payoutClaimed.toString(),
81
+ bond: {
82
+ ...entry.bond,
83
+ payoutTokenPrice: payoutTokenPrice?.toString(),
84
+ principalTokenPrice: principalTokenPrice?.toString(),
85
+ },
86
+ });
87
+ billsByBond.set(entry.bond.index, bills);
88
+ });
89
+ const results = solBonds.map((bond) => ({
90
+ index: bond.index,
91
+ userOwnedBills: billsByBond.get(bond.index) ?? [],
92
+ }));
93
+ console.timeEnd('fetchUserBondsSOLMulticall');
94
+ return results;
95
+ }
96
+ catch (err) {
97
+ console.error('Error fetching assets by collection (multicall):', err);
98
+ throw err;
99
+ }
100
+ }
101
+ function buildClaimablePayoutInstruction(entry, encodedTimestamp) {
102
+ return new TransactionInstruction({
103
+ keys: [
104
+ { pubkey: entry.bondPda, isSigner: false, isWritable: true },
105
+ { pubkey: entry.bondTermPda, isSigner: false, isWritable: false },
106
+ { pubkey: entry.bondIssuancePda, isSigner: false, isWritable: false },
107
+ { pubkey: entry.nftMint, isSigner: false, isWritable: false },
108
+ ],
109
+ programId: PROGRAM_ID,
110
+ data: Buffer.concat([CLAIMABLE_PAYOUT_DISCRIMINATOR, Buffer.from(encodedTimestamp)]),
111
+ });
112
+ }
113
+ /**
114
+ * Pending rewards for every entry (same order). Simulates SIMULATIONS_PER_TX instructions per transaction and reads
115
+ * each instruction's return value from the `Program return: <programId> <base64>` log lines, which the runtime emits
116
+ * in instruction order. If a chunk fails or doesn't yield exactly one return per instruction (error, truncated logs…)
117
+ * it falls back to one simulation per entry, i.e. the legacy behaviour.
118
+ */
119
+ async function calculatePendingRewardsBatch(connection, entries) {
120
+ const encodedTimestamp = getU64Codec().encode(BigInt(Math.floor(Date.now() / 1000)));
121
+ const programId = PROGRAM_ID.toBase58();
122
+ const out = new Array(entries.length).fill('0');
123
+ const chunks = [];
124
+ for (let i = 0; i < entries.length; i += SIMULATIONS_PER_TX) {
125
+ chunks.push(entries.slice(i, i + SIMULATIONS_PER_TX).map((_, j) => i + j));
126
+ }
127
+ const runChunk = async (indexes) => {
128
+ try {
129
+ const transaction = new Transaction();
130
+ transaction.feePayer = new PublicKey('11111111111111111111111111111112'); // Dummy public key for fee payer
131
+ indexes.forEach((i) => transaction.add(buildClaimablePayoutInstruction(entries[i], encodedTimestamp)));
132
+ const simulation = await connection.simulateTransaction(transaction);
133
+ const returns = (simulation.value.logs ?? [])
134
+ .filter((line) => line.startsWith(`Program return: ${programId} `))
135
+ .map((line) => line.slice(`Program return: ${programId} `.length).trim());
136
+ if (simulation.value.err || returns.length !== indexes.length) {
137
+ throw new Error(`Unusable batched simulation (err=${JSON.stringify(simulation.value.err)}, returns=${returns.length}/${indexes.length})`);
138
+ }
139
+ indexes.forEach((entryIndex, j) => {
140
+ out[entryIndex] = Buffer.from(returns[j], 'base64').readBigUInt64LE(0).toString();
141
+ });
142
+ }
143
+ catch (e) {
144
+ console.warn('Batched pending rewards simulation failed, falling back to individual simulations:', e);
145
+ for (const i of indexes) {
146
+ const { bondPda, bondTermPda, bondIssuancePda, nftMint } = entries[i];
147
+ out[i] = await calculatePendingRewards(connection, bondPda, bondTermPda, bondIssuancePda, nftMint);
148
+ }
149
+ }
150
+ };
151
+ // Bounded parallelism over the chunks
152
+ let next = 0;
153
+ await Promise.all(Array.from({ length: Math.min(MAX_PARALLEL_SIMULATIONS, chunks.length) }, async () => {
154
+ while (next < chunks.length) {
155
+ await runChunk(chunks[next++]);
156
+ }
157
+ }));
158
+ return out;
159
+ }
160
+
161
+ export { fetchUsersBondsSOLMulticall };
162
+ //# sourceMappingURL=fetchUsersBondsSOLMulticall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchUsersBondsSOLMulticall.js","sources":["../../../../src/adapters/solana/state/fetchUsersBondsSOLMulticall.ts"],"sourcesContent":["import { BillsConfig, ChainId } from '@ape.swap/apeswap-lists'\nimport { UserBill, UserOwnedBillsData } from '../../../types/yourbonds'\nimport { Connection, PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js'\nimport { TokenPrices } from '../../../state/tokenPrices/useTokenPrices'\nimport { HeliusAsset } from '../types/helius'\nimport { getU64Codec } from '@solana/kit'\nimport { CLAIMABLE_PAYOUT_DISCRIMINATOR, PROGRAM_ID } from '../constants/solanaConstants'\nimport { Buffer } from 'buffer'\nimport { getRPC } from '../../../config/constants/networks'\nimport { resolveBondTokenPrices } from '../../../state/tokenPrices/resolveTokenPrice'\nimport { fetchUserAssetsSOL } from '../utils/fetchUserAssetsSOL'\nimport { getBondAccountsSolanaAligned } from '../utils/getSolanaAccount'\nimport { BondAccount } from '../utils/codecs'\nimport { calculatePendingRewards } from '../utils/calculatePendingRewards'\n\n// A legacy transaction is capped at 1232 bytes; each claimable-payout instruction adds up to two unique accounts\n// (bond PDA + NFT mint), so 6 per simulation leaves a comfortable margin.\nconst SIMULATIONS_PER_TX = 6\nconst MAX_PARALLEL_SIMULATIONS = 3\n\ninterface UserAssetEntry {\n bond: BillsConfig\n bondIssuancePda: PublicKey\n bondTermPda: PublicKey\n asset: HeliusAsset\n nftMint: PublicKey\n bondPda: PublicKey\n}\n\n/**\n * Multicall version of `fetchUsersBondsSOLSequential`. Throws on failure (the caller `fetchUsersBondsSOL` reports it). Same inputs and output, but:\n * - all Bond accounts are read with one batched `getMultipleAccounts` (`getBondAccountsSolanaAligned`) instead of one per NFT\n * - the pending-rewards simulations are packed several instructions per transaction (the program's return value of\n * each instruction is read from the \"Program return:\" log lines) instead of one simulation per NFT\n * - token prices are resolved in parallel, only for bonds the user owns\n */\nexport async function fetchUsersBondsSOLMulticall(\n user: string,\n bondData: BillsConfig[],\n tokenPrices: Partial<Record<ChainId, TokenPrices[]>>,\n prefetchedAssets?: HeliusAsset[],\n): Promise<UserOwnedBillsData[]> {\n if (!user) return []\n try {\n console.time('fetchUserBondsSOLMulticall')\n const connection = new Connection(getRPC(ChainId.SOL), 'confirmed')\n\n const collections: string[] = bondData.map((bond) => bond.billNnftAddress[ChainId.SOL]!)\n\n const allUserAssets: HeliusAsset[] = prefetchedAssets ?? (await fetchUserAssetsSOL(user, collections))\n\n const solBonds = bondData.filter((bond) => bond.chainId === ChainId.SOL && !!bond.contractAddress[ChainId.SOL])\n\n // 1. Flatten every owned NFT of every bond, deriving its PDAs\n const entries: UserAssetEntry[] = []\n for (const bond of solBonds) {\n const bondIssuancePda = new PublicKey(bond.contractAddress[ChainId.SOL]!)\n const [bondTermPda] = PublicKey.findProgramAddressSync(\n [new TextEncoder().encode('bond_term'), bondIssuancePda.toBuffer()],\n PROGRAM_ID,\n )\n const userAssets = allUserAssets.filter(\n (asset) => asset.authorities[0].address.toLowerCase() === bond.contractAddress[ChainId.SOL]?.toLowerCase(),\n )\n for (const asset of userAssets) {\n const nftMint = new PublicKey(asset.id)\n const [bondPda] = PublicKey.findProgramAddressSync(\n [new TextEncoder().encode('bond'), bondIssuancePda.toBuffer(), nftMint.toBuffer()],\n PROGRAM_ID,\n )\n entries.push({ bond, bondIssuancePda, bondTermPda, asset, nftMint, bondPda })\n }\n }\n\n // 2. Bond accounts, pending rewards and token prices. Prices are only resolved for bonds the user owns NFTs of:\n // the bulk price list first, then an individual request for tokens missing from it.\n // The bond accounts are read directly (not through `solanaMulticall`, which swallows errors and would make a\n // failed read look like \"no bonds\") so any failure reaches `fetchUsersBondsSOL` and gets reported.\n const ownedBonds = solBonds.filter((bond) => entries.some((e) => e.bond === bond))\n const [bondAccounts, pendingRewards, ownedBondPrices] = await Promise.all([\n entries.length\n ? getBondAccountsSolanaAligned(entries.map((e) => e.bondPda.toBase58()))\n : Promise.resolve([] as (BondAccount | null)[]),\n calculatePendingRewardsBatch(connection, entries),\n Promise.all(ownedBonds.map((bond) => resolveBondTokenPrices(bond, tokenPrices?.[ChainId.SOL]))),\n ])\n const pricesByBond = new Map(ownedBonds.map((bond, k) => [bond, ownedBondPrices[k]]))\n\n // 3. Assemble, in the same shape and order as the sequential version\n const billsByBond = new Map<number, UserBill[]>()\n entries.forEach((entry, i) => {\n const bondAccount = bondAccounts[i]\n if (!bondAccount) {\n console.error(`Could not fetch bond account for mint ${entry.nftMint.toBase58()}`)\n return\n }\n const { lpPrice: principalTokenPrice, earnTokenPrice: payoutTokenPrice } = pricesByBond.get(entry.bond)!\n const idTrimmed: string = entry.asset.content.metadata.name.split('-')[1]?.trim()\n const bills = billsByBond.get(entry.bond.index) ?? []\n bills.push({\n address: entry.bondPda.toBase58(),\n id: idTrimmed,\n asset: entry.nftMint.toBase58(),\n vestingTerm: bondAccount.vestingTerm.toString(),\n payout: (bondAccount.payout - bondAccount.payoutClaimed).toString(),\n totalPayout: bondAccount.payout.toString(),\n truePricePaid: bondAccount.truePricePaid.toString(),\n lastBlockTimestamp: bondAccount.lastClaimTimestamp.toString(),\n vestingStartTimestamp: bondAccount.vestingStartTimestamp.toString(),\n pendingRewards: pendingRewards[i],\n billNftAddress: entry.bond.billNnftAddress[ChainId.SOL] as string,\n payoutClaimed: bondAccount.payoutClaimed.toString(),\n bond: {\n ...entry.bond,\n payoutTokenPrice: payoutTokenPrice?.toString(),\n principalTokenPrice: principalTokenPrice?.toString(),\n },\n })\n billsByBond.set(entry.bond.index, bills)\n })\n\n const results: UserOwnedBillsData[] = solBonds.map((bond) => ({\n index: bond.index,\n userOwnedBills: billsByBond.get(bond.index) ?? [],\n }))\n console.timeEnd('fetchUserBondsSOLMulticall')\n return results\n } catch (err) {\n console.error('Error fetching assets by collection (multicall):', err)\n throw err\n }\n}\n\nfunction buildClaimablePayoutInstruction(entry: UserAssetEntry, encodedTimestamp: ArrayLike<number>) {\n return new TransactionInstruction({\n keys: [\n { pubkey: entry.bondPda, isSigner: false, isWritable: true },\n { pubkey: entry.bondTermPda, isSigner: false, isWritable: false },\n { pubkey: entry.bondIssuancePda, isSigner: false, isWritable: false },\n { pubkey: entry.nftMint, isSigner: false, isWritable: false },\n ],\n programId: PROGRAM_ID,\n data: Buffer.concat([CLAIMABLE_PAYOUT_DISCRIMINATOR, Buffer.from(encodedTimestamp)]),\n })\n}\n\n/**\n * Pending rewards for every entry (same order). Simulates SIMULATIONS_PER_TX instructions per transaction and reads\n * each instruction's return value from the `Program return: <programId> <base64>` log lines, which the runtime emits\n * in instruction order. If a chunk fails or doesn't yield exactly one return per instruction (error, truncated logs…)\n * it falls back to one simulation per entry, i.e. the legacy behaviour.\n */\nasync function calculatePendingRewardsBatch(connection: Connection, entries: UserAssetEntry[]): Promise<string[]> {\n const encodedTimestamp = getU64Codec().encode(BigInt(Math.floor(Date.now() / 1000)))\n const programId = PROGRAM_ID.toBase58()\n const out: string[] = new Array(entries.length).fill('0')\n\n const chunks: number[][] = []\n for (let i = 0; i < entries.length; i += SIMULATIONS_PER_TX) {\n chunks.push(entries.slice(i, i + SIMULATIONS_PER_TX).map((_, j) => i + j))\n }\n\n const runChunk = async (indexes: number[]) => {\n try {\n const transaction = new Transaction()\n transaction.feePayer = new PublicKey('11111111111111111111111111111112') // Dummy public key for fee payer\n indexes.forEach((i) => transaction.add(buildClaimablePayoutInstruction(entries[i], encodedTimestamp)))\n const simulation = await connection.simulateTransaction(transaction)\n\n const returns = (simulation.value.logs ?? [])\n .filter((line) => line.startsWith(`Program return: ${programId} `))\n .map((line) => line.slice(`Program return: ${programId} `.length).trim())\n\n if (simulation.value.err || returns.length !== indexes.length) {\n throw new Error(\n `Unusable batched simulation (err=${JSON.stringify(simulation.value.err)}, returns=${returns.length}/${indexes.length})`,\n )\n }\n indexes.forEach((entryIndex, j) => {\n out[entryIndex] = Buffer.from(returns[j], 'base64').readBigUInt64LE(0).toString()\n })\n } catch (e) {\n console.warn('Batched pending rewards simulation failed, falling back to individual simulations:', e)\n for (const i of indexes) {\n const { bondPda, bondTermPda, bondIssuancePda, nftMint } = entries[i]\n out[i] = await calculatePendingRewards(connection, bondPda, bondTermPda, bondIssuancePda, nftMint)\n }\n }\n }\n\n // Bounded parallelism over the chunks\n let next = 0\n await Promise.all(\n Array.from({ length: Math.min(MAX_PARALLEL_SIMULATIONS, chunks.length) }, async () => {\n while (next < chunks.length) {\n await runChunk(chunks[next++])\n }\n }),\n )\n return out\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAeA;AACA;AACA,MAAM,kBAAkB,GAAG,CAAC;AAC5B,MAAM,wBAAwB,GAAG,CAAC;AAWlC;;;;;;AAMG;AACI,eAAe,2BAA2B,CAC/C,IAAY,EACZ,QAAuB,EACvB,WAAoD,EACpD,gBAAgC,EAAA;AAEhC,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,EAAE;AACpB,IAAA,IAAI;AACF,QAAA,OAAO,CAAC,IAAI,CAAC,4BAA4B,CAAC;AAC1C,QAAA,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC;QAEnE,MAAM,WAAW,GAAa,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAE,CAAC;AAExF,QAAA,MAAM,aAAa,GAAkB,gBAAgB,KAAK,MAAM,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAEtG,QAAA,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;;QAG/G,MAAM,OAAO,GAAqB,EAAE;AACpC,QAAA,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE;AAC3B,YAAA,MAAM,eAAe,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAE,CAAC;YACzE,MAAM,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC,sBAAsB,CACpD,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,eAAe,CAAC,QAAQ,EAAE,CAAC,EACnE,UAAU,CACX;AACD,YAAA,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CACrC,CAAC,KAAK,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,CAC3G;AACD,YAAA,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE;gBAC9B,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;AACvC,gBAAA,MAAM,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,sBAAsB,CAChD,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,EAClF,UAAU,CACX;AACD,gBAAA,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YAC/E;QACF;;;;;QAMA,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAClF,QAAA,MAAM,CAAC,YAAY,EAAE,cAAc,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;AACxE,YAAA,OAAO,CAAC;AACN,kBAAE,4BAA4B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;AACvE,kBAAE,OAAO,CAAC,OAAO,CAAC,EAA4B,CAAC;AACjD,YAAA,4BAA4B,CAAC,UAAU,EAAE,OAAO,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAChG,SAAA,CAAC;QACF,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;AAGrF,QAAA,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB;QACjD,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,KAAI;AAC3B,YAAA,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC;YACnC,IAAI,CAAC,WAAW,EAAE;AAChB,gBAAA,OAAO,CAAC,KAAK,CAAC,CAAA,sCAAA,EAAyC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAA,CAAE,CAAC;gBAClF;YACF;AACA,YAAA,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,gBAAgB,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE;YACxG,MAAM,SAAS,GAAW,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;AACjF,YAAA,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACrD,KAAK,CAAC,IAAI,CAAC;AACT,gBAAA,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE;AACjC,gBAAA,EAAE,EAAE,SAAS;AACb,gBAAA,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE;AAC/B,gBAAA,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,QAAQ,EAAE;AAC/C,gBAAA,MAAM,EAAE,CAAC,WAAW,CAAC,MAAM,GAAG,WAAW,CAAC,aAAa,EAAE,QAAQ,EAAE;AACnE,gBAAA,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE;AAC1C,gBAAA,aAAa,EAAE,WAAW,CAAC,aAAa,CAAC,QAAQ,EAAE;AACnD,gBAAA,kBAAkB,EAAE,WAAW,CAAC,kBAAkB,CAAC,QAAQ,EAAE;AAC7D,gBAAA,qBAAqB,EAAE,WAAW,CAAC,qBAAqB,CAAC,QAAQ,EAAE;AACnE,gBAAA,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC;gBACjC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAW;AACjE,gBAAA,aAAa,EAAE,WAAW,CAAC,aAAa,CAAC,QAAQ,EAAE;AACnD,gBAAA,IAAI,EAAE;oBACJ,GAAG,KAAK,CAAC,IAAI;AACb,oBAAA,gBAAgB,EAAE,gBAAgB,EAAE,QAAQ,EAAE;AAC9C,oBAAA,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EAAE;AACrD,iBAAA;AACF,aAAA,CAAC;YACF,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC;AAC1C,QAAA,CAAC,CAAC;QAEF,MAAM,OAAO,GAAyB,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;YAC5D,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,cAAc,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;AAClD,SAAA,CAAC,CAAC;AACH,QAAA,OAAO,CAAC,OAAO,CAAC,4BAA4B,CAAC;AAC7C,QAAA,OAAO,OAAO;IAChB;IAAE,OAAO,GAAG,EAAE;AACZ,QAAA,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,GAAG,CAAC;AACtE,QAAA,MAAM,GAAG;IACX;AACF;AAEA,SAAS,+BAA+B,CAAC,KAAqB,EAAE,gBAAmC,EAAA;IACjG,OAAO,IAAI,sBAAsB,CAAC;AAChC,QAAA,IAAI,EAAE;AACJ,YAAA,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE;AAC5D,YAAA,EAAE,MAAM,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;AACjE,YAAA,EAAE,MAAM,EAAE,KAAK,CAAC,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;AACrE,YAAA,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;AAC9D,SAAA;AACD,QAAA,SAAS,EAAE,UAAU;AACrB,QAAA,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,8BAA8B,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AACrF,KAAA,CAAC;AACJ;AAEA;;;;;AAKG;AACH,eAAe,4BAA4B,CAAC,UAAsB,EAAE,OAAyB,EAAA;IAC3F,MAAM,gBAAgB,GAAG,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;AACpF,IAAA,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE;AACvC,IAAA,MAAM,GAAG,GAAa,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;IAEzD,MAAM,MAAM,GAAe,EAAE;AAC7B,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,kBAAkB,EAAE;AAC3D,QAAA,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5E;AAEA,IAAA,MAAM,QAAQ,GAAG,OAAO,OAAiB,KAAI;AAC3C,QAAA,IAAI;AACF,YAAA,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE;YACrC,WAAW,CAAC,QAAQ,GAAG,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAA;YACxE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,GAAG,CAAC,+BAA+B,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;YACtG,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,mBAAmB,CAAC,WAAW,CAAC;YAEpE,MAAM,OAAO,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE;AACzC,iBAAA,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,CAAA,gBAAA,EAAmB,SAAS,CAAA,CAAA,CAAG,CAAC;iBACjE,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,mBAAmB,SAAS,CAAA,CAAA,CAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AAE3E,YAAA,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE;gBAC7D,MAAM,IAAI,KAAK,CACb,CAAA,iCAAA,EAAoC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,UAAA,EAAa,OAAO,CAAC,MAAM,CAAA,CAAA,EAAI,OAAO,CAAC,MAAM,CAAA,CAAA,CAAG,CACzH;YACH;YACA,OAAO,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC,KAAI;gBAChC,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;AACnF,YAAA,CAAC,CAAC;QACJ;QAAE,OAAO,CAAC,EAAE;AACV,YAAA,OAAO,CAAC,IAAI,CAAC,oFAAoF,EAAE,CAAC,CAAC;AACrG,YAAA,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE;AACvB,gBAAA,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;AACrE,gBAAA,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,uBAAuB,CAAC,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,CAAC;YACpG;QACF;AACF,IAAA,CAAC;;IAGD,IAAI,IAAI,GAAG,CAAC;IACZ,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,wBAAwB,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,YAAW;AACnF,QAAA,OAAO,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE;YAC3B,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAChC;IACF,CAAC,CAAC,CACH;AACD,IAAA,OAAO,GAAG;AACZ;;;;"}
@@ -0,0 +1,2 @@
1
+ import { Connection, PublicKey } from '@solana/web3.js';
2
+ export declare function calculatePendingRewards(connection: Connection, bondPda: PublicKey, bondTermPda: PublicKey, bondIssuancePda: PublicKey, nftMint: PublicKey): Promise<string>;
@@ -0,0 +1,53 @@
1
+ import { TransactionInstruction, Transaction, PublicKey } from '@solana/web3.js';
2
+ import { getU64Codec } from '@solana/kit';
3
+ import { Buffer } from 'buffer';
4
+ import { CLAIMABLE_PAYOUT_DISCRIMINATOR, PROGRAM_ID } from '../constants/solanaConstants.js';
5
+
6
+ async function calculatePendingRewards(connection, bondPda, bondTermPda, bondIssuancePda, nftMint) {
7
+ try {
8
+ const u64Codec = getU64Codec();
9
+ const currentTimestamp = Math.floor(Date.now() / 1000);
10
+ const encodedTimestamp = u64Codec.encode(BigInt(currentTimestamp));
11
+ // Instruction data
12
+ const instructionData = Buffer.concat([CLAIMABLE_PAYOUT_DISCRIMINATOR, Buffer.from(encodedTimestamp)]);
13
+ // Accounts
14
+ const keys = [
15
+ { pubkey: bondPda, isSigner: false, isWritable: true },
16
+ { pubkey: bondTermPda, isSigner: false, isWritable: false },
17
+ { pubkey: bondIssuancePda, isSigner: false, isWritable: false },
18
+ { pubkey: nftMint, isSigner: false, isWritable: false },
19
+ ];
20
+ // Instruction
21
+ const instruction = new TransactionInstruction({
22
+ keys,
23
+ programId: PROGRAM_ID,
24
+ data: instructionData,
25
+ });
26
+ // Transaction
27
+ const transaction = new Transaction();
28
+ const userPublicKey = new PublicKey('11111111111111111111111111111112'); // Dummy public key for fee payer
29
+ transaction.feePayer = userPublicKey;
30
+ transaction.add(instruction);
31
+ // Simulate
32
+ const simulation = await connection.simulateTransaction(transaction);
33
+ // Extract return value
34
+ const returnData = simulation.value.returnData;
35
+ if (returnData) {
36
+ const [data, encoding] = returnData.data;
37
+ if (encoding !== 'base64') {
38
+ throw new Error(`Unexpected return data encoding: ${encoding}`);
39
+ }
40
+ const decodedData = Buffer.from(data, 'base64');
41
+ return decodedData.readBigUInt64LE(0).toString();
42
+ }
43
+ // Fallback
44
+ return '0';
45
+ }
46
+ catch (error) {
47
+ console.error('Error calculating pending rewards:', error);
48
+ return '0';
49
+ }
50
+ }
51
+
52
+ export { calculatePendingRewards };
53
+ //# sourceMappingURL=calculatePendingRewards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calculatePendingRewards.js","sources":["../../../../src/adapters/solana/utils/calculatePendingRewards.ts"],"sourcesContent":["import { Connection, PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js'\nimport { getU64Codec } from '@solana/kit'\nimport { Buffer } from 'buffer'\nimport { CLAIMABLE_PAYOUT_DISCRIMINATOR, PROGRAM_ID } from '../constants/solanaConstants'\n\nexport async function calculatePendingRewards(\n connection: Connection,\n bondPda: PublicKey,\n bondTermPda: PublicKey,\n bondIssuancePda: PublicKey,\n nftMint: PublicKey,\n): Promise<string> {\n try {\n const u64Codec = getU64Codec()\n\n const currentTimestamp = Math.floor(Date.now() / 1000)\n\n const encodedTimestamp = u64Codec.encode(BigInt(currentTimestamp))\n\n // Instruction data\n const instructionData = Buffer.concat([CLAIMABLE_PAYOUT_DISCRIMINATOR, Buffer.from(encodedTimestamp)])\n\n // Accounts\n const keys = [\n { pubkey: bondPda, isSigner: false, isWritable: true },\n { pubkey: bondTermPda, isSigner: false, isWritable: false },\n { pubkey: bondIssuancePda, isSigner: false, isWritable: false },\n { pubkey: nftMint, isSigner: false, isWritable: false },\n ]\n\n // Instruction\n const instruction = new TransactionInstruction({\n keys,\n programId: PROGRAM_ID,\n data: instructionData,\n })\n\n // Transaction\n const transaction = new Transaction()\n const userPublicKey = new PublicKey('11111111111111111111111111111112') // Dummy public key for fee payer\n transaction.feePayer = userPublicKey\n transaction.add(instruction)\n\n // Simulate\n const simulation = await connection.simulateTransaction(transaction)\n\n // Extract return value\n const returnData = simulation.value.returnData\n if (returnData) {\n const [data, encoding] = returnData.data\n if (encoding !== 'base64') {\n throw new Error(`Unexpected return data encoding: ${encoding}`)\n }\n\n const decodedData = Buffer.from(data, 'base64')\n return decodedData.readBigUInt64LE(0).toString()\n }\n\n // Fallback\n return '0'\n } catch (error) {\n console.error('Error calculating pending rewards:', error)\n return '0'\n }\n}\n"],"names":[],"mappings":";;;;;AAKO,eAAe,uBAAuB,CAC3C,UAAsB,EACtB,OAAkB,EAClB,WAAsB,EACtB,eAA0B,EAC1B,OAAkB,EAAA;AAElB,IAAA,IAAI;AACF,QAAA,MAAM,QAAQ,GAAG,WAAW,EAAE;AAE9B,QAAA,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAEtD,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;;AAGlE,QAAA,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,8BAA8B,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;;AAGtG,QAAA,MAAM,IAAI,GAAG;YACX,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE;YACtD,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;YAC3D,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;YAC/D,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;SACxD;;AAGD,QAAA,MAAM,WAAW,GAAG,IAAI,sBAAsB,CAAC;YAC7C,IAAI;AACJ,YAAA,SAAS,EAAE,UAAU;AACrB,YAAA,IAAI,EAAE,eAAe;AACtB,SAAA,CAAC;;AAGF,QAAA,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE;QACrC,MAAM,aAAa,GAAG,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAA;AACvE,QAAA,WAAW,CAAC,QAAQ,GAAG,aAAa;AACpC,QAAA,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC;;QAG5B,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,mBAAmB,CAAC,WAAW,CAAC;;AAGpE,QAAA,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU;QAC9C,IAAI,UAAU,EAAE;YACd,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,IAAI;AACxC,YAAA,IAAI,QAAQ,KAAK,QAAQ,EAAE;AACzB,gBAAA,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,CAAA,CAAE,CAAC;YACjE;YAEA,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC;YAC/C,OAAO,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;QAClD;;AAGA,QAAA,OAAO,GAAG;IACZ;IAAE,OAAO,KAAK,EAAE;AACd,QAAA,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,CAAC;AAC1D,QAAA,OAAO,GAAG;IACZ;AACF;;;;"}
@@ -0,0 +1,3 @@
1
+ import { HeliusAsset } from '../types/helius';
2
+ /** The user's bond NFTs (across the given collections) through Helius `searchAssets`, all pages. */
3
+ export declare function fetchUserAssetsSOL(user: string, collections: string[]): Promise<HeliusAsset[]>;
@@ -0,0 +1,49 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ import { getShuffledHeliusRPCs } from '../../../config/constants/networks.js';
3
+
4
+ const ASSETS_PER_PAGE = 1000;
5
+ // Safety net against a node that keeps returning full pages
6
+ const MAX_PAGES = 20;
7
+ /** One `searchAssets` page. A failed call (network, non-2xx, JSON-RPC error) is retried once on a different node. */
8
+ async function fetchAssetsPage(ownerAddress, collections, page) {
9
+ // Custom RPCs from the consumer are deliberately ignored: searchAssets is a Helius-only DAS method
10
+ const [primary, fallback] = getShuffledHeliusRPCs();
11
+ const body = JSON.stringify({
12
+ jsonrpc: '2.0',
13
+ id: '1',
14
+ method: 'searchAssets',
15
+ params: { ownerAddress, collections, page, limit: ASSETS_PER_PAGE },
16
+ });
17
+ const request = async (url) => {
18
+ const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body });
19
+ if (!response.ok)
20
+ throw new Error(`Helius searchAssets failed: HTTP ${response.status}`);
21
+ const data = await response.json();
22
+ if (data.error || !data.result?.items) {
23
+ throw new Error(`Helius searchAssets failed: ${data.error?.message ?? 'missing result'}`);
24
+ }
25
+ return data.result.items;
26
+ };
27
+ try {
28
+ return await request(primary);
29
+ }
30
+ catch (error) {
31
+ console.warn('Helius searchAssets failed, retrying with another node:', error);
32
+ return request(fallback ?? primary);
33
+ }
34
+ }
35
+ /** The user's bond NFTs (across the given collections) through Helius `searchAssets`, all pages. */
36
+ async function fetchUserAssetsSOL(user, collections) {
37
+ const ownerAddress = new PublicKey(user).toBase58();
38
+ const assets = [];
39
+ for (let page = 1; page <= MAX_PAGES; page++) {
40
+ const items = await fetchAssetsPage(ownerAddress, collections, page);
41
+ assets.push(...items);
42
+ if (items.length < ASSETS_PER_PAGE)
43
+ break;
44
+ }
45
+ return assets;
46
+ }
47
+
48
+ export { fetchUserAssetsSOL };
49
+ //# sourceMappingURL=fetchUserAssetsSOL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchUserAssetsSOL.js","sources":["../../../../src/adapters/solana/utils/fetchUserAssetsSOL.ts"],"sourcesContent":["import { PublicKey } from '@solana/web3.js'\nimport { getShuffledHeliusRPCs } from '../../../config/constants/networks'\nimport { HeliusApiResponse, HeliusAsset } from '../types/helius'\n\nconst ASSETS_PER_PAGE = 1000\n// Safety net against a node that keeps returning full pages\nconst MAX_PAGES = 20\n\n/** One `searchAssets` page. A failed call (network, non-2xx, JSON-RPC error) is retried once on a different node. */\nasync function fetchAssetsPage(ownerAddress: string, collections: string[], page: number): Promise<HeliusAsset[]> {\n // Custom RPCs from the consumer are deliberately ignored: searchAssets is a Helius-only DAS method\n const [primary, fallback] = getShuffledHeliusRPCs()\n const body = JSON.stringify({\n jsonrpc: '2.0',\n id: '1',\n method: 'searchAssets',\n params: { ownerAddress, collections, page, limit: ASSETS_PER_PAGE },\n })\n\n const request = async (url: string): Promise<HeliusAsset[]> => {\n const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body })\n if (!response.ok) throw new Error(`Helius searchAssets failed: HTTP ${response.status}`)\n const data: HeliusApiResponse & { error?: { message?: string } } = await response.json()\n if (data.error || !data.result?.items) {\n throw new Error(`Helius searchAssets failed: ${data.error?.message ?? 'missing result'}`)\n }\n return data.result.items\n }\n\n try {\n return await request(primary)\n } catch (error) {\n console.warn('Helius searchAssets failed, retrying with another node:', error)\n return request(fallback ?? primary)\n }\n}\n\n/** The user's bond NFTs (across the given collections) through Helius `searchAssets`, all pages. */\nexport async function fetchUserAssetsSOL(user: string, collections: string[]): Promise<HeliusAsset[]> {\n const ownerAddress = new PublicKey(user).toBase58()\n const assets: HeliusAsset[] = []\n for (let page = 1; page <= MAX_PAGES; page++) {\n const items = await fetchAssetsPage(ownerAddress, collections, page)\n assets.push(...items)\n if (items.length < ASSETS_PER_PAGE) break\n }\n return assets\n}\n"],"names":[],"mappings":";;;AAIA,MAAM,eAAe,GAAG,IAAI;AAC5B;AACA,MAAM,SAAS,GAAG,EAAE;AAEpB;AACA,eAAe,eAAe,CAAC,YAAoB,EAAE,WAAqB,EAAE,IAAY,EAAA;;IAEtF,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,qBAAqB,EAAE;AACnD,IAAA,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;AAC1B,QAAA,OAAO,EAAE,KAAK;AACd,QAAA,EAAE,EAAE,GAAG;AACP,QAAA,MAAM,EAAE,cAAc;QACtB,MAAM,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE;AACpE,KAAA,CAAC;AAEF,IAAA,MAAM,OAAO,GAAG,OAAO,GAAW,KAA4B;QAC5D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,IAAI,EAAE,CAAC;QAC5G,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,CAAA,iCAAA,EAAoC,QAAQ,CAAC,MAAM,CAAA,CAAE,CAAC;AACxF,QAAA,MAAM,IAAI,GAAyD,MAAM,QAAQ,CAAC,IAAI,EAAE;QACxF,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE;AACrC,YAAA,MAAM,IAAI,KAAK,CAAC,CAAA,4BAAA,EAA+B,IAAI,CAAC,KAAK,EAAE,OAAO,IAAI,gBAAgB,CAAA,CAAE,CAAC;QAC3F;AACA,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK;AAC1B,IAAA,CAAC;AAED,IAAA,IAAI;AACF,QAAA,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC;IAC/B;IAAE,OAAO,KAAK,EAAE;AACd,QAAA,OAAO,CAAC,IAAI,CAAC,yDAAyD,EAAE,KAAK,CAAC;AAC9E,QAAA,OAAO,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC;IACrC;AACF;AAEA;AACO,eAAe,kBAAkB,CAAC,IAAY,EAAE,WAAqB,EAAA;IAC1E,MAAM,YAAY,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IACnD,MAAM,MAAM,GAAkB,EAAE;AAChC,IAAA,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,SAAS,EAAE,IAAI,EAAE,EAAE;QAC5C,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,YAAY,EAAE,WAAW,EAAE,IAAI,CAAC;AACpE,QAAA,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;AACrB,QAAA,IAAI,KAAK,CAAC,MAAM,GAAG,eAAe;YAAE;IACtC;AACA,IAAA,OAAO,MAAM;AACf;;;;"}
@@ -1,5 +1,10 @@
1
1
  import { BondIssuanceAccount, BondAccount, BondTermAccount, BondPricingAccount } from './codecs';
2
2
  export declare function getBondIssuanceAccountSolana(accountAddresses: string[]): Promise<BondIssuanceAccount[] | undefined>;
3
3
  export declare function getBondAccountSolana(accountAddresses: string[]): Promise<BondAccount[] | undefined>;
4
+ /**
5
+ * Batched version of getBondAccountSolana: the result is aligned with `accountAddresses` (a missing account yields
6
+ * `null` instead of aborting the whole batch) and requests are chunked to the RPC limit and sent in parallel.
7
+ */
8
+ export declare function getBondAccountsSolanaAligned(accountAddresses: string[]): Promise<(BondAccount | null)[]>;
4
9
  export declare function getBondTermAccountSolana(accountAddresses: string[]): Promise<BondTermAccount[] | undefined>;
5
10
  export declare function getBondPricingAccountSolana(accountAddresses: string[]): Promise<BondPricingAccount[] | undefined>;
@@ -31,30 +31,33 @@ async function getBondIssuanceAccountSolana(accountAddresses) {
31
31
  }
32
32
  return bondIssuanceAccounts;
33
33
  }
34
- async function getBondAccountSolana(accountAddresses) {
34
+ // getMultipleAccounts accepts up to 100 addresses per request
35
+ const MAX_ACCOUNTS_PER_REQUEST = 100;
36
+ /**
37
+ * Batched version of getBondAccountSolana: the result is aligned with `accountAddresses` (a missing account yields
38
+ * `null` instead of aborting the whole batch) and requests are chunked to the RPC limit and sent in parallel.
39
+ */
40
+ async function getBondAccountsSolanaAligned(accountAddresses) {
35
41
  const rpc = getSolanaRpc();
36
- const bondAddresses = accountAddresses.map((a) => address(a));
37
- const { value } = await rpc
38
- .getMultipleAccounts(bondAddresses, {
39
- encoding: 'base64',
40
- commitment: 'confirmed',
41
- })
42
- .send();
43
- if (!value?.length) {
44
- throw new Error(`Accounts not found for: ${bondAddresses}`);
45
- }
46
- const bondAccounts = [];
47
- for (const account of value) {
48
- if (!account?.data?.[0])
49
- return;
50
- const accountDataBytes = Buffer.from(account?.data?.[0], 'base64');
51
- if (!verifyBondDiscriminator(accountDataBytes)) {
52
- throw new Error(`Invalid account type. Expected Bond account at ${account.owner}`);
53
- }
54
- const bondAccount = bondDecoder.decode(accountDataBytes);
55
- bondAccounts.push(bondAccount);
42
+ const chunks = [];
43
+ for (let i = 0; i < accountAddresses.length; i += MAX_ACCOUNTS_PER_REQUEST) {
44
+ chunks.push(accountAddresses.slice(i, i + MAX_ACCOUNTS_PER_REQUEST));
56
45
  }
57
- return bondAccounts;
46
+ const decodedChunks = await Promise.all(chunks.map(async (chunk) => {
47
+ const { value } = await rpc
48
+ .getMultipleAccounts(chunk.map((a) => address(a)), { encoding: 'base64', commitment: 'confirmed' })
49
+ .send();
50
+ return value.map((account, i) => {
51
+ if (!account?.data?.[0])
52
+ return null;
53
+ const accountDataBytes = Buffer.from(account.data[0], 'base64');
54
+ if (!verifyBondDiscriminator(accountDataBytes)) {
55
+ throw new Error(`Invalid account type. Expected Bond account at ${chunk[i]}`);
56
+ }
57
+ return bondDecoder.decode(accountDataBytes);
58
+ });
59
+ }));
60
+ return decodedChunks.flat();
58
61
  }
59
62
  async function getBondTermAccountSolana(accountAddresses) {
60
63
  const rpc = getSolanaRpc();
@@ -107,5 +110,5 @@ async function getBondPricingAccountSolana(accountAddresses) {
107
110
  return bondPricingAccounts;
108
111
  }
109
112
 
110
- export { getBondAccountSolana, getBondIssuanceAccountSolana, getBondPricingAccountSolana, getBondTermAccountSolana };
113
+ export { getBondAccountsSolanaAligned, getBondIssuanceAccountSolana, getBondPricingAccountSolana, getBondTermAccountSolana };
111
114
  //# sourceMappingURL=getSolanaAccount.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getSolanaAccount.js","sources":["../../../../src/adapters/solana/utils/getSolanaAccount.ts"],"sourcesContent":["import { getSolanaRpc } from './solanaRpc'\nimport { address } from '@solana/kit'\nimport {\n bondIssuanceDecoder,\n verifyBondIssuanceDiscriminator,\n bondDecoder,\n verifyBondDiscriminator,\n bondTermDecoder,\n verifyBondTermDiscriminator,\n bondPricingDecoder,\n verifyBondPricingDiscriminator,\n BondIssuanceAccount,\n BondAccount,\n BondTermAccount,\n BondPricingAccount,\n} from './codecs'\nimport { Buffer } from 'buffer'\n\nexport async function getBondIssuanceAccountSolana(accountAddresses: string[]) {\n const rpc = getSolanaRpc()\n const bondIssuanceAddresses = accountAddresses.map((a) => address(a))\n\n const { value } = await rpc\n .getMultipleAccounts(bondIssuanceAddresses, {\n encoding: 'base64',\n commitment: 'confirmed',\n })\n .send()\n\n if (!value?.length) {\n throw new Error(`Accounts not found for: ${bondIssuanceAddresses}`)\n }\n\n const bondIssuanceAccounts: BondIssuanceAccount[] = []\n\n for (const account of value) {\n if (!account?.data?.[0]) return\n const accountDataBytes = Buffer.from(account?.data?.[0], 'base64')\n\n if (!verifyBondIssuanceDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected BondIssuance account at ${account.owner}`)\n }\n\n const bondIssuanceAccount = bondIssuanceDecoder.decode(accountDataBytes)\n bondIssuanceAccounts.push(bondIssuanceAccount)\n }\n\n return bondIssuanceAccounts\n}\n\nexport async function getBondAccountSolana(accountAddresses: string[]) {\n const rpc = getSolanaRpc()\n const bondAddresses = accountAddresses.map((a) => address(a))\n\n const { value } = await rpc\n .getMultipleAccounts(bondAddresses, {\n encoding: 'base64',\n commitment: 'confirmed',\n })\n .send()\n\n if (!value?.length) {\n throw new Error(`Accounts not found for: ${bondAddresses}`)\n }\n\n const bondAccounts: BondAccount[] = []\n\n for (const account of value) {\n if (!account?.data?.[0]) return\n const accountDataBytes = Buffer.from(account?.data?.[0], 'base64')\n\n if (!verifyBondDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected Bond account at ${account.owner}`)\n }\n\n const bondAccount = bondDecoder.decode(accountDataBytes)\n bondAccounts.push(bondAccount)\n }\n\n return bondAccounts\n}\n\nexport async function getBondTermAccountSolana(accountAddresses: string[]) {\n const rpc = getSolanaRpc()\n const bondTermAddresses = accountAddresses.map((a) => address(a))\n\n const { value } = await rpc\n .getMultipleAccounts(bondTermAddresses, {\n encoding: 'base64',\n commitment: 'confirmed',\n })\n .send()\n\n if (!value?.length) {\n throw new Error(`Accounts not found for: ${bondTermAddresses}`)\n }\n\n const bondTermAccounts: BondTermAccount[] = []\n\n for (const account of value) {\n if (!account?.data?.[0]) return\n const accountDataBytes = Buffer.from(account?.data?.[0], 'base64')\n\n if (!verifyBondTermDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected BondTerm account at ${account.owner}`)\n }\n\n const bondTermAccount = bondTermDecoder.decode(accountDataBytes)\n bondTermAccounts.push(bondTermAccount)\n }\n\n return bondTermAccounts\n}\n\nexport async function getBondPricingAccountSolana(accountAddresses: string[]) {\n const rpc = getSolanaRpc()\n const bondPricingAddresses = accountAddresses.map((a) => address(a))\n\n const { value } = await rpc\n .getMultipleAccounts(bondPricingAddresses, {\n encoding: 'base64',\n commitment: 'confirmed',\n })\n .send()\n\n if (!value?.length) {\n throw new Error(`Accounts not found for: ${bondPricingAddresses}`)\n }\n\n const bondPricingAccounts: BondPricingAccount[] = []\n\n for (const account of value) {\n if (!account?.data?.[0]) return\n const accountDataBytes = Buffer.from(account?.data?.[0], 'base64')\n\n if (!verifyBondPricingDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected BondPricing account at ${account.owner}`)\n }\n\n const bondPricingAccount = bondPricingDecoder.decode(accountDataBytes)\n bondPricingAccounts.push(bondPricingAccount)\n }\n\n return bondPricingAccounts\n}\n"],"names":[],"mappings":";;;;;;;;AAkBO,eAAe,4BAA4B,CAAC,gBAA0B,EAAA;AAC3E,IAAA,MAAM,GAAG,GAAG,YAAY,EAAE;AAC1B,IAAA,MAAM,qBAAqB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAErE,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM;SACrB,mBAAmB,CAAC,qBAAqB,EAAE;AAC1C,QAAA,QAAQ,EAAE,QAAQ;AAClB,QAAA,UAAU,EAAE,WAAW;KACxB;AACA,SAAA,IAAI,EAAE;AAET,IAAA,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE;AAClB,QAAA,MAAM,IAAI,KAAK,CAAC,2BAA2B,qBAAqB,CAAA,CAAE,CAAC;IACrE;IAEA,MAAM,oBAAoB,GAA0B,EAAE;AAEtD,IAAA,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE;AAC3B,QAAA,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC;YAAE;AACzB,QAAA,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;AAElE,QAAA,IAAI,CAAC,+BAA+B,CAAC,gBAAgB,CAAC,EAAE;YACtD,MAAM,IAAI,KAAK,CAAC,CAAA,uDAAA,EAA0D,OAAO,CAAC,KAAK,CAAA,CAAE,CAAC;QAC5F;QAEA,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,MAAM,CAAC,gBAAgB,CAAC;AACxE,QAAA,oBAAoB,CAAC,IAAI,CAAC,mBAAmB,CAAC;IAChD;AAEA,IAAA,OAAO,oBAAoB;AAC7B;AAEO,eAAe,oBAAoB,CAAC,gBAA0B,EAAA;AACnE,IAAA,MAAM,GAAG,GAAG,YAAY,EAAE;AAC1B,IAAA,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7D,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM;SACrB,mBAAmB,CAAC,aAAa,EAAE;AAClC,QAAA,QAAQ,EAAE,QAAQ;AAClB,QAAA,UAAU,EAAE,WAAW;KACxB;AACA,SAAA,IAAI,EAAE;AAET,IAAA,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE;AAClB,QAAA,MAAM,IAAI,KAAK,CAAC,2BAA2B,aAAa,CAAA,CAAE,CAAC;IAC7D;IAEA,MAAM,YAAY,GAAkB,EAAE;AAEtC,IAAA,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE;AAC3B,QAAA,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC;YAAE;AACzB,QAAA,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;AAElE,QAAA,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,CAAC,EAAE;YAC9C,MAAM,IAAI,KAAK,CAAC,CAAA,+CAAA,EAAkD,OAAO,CAAC,KAAK,CAAA,CAAE,CAAC;QACpF;QAEA,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC;AACxD,QAAA,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC;IAChC;AAEA,IAAA,OAAO,YAAY;AACrB;AAEO,eAAe,wBAAwB,CAAC,gBAA0B,EAAA;AACvE,IAAA,MAAM,GAAG,GAAG,YAAY,EAAE;AAC1B,IAAA,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEjE,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM;SACrB,mBAAmB,CAAC,iBAAiB,EAAE;AACtC,QAAA,QAAQ,EAAE,QAAQ;AAClB,QAAA,UAAU,EAAE,WAAW;KACxB;AACA,SAAA,IAAI,EAAE;AAET,IAAA,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE;AAClB,QAAA,MAAM,IAAI,KAAK,CAAC,2BAA2B,iBAAiB,CAAA,CAAE,CAAC;IACjE;IAEA,MAAM,gBAAgB,GAAsB,EAAE;AAE9C,IAAA,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE;AAC3B,QAAA,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC;YAAE;AACzB,QAAA,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;AAElE,QAAA,IAAI,CAAC,2BAA2B,CAAC,gBAAgB,CAAC,EAAE;YAClD,MAAM,IAAI,KAAK,CAAC,CAAA,mDAAA,EAAsD,OAAO,CAAC,KAAK,CAAA,CAAE,CAAC;QACxF;QAEA,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,gBAAgB,CAAC;AAChE,QAAA,gBAAgB,CAAC,IAAI,CAAC,eAAe,CAAC;IACxC;AAEA,IAAA,OAAO,gBAAgB;AACzB;AAEO,eAAe,2BAA2B,CAAC,gBAA0B,EAAA;AAC1E,IAAA,MAAM,GAAG,GAAG,YAAY,EAAE;AAC1B,IAAA,MAAM,oBAAoB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAEpE,IAAA,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM;SACrB,mBAAmB,CAAC,oBAAoB,EAAE;AACzC,QAAA,QAAQ,EAAE,QAAQ;AAClB,QAAA,UAAU,EAAE,WAAW;KACxB;AACA,SAAA,IAAI,EAAE;AAET,IAAA,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE;AAClB,QAAA,MAAM,IAAI,KAAK,CAAC,2BAA2B,oBAAoB,CAAA,CAAE,CAAC;IACpE;IAEA,MAAM,mBAAmB,GAAyB,EAAE;AAEpD,IAAA,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE;AAC3B,QAAA,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC;YAAE;AACzB,QAAA,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;AAElE,QAAA,IAAI,CAAC,8BAA8B,CAAC,gBAAgB,CAAC,EAAE;YACrD,MAAM,IAAI,KAAK,CAAC,CAAA,sDAAA,EAAyD,OAAO,CAAC,KAAK,CAAA,CAAE,CAAC;QAC3F;QAEA,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,MAAM,CAAC,gBAAgB,CAAC;AACtE,QAAA,mBAAmB,CAAC,IAAI,CAAC,kBAAkB,CAAC;IAC9C;AAEA,IAAA,OAAO,mBAAmB;AAC5B;;;;"}
1
+ {"version":3,"file":"getSolanaAccount.js","sources":["../../../../src/adapters/solana/utils/getSolanaAccount.ts"],"sourcesContent":["import { getSolanaRpc } from './solanaRpc'\nimport { address } from '@solana/kit'\nimport {\n bondIssuanceDecoder,\n verifyBondIssuanceDiscriminator,\n bondDecoder,\n verifyBondDiscriminator,\n bondTermDecoder,\n verifyBondTermDiscriminator,\n bondPricingDecoder,\n verifyBondPricingDiscriminator,\n BondIssuanceAccount,\n BondAccount,\n BondTermAccount,\n BondPricingAccount,\n} from './codecs'\nimport { Buffer } from 'buffer'\n\nexport async function getBondIssuanceAccountSolana(accountAddresses: string[]) {\n const rpc = getSolanaRpc()\n const bondIssuanceAddresses = accountAddresses.map((a) => address(a))\n\n const { value } = await rpc\n .getMultipleAccounts(bondIssuanceAddresses, {\n encoding: 'base64',\n commitment: 'confirmed',\n })\n .send()\n\n if (!value?.length) {\n throw new Error(`Accounts not found for: ${bondIssuanceAddresses}`)\n }\n\n const bondIssuanceAccounts: BondIssuanceAccount[] = []\n\n for (const account of value) {\n if (!account?.data?.[0]) return\n const accountDataBytes = Buffer.from(account?.data?.[0], 'base64')\n\n if (!verifyBondIssuanceDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected BondIssuance account at ${account.owner}`)\n }\n\n const bondIssuanceAccount = bondIssuanceDecoder.decode(accountDataBytes)\n bondIssuanceAccounts.push(bondIssuanceAccount)\n }\n\n return bondIssuanceAccounts\n}\n\nexport async function getBondAccountSolana(accountAddresses: string[]) {\n const rpc = getSolanaRpc()\n const bondAddresses = accountAddresses.map((a) => address(a))\n\n const { value } = await rpc\n .getMultipleAccounts(bondAddresses, {\n encoding: 'base64',\n commitment: 'confirmed',\n })\n .send()\n\n if (!value?.length) {\n throw new Error(`Accounts not found for: ${bondAddresses}`)\n }\n\n const bondAccounts: BondAccount[] = []\n\n for (const account of value) {\n if (!account?.data?.[0]) return\n const accountDataBytes = Buffer.from(account?.data?.[0], 'base64')\n\n if (!verifyBondDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected Bond account at ${account.owner}`)\n }\n\n const bondAccount = bondDecoder.decode(accountDataBytes)\n bondAccounts.push(bondAccount)\n }\n\n return bondAccounts\n}\n\n// getMultipleAccounts accepts up to 100 addresses per request\nconst MAX_ACCOUNTS_PER_REQUEST = 100\n\n/**\n * Batched version of getBondAccountSolana: the result is aligned with `accountAddresses` (a missing account yields\n * `null` instead of aborting the whole batch) and requests are chunked to the RPC limit and sent in parallel.\n */\nexport async function getBondAccountsSolanaAligned(accountAddresses: string[]): Promise<(BondAccount | null)[]> {\n const rpc = getSolanaRpc()\n const chunks: string[][] = []\n for (let i = 0; i < accountAddresses.length; i += MAX_ACCOUNTS_PER_REQUEST) {\n chunks.push(accountAddresses.slice(i, i + MAX_ACCOUNTS_PER_REQUEST))\n }\n\n const decodedChunks = await Promise.all(\n chunks.map(async (chunk) => {\n const { value } = await rpc\n .getMultipleAccounts(\n chunk.map((a) => address(a)),\n { encoding: 'base64', commitment: 'confirmed' },\n )\n .send()\n\n return value.map((account, i) => {\n if (!account?.data?.[0]) return null\n const accountDataBytes = Buffer.from(account.data[0], 'base64')\n if (!verifyBondDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected Bond account at ${chunk[i]}`)\n }\n return bondDecoder.decode(accountDataBytes)\n })\n }),\n )\n\n return decodedChunks.flat()\n}\n\nexport async function getBondTermAccountSolana(accountAddresses: string[]) {\n const rpc = getSolanaRpc()\n const bondTermAddresses = accountAddresses.map((a) => address(a))\n\n const { value } = await rpc\n .getMultipleAccounts(bondTermAddresses, {\n encoding: 'base64',\n commitment: 'confirmed',\n })\n .send()\n\n if (!value?.length) {\n throw new Error(`Accounts not found for: ${bondTermAddresses}`)\n }\n\n const bondTermAccounts: BondTermAccount[] = []\n\n for (const account of value) {\n if (!account?.data?.[0]) return\n const accountDataBytes = Buffer.from(account?.data?.[0], 'base64')\n\n if (!verifyBondTermDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected BondTerm account at ${account.owner}`)\n }\n\n const bondTermAccount = bondTermDecoder.decode(accountDataBytes)\n bondTermAccounts.push(bondTermAccount)\n }\n\n return bondTermAccounts\n}\n\nexport async function getBondPricingAccountSolana(accountAddresses: string[]) {\n const rpc = getSolanaRpc()\n const bondPricingAddresses = accountAddresses.map((a) => address(a))\n\n const { value } = await rpc\n .getMultipleAccounts(bondPricingAddresses, {\n encoding: 'base64',\n commitment: 'confirmed',\n })\n .send()\n\n if (!value?.length) {\n throw new Error(`Accounts not found for: ${bondPricingAddresses}`)\n }\n\n const bondPricingAccounts: BondPricingAccount[] = []\n\n for (const account of value) {\n if (!account?.data?.[0]) return\n const accountDataBytes = Buffer.from(account?.data?.[0], 'base64')\n\n if (!verifyBondPricingDiscriminator(accountDataBytes)) {\n throw new Error(`Invalid account type. Expected BondPricing account at ${account.owner}`)\n }\n\n const bondPricingAccount = bondPricingDecoder.decode(accountDataBytes)\n bondPricingAccounts.push(bondPricingAccount)\n }\n\n return bondPricingAccounts\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { PublicKey } from '@solana/web3.js';
2
- import { getBondIssuanceAccountSolana, getBondPricingAccountSolana, getBondTermAccountSolana } from './getSolanaAccount.js';
2
+ import { getBondAccountsSolanaAligned, getBondIssuanceAccountSolana, getBondPricingAccountSolana, getBondTermAccountSolana } from './getSolanaAccount.js';
3
3
 
4
4
  /**
5
5
  * Reads multiple Anchor accounts in one batch from multiple programs
@@ -16,7 +16,9 @@ async function solanaMulticall(calls) {
16
16
  }, {});
17
17
  for (const [accountType, callIds] of Object.entries(callIdsGroupedByAccountType)) {
18
18
  if (accountType === 'bond') {
19
- results[accountType] = await getBondIssuanceAccountSolana(callIds.map((id) => id.toBase58()));
19
+ // Aligned with the calls (null for a missing account), so it skips the `[0] === null` reset below
20
+ results[accountType] = await getBondAccountsSolanaAligned(callIds.map((id) => id.toBase58()));
21
+ continue;
20
22
  }
21
23
  if (accountType === 'bondIssuance') {
22
24
  results[accountType] = await getBondIssuanceAccountSolana(callIds.map((id) => id.toBase58()));
@@ -1 +1 @@
1
- {"version":3,"file":"solanaMulticall.js","sources":["../../../../src/adapters/solana/utils/solanaMulticall.ts"],"sourcesContent":["import { PublicKey } from '@solana/web3.js'\nimport { getBondIssuanceAccountSolana, getBondPricingAccountSolana, getBondTermAccountSolana } from './getSolanaAccount'\n\nexport interface SolanaCall {\n accountAddress: string\n accountType: 'bond' | 'bondIssuance' | 'bondPricing' | 'bondTerm'\n}\n\n/**\n * Reads multiple Anchor accounts in one batch from multiple programs\n */\nexport async function solanaMulticall(calls: SolanaCall[]): Promise<Record<string, any>> {\n try {\n const results: Record<string, any> = {}\n\n const callIdsGroupedByAccountType = calls.reduce(\n (acc, call) => {\n if (!acc[call.accountType]) {\n acc[call.accountType] = []\n }\n acc[call.accountType].push(new PublicKey(call.accountAddress))\n return acc\n },\n {} as Record<string, PublicKey[]>,\n )\n\n for (const [accountType, callIds] of Object.entries(callIdsGroupedByAccountType)) {\n if (accountType === 'bond') {\n results[accountType] = await getBondIssuanceAccountSolana(callIds.map((id) => id.toBase58()))\n }\n if (accountType === 'bondIssuance') {\n results[accountType] = await getBondIssuanceAccountSolana(callIds.map((id) => id.toBase58()))\n }\n if (accountType === 'bondPricing') {\n results[accountType] = await getBondPricingAccountSolana(callIds.map((id) => id.toBase58()))\n }\n if (accountType === 'bondTerm') {\n results[accountType] = await getBondTermAccountSolana(callIds.map((id) => id.toBase58()))\n }\n if (results?.[accountType]?.[0] === null) {\n results[accountType] = []\n }\n }\n\n return results\n } catch (e) {\n console.error(e)\n return {}\n }\n}\n"],"names":[],"mappings":";;;AAQA;;AAEG;AACI,eAAe,eAAe,CAAC,KAAmB,EAAA;AACvD,IAAA,IAAI;QACF,MAAM,OAAO,GAAwB,EAAE;QAEvC,MAAM,2BAA2B,GAAG,KAAK,CAAC,MAAM,CAC9C,CAAC,GAAG,EAAE,IAAI,KAAI;YACZ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;AAC1B,gBAAA,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;YAC5B;AACA,YAAA,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AAC9D,YAAA,OAAO,GAAG;QACZ,CAAC,EACD,EAAiC,CAClC;AAED,QAAA,KAAK,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,2BAA2B,CAAC,EAAE;AAChF,YAAA,IAAI,WAAW,KAAK,MAAM,EAAE;gBAC1B,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,4BAA4B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC/F;AACA,YAAA,IAAI,WAAW,KAAK,cAAc,EAAE;gBAClC,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,4BAA4B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC/F;AACA,YAAA,IAAI,WAAW,KAAK,aAAa,EAAE;gBACjC,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,2BAA2B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC9F;AACA,YAAA,IAAI,WAAW,KAAK,UAAU,EAAE;gBAC9B,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC3F;AACA,YAAA,IAAI,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;AACxC,gBAAA,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE;YAC3B;QACF;AAEA,QAAA,OAAO,OAAO;IAChB;IAAE,OAAO,CAAC,EAAE;AACV,QAAA,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AAChB,QAAA,OAAO,EAAE;IACX;AACF;;;;"}
1
+ {"version":3,"file":"solanaMulticall.js","sources":["../../../../src/adapters/solana/utils/solanaMulticall.ts"],"sourcesContent":["import { PublicKey } from '@solana/web3.js'\nimport {\n getBondAccountsSolanaAligned,\n getBondIssuanceAccountSolana,\n getBondPricingAccountSolana,\n getBondTermAccountSolana,\n} from './getSolanaAccount'\n\nexport interface SolanaCall {\n accountAddress: string\n accountType: 'bond' | 'bondIssuance' | 'bondPricing' | 'bondTerm'\n}\n\n/**\n * Reads multiple Anchor accounts in one batch from multiple programs\n */\nexport async function solanaMulticall(calls: SolanaCall[]): Promise<Record<string, any>> {\n try {\n const results: Record<string, any> = {}\n\n const callIdsGroupedByAccountType = calls.reduce(\n (acc, call) => {\n if (!acc[call.accountType]) {\n acc[call.accountType] = []\n }\n acc[call.accountType].push(new PublicKey(call.accountAddress))\n return acc\n },\n {} as Record<string, PublicKey[]>,\n )\n\n for (const [accountType, callIds] of Object.entries(callIdsGroupedByAccountType)) {\n if (accountType === 'bond') {\n // Aligned with the calls (null for a missing account), so it skips the `[0] === null` reset below\n results[accountType] = await getBondAccountsSolanaAligned(callIds.map((id) => id.toBase58()))\n continue\n }\n if (accountType === 'bondIssuance') {\n results[accountType] = await getBondIssuanceAccountSolana(callIds.map((id) => id.toBase58()))\n }\n if (accountType === 'bondPricing') {\n results[accountType] = await getBondPricingAccountSolana(callIds.map((id) => id.toBase58()))\n }\n if (accountType === 'bondTerm') {\n results[accountType] = await getBondTermAccountSolana(callIds.map((id) => id.toBase58()))\n }\n if (results?.[accountType]?.[0] === null) {\n results[accountType] = []\n }\n }\n\n return results\n } catch (e) {\n console.error(e)\n return {}\n }\n}\n"],"names":[],"mappings":";;;AAaA;;AAEG;AACI,eAAe,eAAe,CAAC,KAAmB,EAAA;AACvD,IAAA,IAAI;QACF,MAAM,OAAO,GAAwB,EAAE;QAEvC,MAAM,2BAA2B,GAAG,KAAK,CAAC,MAAM,CAC9C,CAAC,GAAG,EAAE,IAAI,KAAI;YACZ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;AAC1B,gBAAA,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE;YAC5B;AACA,YAAA,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AAC9D,YAAA,OAAO,GAAG;QACZ,CAAC,EACD,EAAiC,CAClC;AAED,QAAA,KAAK,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,2BAA2B,CAAC,EAAE;AAChF,YAAA,IAAI,WAAW,KAAK,MAAM,EAAE;;gBAE1B,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,4BAA4B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC7F;YACF;AACA,YAAA,IAAI,WAAW,KAAK,cAAc,EAAE;gBAClC,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,4BAA4B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC/F;AACA,YAAA,IAAI,WAAW,KAAK,aAAa,EAAE;gBACjC,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,2BAA2B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC9F;AACA,YAAA,IAAI,WAAW,KAAK,UAAU,EAAE;gBAC9B,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC3F;AACA,YAAA,IAAI,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;AACxC,gBAAA,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE;YAC3B;QACF;AAEA,QAAA,OAAO,OAAO;IAChB;IAAE,OAAO,CAAC,EAAE;AACV,QAAA,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AAChB,QAAA,OAAO,EAAE;IACX;AACF;;;;"}
@@ -73,7 +73,7 @@ export interface ChainAdapter {
73
73
  usesRealtimeApi: boolean;
74
74
  /** For adapters that use the realtime API this is only the fallback path, used when that API fails. */
75
75
  fetchBondsData(chainId: ChainId, bonds: BillsConfig[], tokenPrices: TokenPrices[] | undefined, apiUrl: string): Promise<BondsData[]>;
76
- fetchUserBonds(chainId: ChainId, account: string, bonds: BillsConfig[], tokenPrices: Partial<Record<ChainId, TokenPrices[]>>): Promise<UserOwnedBillsData[]>;
76
+ fetchUserBonds(chainId: ChainId, account: string, bonds: BillsConfig[], tokenPrices: Partial<Record<ChainId, TokenPrices[]>>, apiUrl: string): Promise<UserOwnedBillsData[]>;
77
77
  /** false: skip the api-v2 /bills/multi-batch NFT metadata enrichment for this family's bills. */
78
78
  enrichUserBondsViaApi: boolean;
79
79
  getTokenBalance(chainId: ChainId, currency: 'NATIVE' | Token, account: string): Promise<string | null>;
@@ -1,5 +1,5 @@
1
1
  import { ChainId } from '@ape.swap/apeswap-lists';
2
2
  export declare const PUBLIC_RPC_URLS: Partial<Record<ChainId, string[]>>;
3
- export declare const HELIUS_SOL_RPC: string;
3
+ export declare const getShuffledHeliusRPCs: () => string[];
4
4
  export declare const setCustomRPCS: (urls: Partial<Record<ChainId, string[]>>) => void;
5
5
  export declare const getRPC: (chain: ChainId) => string;
@@ -84,13 +84,15 @@ const HELIUS_SOL_RPC_ENDPOINTS = [
84
84
  'https://mainnet.helius-rpc.com/?api-key=a5c4ea36-fa6b-4bcc-a7a4-e10012b9ea7e', // RandomApe personal mail
85
85
  ];
86
86
  // export const HELIUS_SOL_RPC_ENDPOINTS = ['https://devnet.helius-rpc.com/?api-key=095f6fc7-1be0-4d94-a737-4a5ee0d82ea5'] //devnet
87
- // Function to get a random Helius SOL RPC endpoint
88
- const getRandomHeliusRPC = () => {
89
- const randomIndex = Math.floor(Math.random() * HELIUS_SOL_RPC_ENDPOINTS.length);
90
- return HELIUS_SOL_RPC_ENDPOINTS[randomIndex];
87
+ // Helius endpoints in random order, so load spreads across the keys and a retry can use a different one
88
+ const getShuffledHeliusRPCs = () => {
89
+ const endpoints = [...HELIUS_SOL_RPC_ENDPOINTS];
90
+ for (let i = endpoints.length - 1; i > 0; i--) {
91
+ const j = Math.floor(Math.random() * (i + 1));
92
+ [endpoints[i], endpoints[j]] = [endpoints[j], endpoints[i]];
93
+ }
94
+ return endpoints;
91
95
  };
92
- // Keep the current export for backward compatibility
93
- const HELIUS_SOL_RPC = getRandomHeliusRPC();
94
96
  // Custom RPC registry — set by the consumer via the `customRPCS` prop
95
97
  let _customRPCS = {};
96
98
  const setCustomRPCS = (urls) => {
@@ -100,5 +102,5 @@ const getRPC = (chain) => {
100
102
  return _customRPCS?.[chain]?.[0] ?? PUBLIC_RPC_URLS?.[chain]?.[0];
101
103
  };
102
104
 
103
- export { HELIUS_SOL_RPC, PUBLIC_RPC_URLS, getRPC, setCustomRPCS };
105
+ export { PUBLIC_RPC_URLS, getRPC, getShuffledHeliusRPCs, setCustomRPCS };
104
106
  //# sourceMappingURL=networks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"networks.js","sources":["../../../src/config/constants/networks.ts"],"sourcesContent":["import { ChainId } from '@ape.swap/apeswap-lists'\nimport { APTOS_FULLNODE_URL } from '../../constants/aptosConstants'\n\nexport const PUBLIC_RPC_URLS: Partial<Record<ChainId, string[]>> = {\n [ChainId.BSC]: [\n 'https://bsc-dataseed.binance.org/',\n 'https://bsc-dataseed1.defibit.io',\n 'https://endpoints.omniatech.io/v1/bsc/mainnet/public',\n ],\n [ChainId.ARBITRUM]: ['https://arb1.arbitrum.io/rpc'],\n [ChainId.BSC_TESTNET]: ['https://data-seed-prebsc-2-s3.binance.org:8545/'],\n [ChainId.MATIC]: [\n 'https://polygon.drpc.org',\n 'https://1rpc.io/matic',\n 'https://polygon-rpc.com/', // this is now freemium\n 'https://polygon-mainnet.public.blastapi.io/',\n 'https://rpc-mainnet.matic.network',\n 'https://matic-mainnet.chainstacklabs.com',\n ],\n [ChainId.MAINNET]: [\n 'https://ethereum-rpc.publicnode.com',\n 'https://eth.drpc.org',\n 'https://eth-mainnet.public.blastapi.io',\n 'https://rpc.ankr.com/eth',\n ],\n [ChainId.TLOS]: ['https://mainnet.telos.net/evm'],\n [ChainId.INEVM]: ['https://mainnet.rpc.inevm.com/http'],\n [ChainId.SEPOLIA]: ['https://eth-sepolia-public.unifra.io'],\n [ChainId.LINEA]: [\n 'https://rpc.linea.build',\n 'https://linea.drpc.org',\n 'https://linea.blockpi.network/v1/rpc/public',\n 'https://linea.decubate.com',\n ],\n [ChainId.LIGHTLINK]: [\n 'https://replicator.phoenix.lightlink.io/rpc/v1',\n 'https://endpoints.omniatech.io/v1/lightlink/phoenix/public',\n ],\n [ChainId.IOTA]: ['https://json-rpc.evm.iotaledger.net/'],\n [ChainId.IOTA_TESTNET]: ['https://json-rpc.evm.testnet.iotaledger.net/'],\n [ChainId.BASE]: ['https://base-rpc.publicnode.com', 'https://mainnet.base.org'],\n [ChainId.GRAPHLINQ]: ['https://glq-dataseed.graphlinq.io'],\n [ChainId.AVAX]: ['https://avalanche-c-chain-rpc.publicnode.com', 'https://avax.meowrpc.com'],\n [ChainId.BLAST]: ['https://rpc.blast.io'],\n [ChainId.CROSSFI]: ['https://rpc.mainnet.ms'],\n [ChainId.MONAD_TESTNET]: ['https://testnet-rpc2.monad.xyz/52227f026fa8fac9e2014c58fbf5643369b3bfc6'],\n [ChainId.MONAD]: [\n 'https://rpc.monad.xyz',\n 'https://rpc1.monad.xyz',\n 'https://rpc2.monad.xyz',\n 'https://rpc3.monad.xyz',\n 'https://rpc4.monad.xyz',\n ],\n [ChainId.SONIC]: ['https://rpc.soniclabs.com'],\n [ChainId.BERACHAIN]: ['https://rpc.berachain.com', 'https://berachain.drpc.org'],\n [ChainId.KATANA]: ['https://rpc-katana.t.conduit.xyz/NRcSLqEG4Hw55GzTSu6Sratq94kKtUGfH'],\n [ChainId.UNICHAIN]: ['https://unichain-rpc.publicnode.com', 'https://unichain.drpc.org'],\n [ChainId.MEGAETH_TESTNET]: ['https://carrot.megaeth.com/rpc'],\n [ChainId.HYPEREVM]: ['https://rpc.hyperliquid.xyz/evm'],\n [ChainId.BITTENSOR_TESTNET]: ['https://bittensor-testnet-lite-public.nodies.app'],\n [ChainId.ROBINHOOD]: ['https://rpc.mainnet.chain.robinhood.com'],\n [ChainId.L1X]: ['https://v2-mainnet-rpc.l1x.foundation'],\n\n // Aptos fullnode — URL is clean; the API key goes in the Authorization\n // header, not as a query param (see aptosConstants.ts). The cast on\n // APTOS_CHAIN_ID is required because the synthetic id is not in the\n // ChainId enum yet.\n [ChainId.APTOS]: [APTOS_FULLNODE_URL],\n\n // Sui fullnode — public RPC, no API key required (see suiConstants.ts).\n [ChainId.SUI]: ['https://fullnode.mainnet.sui.io:443'],\n\n // SOL RPC is not public. Production URL (morning-autumn-dream) is domain-protected, development URL (ancient-cold-sailboat) is free, use with caution.\n [ChainId.SOL]:\n process.env.NODE_ENV === 'production'\n ? ['https://morning-autumn-dream.solana-mainnet.quiknode.pro/1ef6069950c798a43a112c0fe5eb157309427db8']\n : [\n 'https://ancient-cold-sailboat.solana-mainnet.quiknode.pro/da13400ad8d0035313f3e71c6a37c107d0d0334b',\n 'https://mainnet.helius-rpc.com/?api-key=d80b11e0-692f-4da0-91d6-830799c8c385',\n ], // mainnet\n // [ChainId.SOL]: ['https://devnet.helius-rpc.com/?api-key=095f6fc7-1be0-4d94-a737-4a5ee0d82ea5'], // devnet\n}\n\n// Multiple Helius SOL RPC endpoints for rotation\nconst HELIUS_SOL_RPC_ENDPOINTS = [\n 'https://mainnet.helius-rpc.com/?api-key=d80b11e0-692f-4da0-91d6-830799c8c385', // apefede09@gmail.com\n 'https://mainnet.helius-rpc.com/?api-key=8631ce42-9000-490d-9ac3-f3a81de508b2', // apebond.design@gmail.com\n 'https://mainnet.helius-rpc.com/?api-key=c097651d-ae13-4ab4-ae4c-fef7e48d4561', // n0mbr3al3at0r10@gmail.com\n 'https://mainnet.helius-rpc.com/?api-key=a5c4ea36-fa6b-4bcc-a7a4-e10012b9ea7e', // RandomApe personal mail\n]\n\n// export const HELIUS_SOL_RPC_ENDPOINTS = ['https://devnet.helius-rpc.com/?api-key=095f6fc7-1be0-4d94-a737-4a5ee0d82ea5'] //devnet\n\n// Function to get a random Helius SOL RPC endpoint\nconst getRandomHeliusRPC = (): string => {\n const randomIndex = Math.floor(Math.random() * HELIUS_SOL_RPC_ENDPOINTS.length)\n return HELIUS_SOL_RPC_ENDPOINTS[randomIndex]\n}\n\n// Keep the current export for backward compatibility\nexport const HELIUS_SOL_RPC = getRandomHeliusRPC()\n\n// Custom RPC registry — set by the consumer via the `customRPCS` prop\nlet _customRPCS: Partial<Record<ChainId, string[]>> = {}\n\nexport const setCustomRPCS = (urls: Partial<Record<ChainId, string[]>>) => {\n _customRPCS = urls\n}\n\nexport const getRPC = (chain: ChainId): string => {\n return _customRPCS?.[chain]?.[0] ?? (PUBLIC_RPC_URLS?.[chain]?.[0] as string)\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"networks.js","sources":["../../../src/config/constants/networks.ts"],"sourcesContent":["import { ChainId } from '@ape.swap/apeswap-lists'\nimport { APTOS_FULLNODE_URL } from '../../constants/aptosConstants'\n\nexport const PUBLIC_RPC_URLS: Partial<Record<ChainId, string[]>> = {\n [ChainId.BSC]: [\n 'https://bsc-dataseed.binance.org/',\n 'https://bsc-dataseed1.defibit.io',\n 'https://endpoints.omniatech.io/v1/bsc/mainnet/public',\n ],\n [ChainId.ARBITRUM]: ['https://arb1.arbitrum.io/rpc'],\n [ChainId.BSC_TESTNET]: ['https://data-seed-prebsc-2-s3.binance.org:8545/'],\n [ChainId.MATIC]: [\n 'https://polygon.drpc.org',\n 'https://1rpc.io/matic',\n 'https://polygon-rpc.com/', // this is now freemium\n 'https://polygon-mainnet.public.blastapi.io/',\n 'https://rpc-mainnet.matic.network',\n 'https://matic-mainnet.chainstacklabs.com',\n ],\n [ChainId.MAINNET]: [\n 'https://ethereum-rpc.publicnode.com',\n 'https://eth.drpc.org',\n 'https://eth-mainnet.public.blastapi.io',\n 'https://rpc.ankr.com/eth',\n ],\n [ChainId.TLOS]: ['https://mainnet.telos.net/evm'],\n [ChainId.INEVM]: ['https://mainnet.rpc.inevm.com/http'],\n [ChainId.SEPOLIA]: ['https://eth-sepolia-public.unifra.io'],\n [ChainId.LINEA]: [\n 'https://rpc.linea.build',\n 'https://linea.drpc.org',\n 'https://linea.blockpi.network/v1/rpc/public',\n 'https://linea.decubate.com',\n ],\n [ChainId.LIGHTLINK]: [\n 'https://replicator.phoenix.lightlink.io/rpc/v1',\n 'https://endpoints.omniatech.io/v1/lightlink/phoenix/public',\n ],\n [ChainId.IOTA]: ['https://json-rpc.evm.iotaledger.net/'],\n [ChainId.IOTA_TESTNET]: ['https://json-rpc.evm.testnet.iotaledger.net/'],\n [ChainId.BASE]: ['https://base-rpc.publicnode.com', 'https://mainnet.base.org'],\n [ChainId.GRAPHLINQ]: ['https://glq-dataseed.graphlinq.io'],\n [ChainId.AVAX]: ['https://avalanche-c-chain-rpc.publicnode.com', 'https://avax.meowrpc.com'],\n [ChainId.BLAST]: ['https://rpc.blast.io'],\n [ChainId.CROSSFI]: ['https://rpc.mainnet.ms'],\n [ChainId.MONAD_TESTNET]: ['https://testnet-rpc2.monad.xyz/52227f026fa8fac9e2014c58fbf5643369b3bfc6'],\n [ChainId.MONAD]: [\n 'https://rpc.monad.xyz',\n 'https://rpc1.monad.xyz',\n 'https://rpc2.monad.xyz',\n 'https://rpc3.monad.xyz',\n 'https://rpc4.monad.xyz',\n ],\n [ChainId.SONIC]: ['https://rpc.soniclabs.com'],\n [ChainId.BERACHAIN]: ['https://rpc.berachain.com', 'https://berachain.drpc.org'],\n [ChainId.KATANA]: ['https://rpc-katana.t.conduit.xyz/NRcSLqEG4Hw55GzTSu6Sratq94kKtUGfH'],\n [ChainId.UNICHAIN]: ['https://unichain-rpc.publicnode.com', 'https://unichain.drpc.org'],\n [ChainId.MEGAETH_TESTNET]: ['https://carrot.megaeth.com/rpc'],\n [ChainId.HYPEREVM]: ['https://rpc.hyperliquid.xyz/evm'],\n [ChainId.BITTENSOR_TESTNET]: ['https://bittensor-testnet-lite-public.nodies.app'],\n [ChainId.ROBINHOOD]: ['https://rpc.mainnet.chain.robinhood.com'],\n [ChainId.L1X]: ['https://v2-mainnet-rpc.l1x.foundation'],\n\n // Aptos fullnode — URL is clean; the API key goes in the Authorization\n // header, not as a query param (see aptosConstants.ts). The cast on\n // APTOS_CHAIN_ID is required because the synthetic id is not in the\n // ChainId enum yet.\n [ChainId.APTOS]: [APTOS_FULLNODE_URL],\n\n // Sui fullnode — public RPC, no API key required (see suiConstants.ts).\n [ChainId.SUI]: ['https://fullnode.mainnet.sui.io:443'],\n\n // SOL RPC is not public. Production URL (morning-autumn-dream) is domain-protected, development URL (ancient-cold-sailboat) is free, use with caution.\n [ChainId.SOL]:\n process.env.NODE_ENV === 'production'\n ? ['https://morning-autumn-dream.solana-mainnet.quiknode.pro/1ef6069950c798a43a112c0fe5eb157309427db8']\n : [\n 'https://ancient-cold-sailboat.solana-mainnet.quiknode.pro/da13400ad8d0035313f3e71c6a37c107d0d0334b',\n 'https://mainnet.helius-rpc.com/?api-key=d80b11e0-692f-4da0-91d6-830799c8c385',\n ], // mainnet\n // [ChainId.SOL]: ['https://devnet.helius-rpc.com/?api-key=095f6fc7-1be0-4d94-a737-4a5ee0d82ea5'], // devnet\n}\n\n// Multiple Helius SOL RPC endpoints for rotation\nconst HELIUS_SOL_RPC_ENDPOINTS = [\n 'https://mainnet.helius-rpc.com/?api-key=d80b11e0-692f-4da0-91d6-830799c8c385', // apefede09@gmail.com\n 'https://mainnet.helius-rpc.com/?api-key=8631ce42-9000-490d-9ac3-f3a81de508b2', // apebond.design@gmail.com\n 'https://mainnet.helius-rpc.com/?api-key=c097651d-ae13-4ab4-ae4c-fef7e48d4561', // n0mbr3al3at0r10@gmail.com\n 'https://mainnet.helius-rpc.com/?api-key=a5c4ea36-fa6b-4bcc-a7a4-e10012b9ea7e', // RandomApe personal mail\n]\n\n// export const HELIUS_SOL_RPC_ENDPOINTS = ['https://devnet.helius-rpc.com/?api-key=095f6fc7-1be0-4d94-a737-4a5ee0d82ea5'] //devnet\n\n// Helius endpoints in random order, so load spreads across the keys and a retry can use a different one\nexport const getShuffledHeliusRPCs = (): string[] => {\n const endpoints = [...HELIUS_SOL_RPC_ENDPOINTS]\n for (let i = endpoints.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[endpoints[i], endpoints[j]] = [endpoints[j], endpoints[i]]\n }\n return endpoints\n}\n\n// Custom RPC registry — set by the consumer via the `customRPCS` prop\nlet _customRPCS: Partial<Record<ChainId, string[]>> = {}\n\nexport const setCustomRPCS = (urls: Partial<Record<ChainId, string[]>>) => {\n _customRPCS = urls\n}\n\nexport const getRPC = (chain: ChainId): string => {\n return _customRPCS?.[chain]?.[0] ?? (PUBLIC_RPC_URLS?.[chain]?.[0] as string)\n}\n"],"names":[],"mappings":";;;AAGO,MAAM,eAAe,GAAuC;AACjE,IAAA,CAAC,OAAO,CAAC,GAAG,GAAG;QACb,mCAAmC;QACnC,kCAAkC;QAClC,sDAAsD;AACvD,KAAA;AACD,IAAA,CAAC,OAAO,CAAC,QAAQ,GAAG,CAAC,8BAA8B,CAAC;AACpD,IAAA,CAAC,OAAO,CAAC,WAAW,GAAG,CAAC,iDAAiD,CAAC;AAC1E,IAAA,CAAC,OAAO,CAAC,KAAK,GAAG;QACf,0BAA0B;QAC1B,uBAAuB;AACvB,QAAA,0BAA0B;QAC1B,6CAA6C;QAC7C,mCAAmC;QACnC,0CAA0C;AAC3C,KAAA;AACD,IAAA,CAAC,OAAO,CAAC,OAAO,GAAG;QACjB,qCAAqC;QACrC,sBAAsB;QACtB,wCAAwC;QACxC,0BAA0B;AAC3B,KAAA;AACD,IAAA,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,+BAA+B,CAAC;AACjD,IAAA,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,oCAAoC,CAAC;AACvD,IAAA,CAAC,OAAO,CAAC,OAAO,GAAG,CAAC,sCAAsC,CAAC;AAC3D,IAAA,CAAC,OAAO,CAAC,KAAK,GAAG;QACf,yBAAyB;QACzB,wBAAwB;QACxB,6CAA6C;QAC7C,4BAA4B;AAC7B,KAAA;AACD,IAAA,CAAC,OAAO,CAAC,SAAS,GAAG;QACnB,gDAAgD;QAChD,4DAA4D;AAC7D,KAAA;AACD,IAAA,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,sCAAsC,CAAC;AACxD,IAAA,CAAC,OAAO,CAAC,YAAY,GAAG,CAAC,8CAA8C,CAAC;IACxE,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,iCAAiC,EAAE,0BAA0B,CAAC;AAC/E,IAAA,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,mCAAmC,CAAC;IAC1D,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,8CAA8C,EAAE,0BAA0B,CAAC;AAC5F,IAAA,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,sBAAsB,CAAC;AACzC,IAAA,CAAC,OAAO,CAAC,OAAO,GAAG,CAAC,wBAAwB,CAAC;AAC7C,IAAA,CAAC,OAAO,CAAC,aAAa,GAAG,CAAC,yEAAyE,CAAC;AACpG,IAAA,CAAC,OAAO,CAAC,KAAK,GAAG;QACf,uBAAuB;QACvB,wBAAwB;QACxB,wBAAwB;QACxB,wBAAwB;QACxB,wBAAwB;AACzB,KAAA;AACD,IAAA,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,2BAA2B,CAAC;IAC9C,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,2BAA2B,EAAE,4BAA4B,CAAC;AAChF,IAAA,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,oEAAoE,CAAC;IACxF,CAAC,OAAO,CAAC,QAAQ,GAAG,CAAC,qCAAqC,EAAE,2BAA2B,CAAC;AACxF,IAAA,CAAC,OAAO,CAAC,eAAe,GAAG,CAAC,gCAAgC,CAAC;AAC7D,IAAA,CAAC,OAAO,CAAC,QAAQ,GAAG,CAAC,iCAAiC,CAAC;AACvD,IAAA,CAAC,OAAO,CAAC,iBAAiB,GAAG,CAAC,kDAAkD,CAAC;AACjF,IAAA,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,yCAAyC,CAAC;AAChE,IAAA,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,uCAAuC,CAAC;;;;;AAMxD,IAAA,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,kBAAkB,CAAC;;AAGrC,IAAA,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,qCAAqC,CAAC;;IAGtD,CAAC,OAAO,CAAC,GAAG,GACV,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK;UACrB,CAAC,mGAAmG;AACtG,UAAE;YACE,oGAAoG;YACpG,8EAA8E;AAC/E,SAAA;;;AAIT;AACA,MAAM,wBAAwB,GAAG;AAC/B,IAAA,8EAA8E;AAC9E,IAAA,8EAA8E;AAC9E,IAAA,8EAA8E;AAC9E,IAAA,8EAA8E;CAC/E;AAED;AAEA;AACO,MAAM,qBAAqB,GAAG,MAAe;AAClD,IAAA,MAAM,SAAS,GAAG,CAAC,GAAG,wBAAwB,CAAC;AAC/C,IAAA,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AAC7C,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5C,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAC9D;AACA,IAAA,OAAO,SAAS;AAClB;AAEA;AACA,IAAI,WAAW,GAAuC,EAAE;AAEjD,MAAM,aAAa,GAAG,CAAC,IAAwC,KAAI;IACxE,WAAW,GAAG,IAAI;AACpB;AAEO,MAAM,MAAM,GAAG,CAAC,KAAc,KAAY;AAC/C,IAAA,OAAO,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAK,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,CAAY;AAC/E;;;;"}
@@ -46,7 +46,7 @@ const getUserBonds = async (accounts, bondList, queryClient) => {
46
46
  // Chains without a registered adapter are unsupported
47
47
  const adapter = getAdapterForChain(chain);
48
48
  const account = adapter && accounts[adapter.ecosystem];
49
- return adapter && account ? adapter.fetchUserBonds(chain, account, bondList[chain], tokenPrices) : [];
49
+ return adapter && account ? adapter.fetchUserBonds(chain, account, bondList[chain], tokenPrices, apiUrl) : [];
50
50
  }));
51
51
  // Filter out only fulfilled promises
52
52
  const fulfilledResults = results
@@ -1 +1 @@
1
- {"version":3,"file":"useUserBonds.js","sources":["../../../src/state/bonds/useUserBonds.ts"],"sourcesContent":["import { QueryClient, useQuery, UseQueryResult } from '@tanstack/react-query'\nimport { QUERY_KEYS } from '../../config/constants/queryKeys'\nimport { BillsConfig, ChainId } from '@ape.swap/apeswap-lists'\nimport useBondsList from './useBondsList'\nimport { UserBill, UserBillNftData } from '../../types/yourbonds'\nimport { SDKProps } from '../useSDKConfig'\nimport useTokenPrices, { TokenPrices } from '../tokenPrices/useTokenPrices'\nimport { reportError } from '../../utils/reportError'\nimport useChainAccount from '../chainAccount/useChainAccount'\nimport { apiPost } from '../../utils/httpClient'\nimport { getAdapterForChain } from '../../adapters/registry'\nimport type { Ecosystem } from '../../adapters/types'\n\nexport default function useUserBonds(): UseQueryResult<UserBill[]> {\n // First fetch the full list of bonds\n const { data: bondList } = useBondsList()\n const { address: addressEVM } = useChainAccount('evm')\n const { address: addressSOL } = useChainAccount('sol')\n const { address: addressAptos } = useChainAccount('aptos')\n const { address: addressSUI } = useChainAccount('sui')\n const { data: tokenPrices } = useTokenPrices()\n return useQuery({\n queryKey: [\n QUERY_KEYS.USER_BONDS,\n addressEVM,\n addressSOL,\n addressAptos,\n addressSUI,\n Object.keys(tokenPrices ?? {})?.length ?? null,\n ],\n queryFn: ({ client }) =>\n getUserBonds({ evm: addressEVM, sol: addressSOL, aptos: addressAptos, sui: addressSUI }, bondList!, client),\n enabled: !!bondList && !!(addressEVM || addressSOL || addressAptos || addressSUI) && !!tokenPrices,\n refetchInterval: 120000,\n refetchOnWindowFocus: false,\n refetchOnMount: false,\n refetchOnReconnect: false,\n retry: 1,\n })\n}\n\n/** @param accounts the connected account of each family, by ecosystem (see useChainAccount) */\nexport const getUserBonds = async (\n accounts: Partial<Record<Ecosystem, string | null>>,\n bondList: Record<ChainId, BillsConfig[]>,\n queryClient: QueryClient,\n): Promise<UserBill[]> => {\n const { chains, urls } = queryClient?.getQueryData([QUERY_KEYS.SDK_CONFIG]) as SDKProps\n const apiUrl = urls['apiV2']\n const tokenPrices: Partial<Record<ChainId, TokenPrices[]>> = queryClient?.getQueryData([QUERY_KEYS.TOKEN_PRICES])!\n\n try {\n const results = await Promise.allSettled(\n chains\n .filter((chain: ChainId) => bondList[chain])\n .map((chain: ChainId) => {\n // Chains without a registered adapter are unsupported\n const adapter = getAdapterForChain(chain)\n const account = adapter && accounts[adapter.ecosystem]\n return adapter && account ? adapter.fetchUserBonds(chain, account, bondList[chain], tokenPrices) : []\n }),\n )\n\n // Filter out only fulfilled promises\n const fulfilledResults = results\n .filter((result): result is PromiseFulfilledResult<any> => result.status === 'fulfilled')\n .map((result) => result.value)\n\n const onChainData: UserBill[] = fulfilledResults.flat().flatMap((result) => result.userOwnedBills)\n\n // Prepare body for batch API call\n // api-v2 doesn't support adapters with enrichUserBondsViaApi: false (e.g. Aptos, Sui). Their bills are read\n // on-chain, with no /bills/multi-batch enrichment, and use the placeholder image from their constants.\n const enrichViaApi = (chainId?: ChainId) => getAdapterForChain(chainId as ChainId)?.enrichUserBondsViaApi ?? true\n const body = Object.values(\n onChainData\n .filter((data) => enrichViaApi(data?.bond?.chainId))\n .reduce(\n (acc, data) => {\n const contract =\n data?.bond?.chainId === ChainId.SOL ? data.bond.contractAddress[data.bond.chainId] : data.billNftAddress\n const key = `${data?.bond?.chainId}-${contract}`\n\n if (!acc[key]) {\n acc[key] = {\n chainId: data?.bond?.chainId as number,\n contract: contract as string,\n billIds: [],\n }\n }\n\n acc[key].billIds.push(parseInt(data.id))\n return acc\n },\n {} as Record<string, { chainId: number; contract: string; billIds: number[] }>,\n ),\n )\n let responseData\n\n try {\n responseData = await apiPost<any[]>(`${apiUrl}/bills/multi-batch`, body, { timeoutMs: 40000 })\n } catch (e) {\n console.error(e)\n return onChainData\n }\n // Enrich on-chain data with API data.\n // Bills that weren't sent to api-v2 keep the nftData their fetcher populated instead of being overwritten with\n // undefined.\n const enrichedData: UserBill[] = onChainData.map((userBond) => {\n if (!enrichViaApi(userBond.bond?.chainId)) return userBond\n\n const nftDataResponse = responseData?.find((nft: any) => {\n const contract = nft.contract.toLowerCase()\n const chain = nft.chainId\n\n // API uses contractAddress for Solana bonds instead of NFT Address\n const contractToCompare =\n chain === ChainId.SOL ? userBond.bond?.contractAddress[ChainId.SOL] : userBond.billNftAddress\n return contractToCompare?.toLowerCase() === contract && userBond.bond?.chainId === chain\n })\n const nftData: UserBillNftData | undefined = nftDataResponse?.bills?.find(\n (billApiData: UserBillNftData) => billApiData.tokenId === parseInt(userBond.id),\n )\n return {\n ...userBond,\n nftData,\n }\n })\n\n return enrichedData\n } catch (e) {\n console.error(e)\n reportError({\n apiUrl,\n error: e,\n extraInfo: { type: 'getUserBonds', bondList, chains, e },\n account: accounts.evm ?? undefined,\n })\n throw new Error('Failed to fetch user bonds')\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useUserBonds.js","sources":["../../../src/state/bonds/useUserBonds.ts"],"sourcesContent":["import { QueryClient, useQuery, UseQueryResult } from '@tanstack/react-query'\nimport { QUERY_KEYS } from '../../config/constants/queryKeys'\nimport { BillsConfig, ChainId } from '@ape.swap/apeswap-lists'\nimport useBondsList from './useBondsList'\nimport { UserBill, UserBillNftData } from '../../types/yourbonds'\nimport { SDKProps } from '../useSDKConfig'\nimport useTokenPrices, { TokenPrices } from '../tokenPrices/useTokenPrices'\nimport { reportError } from '../../utils/reportError'\nimport useChainAccount from '../chainAccount/useChainAccount'\nimport { apiPost } from '../../utils/httpClient'\nimport { getAdapterForChain } from '../../adapters/registry'\nimport type { Ecosystem } from '../../adapters/types'\n\nexport default function useUserBonds(): UseQueryResult<UserBill[]> {\n // First fetch the full list of bonds\n const { data: bondList } = useBondsList()\n const { address: addressEVM } = useChainAccount('evm')\n const { address: addressSOL } = useChainAccount('sol')\n const { address: addressAptos } = useChainAccount('aptos')\n const { address: addressSUI } = useChainAccount('sui')\n const { data: tokenPrices } = useTokenPrices()\n return useQuery({\n queryKey: [\n QUERY_KEYS.USER_BONDS,\n addressEVM,\n addressSOL,\n addressAptos,\n addressSUI,\n Object.keys(tokenPrices ?? {})?.length ?? null,\n ],\n queryFn: ({ client }) =>\n getUserBonds({ evm: addressEVM, sol: addressSOL, aptos: addressAptos, sui: addressSUI }, bondList!, client),\n enabled: !!bondList && !!(addressEVM || addressSOL || addressAptos || addressSUI) && !!tokenPrices,\n refetchInterval: 120000,\n refetchOnWindowFocus: false,\n refetchOnMount: false,\n refetchOnReconnect: false,\n retry: 1,\n })\n}\n\n/** @param accounts the connected account of each family, by ecosystem (see useChainAccount) */\nexport const getUserBonds = async (\n accounts: Partial<Record<Ecosystem, string | null>>,\n bondList: Record<ChainId, BillsConfig[]>,\n queryClient: QueryClient,\n): Promise<UserBill[]> => {\n const { chains, urls } = queryClient?.getQueryData([QUERY_KEYS.SDK_CONFIG]) as SDKProps\n const apiUrl = urls['apiV2']\n const tokenPrices: Partial<Record<ChainId, TokenPrices[]>> = queryClient?.getQueryData([QUERY_KEYS.TOKEN_PRICES])!\n\n try {\n const results = await Promise.allSettled(\n chains\n .filter((chain: ChainId) => bondList[chain])\n .map((chain: ChainId) => {\n // Chains without a registered adapter are unsupported\n const adapter = getAdapterForChain(chain)\n const account = adapter && accounts[adapter.ecosystem]\n return adapter && account ? adapter.fetchUserBonds(chain, account, bondList[chain], tokenPrices, apiUrl) : []\n }),\n )\n\n // Filter out only fulfilled promises\n const fulfilledResults = results\n .filter((result): result is PromiseFulfilledResult<any> => result.status === 'fulfilled')\n .map((result) => result.value)\n\n const onChainData: UserBill[] = fulfilledResults.flat().flatMap((result) => result.userOwnedBills)\n\n // Prepare body for batch API call\n // api-v2 doesn't support adapters with enrichUserBondsViaApi: false (e.g. Aptos, Sui). Their bills are read\n // on-chain, with no /bills/multi-batch enrichment, and use the placeholder image from their constants.\n const enrichViaApi = (chainId?: ChainId) => getAdapterForChain(chainId as ChainId)?.enrichUserBondsViaApi ?? true\n const body = Object.values(\n onChainData\n .filter((data) => enrichViaApi(data?.bond?.chainId))\n .reduce(\n (acc, data) => {\n const contract =\n data?.bond?.chainId === ChainId.SOL ? data.bond.contractAddress[data.bond.chainId] : data.billNftAddress\n const key = `${data?.bond?.chainId}-${contract}`\n\n if (!acc[key]) {\n acc[key] = {\n chainId: data?.bond?.chainId as number,\n contract: contract as string,\n billIds: [],\n }\n }\n\n acc[key].billIds.push(parseInt(data.id))\n return acc\n },\n {} as Record<string, { chainId: number; contract: string; billIds: number[] }>,\n ),\n )\n let responseData\n\n try {\n responseData = await apiPost<any[]>(`${apiUrl}/bills/multi-batch`, body, { timeoutMs: 40000 })\n } catch (e) {\n console.error(e)\n return onChainData\n }\n // Enrich on-chain data with API data.\n // Bills that weren't sent to api-v2 keep the nftData their fetcher populated instead of being overwritten with\n // undefined.\n const enrichedData: UserBill[] = onChainData.map((userBond) => {\n if (!enrichViaApi(userBond.bond?.chainId)) return userBond\n\n const nftDataResponse = responseData?.find((nft: any) => {\n const contract = nft.contract.toLowerCase()\n const chain = nft.chainId\n\n // API uses contractAddress for Solana bonds instead of NFT Address\n const contractToCompare =\n chain === ChainId.SOL ? userBond.bond?.contractAddress[ChainId.SOL] : userBond.billNftAddress\n return contractToCompare?.toLowerCase() === contract && userBond.bond?.chainId === chain\n })\n const nftData: UserBillNftData | undefined = nftDataResponse?.bills?.find(\n (billApiData: UserBillNftData) => billApiData.tokenId === parseInt(userBond.id),\n )\n return {\n ...userBond,\n nftData,\n }\n })\n\n return enrichedData\n } catch (e) {\n console.error(e)\n reportError({\n apiUrl,\n error: e,\n extraInfo: { type: 'getUserBonds', bondList, chains, e },\n account: accounts.evm ?? undefined,\n })\n throw new Error('Failed to fetch user bonds')\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ape Bond SDK",
4
4
  "author": "Ape Bond",
5
5
  "license": "MIT",
6
- "version": "7.0.2",
6
+ "version": "7.0.3",
7
7
  "module": "dist/main.js",
8
8
  "type": "module",
9
9
  "types": "dist/main.d.ts",