@deserialize/multi-vm-wallet 1.2.293 → 1.3.0

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 (106) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/SMART_WALLET_GUIDE.md +746 -0
  3. package/SMART_WALLET_IMPLEMENTATION.md +460 -0
  4. package/dist/IChainWallet.d.ts +4 -3
  5. package/dist/IChainWallet.js +5 -0
  6. package/dist/IChainWallet.js.map +1 -1
  7. package/dist/constant.js +17 -4
  8. package/dist/constant.js.map +1 -1
  9. package/dist/evm/SMART_WALLET_EXAMPLES.d.ts +20 -0
  10. package/dist/evm/SMART_WALLET_EXAMPLES.js +451 -0
  11. package/dist/evm/SMART_WALLET_EXAMPLES.js.map +1 -0
  12. package/dist/evm/aa-service/index.d.ts +16 -0
  13. package/dist/evm/aa-service/index.js +69 -0
  14. package/dist/evm/aa-service/index.js.map +1 -0
  15. package/dist/evm/aa-service/lib/account-adapter.d.ts +26 -0
  16. package/dist/evm/aa-service/lib/account-adapter.js +53 -0
  17. package/dist/evm/aa-service/lib/account-adapter.js.map +1 -0
  18. package/dist/evm/aa-service/lib/kernel-account.d.ts +91 -0
  19. package/dist/evm/aa-service/lib/kernel-account.js +251 -0
  20. package/dist/evm/aa-service/lib/kernel-account.js.map +1 -0
  21. package/dist/evm/aa-service/lib/kernel-modules.d.ts +240 -0
  22. package/dist/evm/aa-service/lib/kernel-modules.js +409 -0
  23. package/dist/evm/aa-service/lib/kernel-modules.js.map +1 -0
  24. package/dist/evm/aa-service/lib/session-keys.d.ts +170 -0
  25. package/dist/evm/aa-service/lib/session-keys.js +297 -0
  26. package/dist/evm/aa-service/lib/session-keys.js.map +1 -0
  27. package/dist/evm/aa-service/lib/type.d.ts +167 -0
  28. package/dist/evm/aa-service/lib/type.js +43 -0
  29. package/dist/evm/aa-service/lib/type.js.map +1 -0
  30. package/dist/evm/aa-service/services/account-abstraction.d.ts +614 -0
  31. package/dist/evm/aa-service/services/account-abstraction.js +754 -0
  32. package/dist/evm/aa-service/services/account-abstraction.js.map +1 -0
  33. package/dist/evm/aa-service/services/bundler.d.ts +29 -0
  34. package/dist/evm/aa-service/services/bundler.js +168 -0
  35. package/dist/evm/aa-service/services/bundler.js.map +1 -0
  36. package/dist/evm/evm.d.ts +68 -3
  37. package/dist/evm/evm.js +223 -8
  38. package/dist/evm/evm.js.map +1 -1
  39. package/dist/evm/index.d.ts +1 -0
  40. package/dist/evm/index.js +3 -0
  41. package/dist/evm/index.js.map +1 -1
  42. package/dist/evm/smartWallet.d.ts +265 -0
  43. package/dist/evm/smartWallet.js +675 -0
  44. package/dist/evm/smartWallet.js.map +1 -0
  45. package/dist/evm/smartWallet.types.d.ts +10 -0
  46. package/dist/evm/smartWallet.types.js +16 -0
  47. package/dist/evm/smartWallet.types.js.map +1 -0
  48. package/dist/evm/transaction.utils.d.ts +10 -10
  49. package/dist/evm/transaction.utils.js +12 -8
  50. package/dist/evm/transaction.utils.js.map +1 -1
  51. package/dist/evm/transactionParsing.js +123 -27
  52. package/dist/evm/transactionParsing.js.map +1 -1
  53. package/dist/evm/utils.d.ts +12 -1
  54. package/dist/evm/utils.js +138 -2
  55. package/dist/evm/utils.js.map +1 -1
  56. package/dist/helpers/index.d.ts +4 -1
  57. package/dist/helpers/index.js +25 -0
  58. package/dist/helpers/index.js.map +1 -1
  59. package/dist/helpers/routeScan.d.ts +191 -0
  60. package/dist/helpers/routeScan.js +114 -0
  61. package/dist/helpers/routeScan.js.map +1 -0
  62. package/dist/index.d.ts +0 -2
  63. package/dist/index.js +0 -2
  64. package/dist/index.js.map +1 -1
  65. package/dist/svm/svm.d.ts +6 -4
  66. package/dist/svm/svm.js +33 -19
  67. package/dist/svm/svm.js.map +1 -1
  68. package/dist/svm/transactionSender.js +2 -2
  69. package/dist/svm/transactionSender.js.map +1 -1
  70. package/dist/svm/utils.d.ts +20 -4
  71. package/dist/svm/utils.js +232 -12
  72. package/dist/svm/utils.js.map +1 -1
  73. package/dist/test.d.ts +1 -6
  74. package/dist/test.js +47 -16
  75. package/dist/test.js.map +1 -1
  76. package/dist/types.d.ts +169 -2
  77. package/dist/types.js.map +1 -1
  78. package/dist/vm.js +9 -7
  79. package/dist/vm.js.map +1 -1
  80. package/package.json +2 -2
  81. package/tsconfig.json +4 -3
  82. package/utils/IChainWallet.ts +4 -3
  83. package/utils/constant.ts +18 -4
  84. package/utils/evm/SMART_WALLET_EXAMPLES.ts.bak +591 -0
  85. package/utils/evm/aa-service/index.ts +85 -0
  86. package/utils/evm/aa-service/lib/account-adapter.ts +60 -0
  87. package/utils/evm/aa-service/lib/kernel-account.ts +367 -0
  88. package/utils/evm/aa-service/lib/kernel-modules.ts +598 -0
  89. package/utils/evm/aa-service/lib/session-keys.ts +389 -0
  90. package/utils/evm/aa-service/lib/type.ts +236 -0
  91. package/utils/evm/aa-service/services/account-abstraction.ts +1015 -0
  92. package/utils/evm/aa-service/services/bundler.ts +217 -0
  93. package/utils/evm/evm.ts +281 -13
  94. package/utils/evm/index.ts +5 -1
  95. package/utils/evm/smartWallet.ts +797 -0
  96. package/utils/evm/smartWallet.types.ts +33 -0
  97. package/utils/evm/transaction.utils.ts +12 -10
  98. package/utils/evm/transactionParsing.ts +153 -63
  99. package/utils/evm/utils.ts +161 -2
  100. package/utils/helpers/index.ts +13 -1
  101. package/utils/helpers/routeScan.ts +397 -0
  102. package/utils/index.ts +0 -2
  103. package/utils/svm/svm.ts +61 -14
  104. package/utils/svm/utils.ts +317 -14
  105. package/utils/test.ts +54 -18
  106. package/utils/types.ts +223 -2
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Smart Wallet Types (Re-exports from aa-service)
3
+ *
4
+ * This file re-exports types from the AA service for backwards compatibility
5
+ * and cleaner imports in smartWallet.ts
6
+ */
7
+
8
+ export type {
9
+ SmartWalletOptions,
10
+ SmartWalletTransactionResult,
11
+ Call,
12
+ SessionKeyApprovalOptions,
13
+ SessionKeyUsageOptions,
14
+ ModuleInstallOptions,
15
+ ModuleUninstallOptions,
16
+ MultiSigConfig,
17
+ RecoveryConfig,
18
+ PaymasterConfig,
19
+ SmartAccountInfo,
20
+ EntryPointVersion,
21
+ KernelVersion
22
+ } from './aa-service/lib/type';
23
+
24
+ // Export error classes (not as types, as actual classes)
25
+ export {
26
+ SmartWalletError,
27
+ SessionKeyError,
28
+ ModuleError,
29
+ TransactionError
30
+ } from './aa-service/lib/type';
31
+
32
+ export type { ModuleType } from './aa-service/lib/kernel-modules';
33
+ export type { SessionKeyPermissionRule, SessionKeyInfo } from './aa-service/lib/session-keys';
@@ -477,18 +477,22 @@ export async function getTokenStandard(
477
477
  ): Promise<TokenStandard> {
478
478
  // Try ERC165 supportsInterface for ERC721/ERC1155
479
479
  try {
480
- const contract = getContract({
480
+ // Check ERC721
481
+ const isERC721 = await client.readContract({
481
482
  address,
482
483
  abi: parseAbi(['function supportsInterface(bytes4) view returns (bool)']),
483
- client,
484
+ functionName: 'supportsInterface',
485
+ args: [INTERFACE_IDS.ERC721 as Hex],
484
486
  });
485
-
486
- // Check ERC721
487
- const isERC721 = await contract.read.supportsInterface([INTERFACE_IDS.ERC721 as Hex]);
488
487
  if (isERC721) return TokenStandard.ERC721;
489
488
 
490
489
  // Check ERC1155
491
- const isERC1155 = await contract.read.supportsInterface([INTERFACE_IDS.ERC1155 as Hex]);
490
+ const isERC1155 = await client.readContract({
491
+ address,
492
+ abi: parseAbi(['function supportsInterface(bytes4) view returns (bool)']),
493
+ functionName: 'supportsInterface',
494
+ args: [INTERFACE_IDS.ERC1155 as Hex],
495
+ });
492
496
  if (isERC1155) return TokenStandard.ERC1155;
493
497
  } catch {
494
498
  // Not ERC721/ERC1155, try ERC20
@@ -496,13 +500,11 @@ export async function getTokenStandard(
496
500
 
497
501
  // Try ERC20 by checking for decimals function
498
502
  try {
499
- const contract = getContract({
503
+ await client.readContract({
500
504
  address,
501
505
  abi: parseAbi(['function decimals() view returns (uint8)']),
502
- client,
506
+ functionName: 'decimals',
503
507
  });
504
-
505
- await contract.read.decimals();
506
508
  return TokenStandard.ERC20;
507
509
  } catch {
508
510
  // Not a recognized token standard
@@ -12,6 +12,27 @@ import {
12
12
  parseAbi
13
13
  } from 'viem';
14
14
  import { TRANSACTION_TYPE, TransactionType } from '../constant';
15
+ import { HelperAPI, RouteScanAPI } from '../helpers';
16
+
17
+ // Mapping of chainId to RouteScan network name
18
+ const CHAIN_ID_TO_NETWORK: Record<number, string> = {
19
+ 1: 'mainnet', // Ethereum Mainnet
20
+ 5: 'testnet', // Goerli
21
+ 11155111: 'testnet', // Sepolia
22
+ 137: 'mainnet', // Polygon
23
+ 80001: 'testnet', // Polygon Mumbai
24
+ 56: 'mainnet', // BSC
25
+ 97: 'testnet', // BSC Testnet
26
+ 43114: 'mainnet', // Avalanche
27
+ 43113: 'testnet', // Avalanche Fuji
28
+ 42161: 'mainnet', // Arbitrum
29
+ 421611: 'testnet', // Arbitrum Testnet
30
+ 10: 'mainnet', // Optimism
31
+ 420: 'testnet', // Optimism Goerli
32
+ 8453: 'mainnet', // Base
33
+ 84531: 'testnet', // Base Goerli
34
+ 16661: 'mainnet', // 0G Newton Testnet (using mainnet for simplicity)
35
+ };
15
36
 
16
37
  export interface EVMTransactionHistoryItem {
17
38
  hash: string;
@@ -92,64 +113,132 @@ export async function getEVMTransactionHistory(
92
113
  walletAddress: Address,
93
114
  options: TransactionHistoryOptions = {}
94
115
  ): Promise<EVMTransactionHistoryItem[]> {
95
- const {
96
- startBlock = 0n,
97
- endBlock,
98
- includeTokenTransfers = true,
99
- includeNFTTransfers = true,
100
- } = options;
101
-
102
- try {
103
- const currentBlock = await client.getBlockNumber();
104
- const toBlock = endBlock || currentBlock;
105
-
106
- // For wallet UI, we typically want recent transactions
107
- // Start scanning from current block backwards
108
- const scanStartBlock = startBlock > 0n ? startBlock : (currentBlock > 5000n ? currentBlock - 5000n : 0n);
109
-
110
- console.log(`Fetching recent transactions from block ${scanStartBlock} to ${toBlock}`);
111
-
112
- // Get transaction hashes for the address (max 15 transactions)
113
- const txHashes = await getRecentTransactionHashes(
114
- client,
115
- walletAddress,
116
- scanStartBlock,
117
- toBlock,
118
- 15 // max transactions to fetch
119
- );
120
-
121
- console.log(`Found ${txHashes.length} unique transactions`);
122
-
123
- // Fetch full transaction details in batches
124
- const transactions = await fetchTransactionsInBatches(client, txHashes, 10);
125
-
126
- // Parse each transaction
127
- const history: EVMTransactionHistoryItem[] = [];
128
-
129
- for (const { tx, receipt, block } of transactions) {
130
- if (!tx || !receipt) continue;
131
-
132
- const parsed = await parseEVMTransaction(
133
- client,
134
- tx,
135
- receipt,
136
- block?.timestamp || null,
116
+ // const {
117
+ // startBlock = 0n,
118
+ // endBlock,
119
+ // includeTokenTransfers = true,
120
+ // includeNFTTransfers = true,
121
+ // } = options;
122
+
123
+ // try {
124
+ // const currentBlock = await client.getBlockNumber();
125
+ // const toBlock = endBlock || currentBlock;
126
+
127
+ // // For wallet UI, we typically want recent transactions
128
+ // // Start scanning from current block backwards
129
+ // const scanStartBlock = startBlock > 0n ? startBlock : (currentBlock > 5000n ? currentBlock - 5000n : 0n);
130
+
131
+ // console.log(`Fetching recent transactions from block ${scanStartBlock} to ${toBlock}`);
132
+
133
+ // // Get transaction hashes for the address (max 15 transactions)
134
+ // const txHashes = await getRecentTransactionHashes(
135
+ // client,
136
+ // walletAddress,
137
+ // scanStartBlock,
138
+ // toBlock,
139
+ // 15 // max transactions to fetch
140
+ // );
141
+
142
+ // console.log(`Found ${txHashes.length} unique transactions`);
143
+
144
+ // // Fetch full transaction details in batches
145
+ // const transactions = await fetchTransactionsInBatches(client, txHashes, 10);
146
+
147
+ // // Parse each transaction
148
+ // const history: EVMTransactionHistoryItem[] = [];
149
+
150
+ // for (const { tx, receipt, block } of transactions) {
151
+ // if (!tx || !receipt) continue;
152
+
153
+ // const parsed = await parseEVMTransaction(
154
+ // client,
155
+ // tx,
156
+ // receipt,
157
+ // block?.timestamp || null,
158
+ // walletAddress,
159
+ // includeTokenTransfers,
160
+ // includeNFTTransfers
161
+ // );
162
+
163
+ // history.push(parsed);
164
+ // }
165
+
166
+ // // Sort by block number (newest first)
167
+ // history.sort((a, b) => Number(b.blockNumber - a.blockNumber));
168
+
169
+ // return history;
170
+ // } catch (error) {
171
+ // console.error('Error fetching EVM transaction history:', error);
172
+ // throw error;
173
+ // }
174
+ if (client.chain?.id === undefined) throw new Error("Chain Id is Undefined")
175
+
176
+ const chainId = client.chain.id;
177
+ const network = CHAIN_ID_TO_NETWORK[chainId];
178
+
179
+ // Try RouteScan first if network mapping exists
180
+ if (network) {
181
+ try {
182
+ console.log(`Fetching transaction history from RouteScan for chain ${chainId} (${network})`);
183
+ const routeScanResponse = await RouteScanAPI.getTxList(
184
+ network,
185
+ chainId,
137
186
  walletAddress,
138
- includeTokenTransfers,
139
- includeNFTTransfers
187
+ 0,
188
+ 99999999,
189
+ 1,
190
+ 20, // limit to 20 recent transactions
191
+ 'desc' // newest first
140
192
  );
141
193
 
142
- history.push(parsed);
194
+ if (routeScanResponse.status === '1' && routeScanResponse.result) {
195
+ console.log(`RouteScan returned ${routeScanResponse.result.length} transactions`);
196
+ // Convert RouteScan format to our EVMTransactionHistoryItem format
197
+ return routeScanResponse.result.map(tx => ({
198
+ hash: tx.hash,
199
+ timestamp: parseInt(tx.timeStamp),
200
+ status: tx.txreceipt_status === '1' ? 'success' as const : 'failed' as const,
201
+ fee: formatEther(BigInt(tx.gasUsed) * BigInt(tx.gasPrice)),
202
+ type: determineTransactionTypeFromInput(tx.input, tx.value),
203
+ from: tx.from,
204
+ to: tx.to || null,
205
+ blockNumber: BigInt(tx.blockNumber),
206
+ gasUsed: BigInt(tx.gasUsed),
207
+ gasPrice: formatUnits(BigInt(tx.gasPrice), 9), // gwei
208
+ value: formatEther(BigInt(tx.value)),
209
+ method: tx.methodId || undefined,
210
+ // Token and NFT transfers would need additional API calls
211
+ // We'll skip them for now to keep it simple
212
+ }));
213
+ }
214
+ } catch (error) {
215
+ console.warn('RouteScan failed, falling back to HelperAPI:', error);
143
216
  }
217
+ }
144
218
 
145
- // Sort by block number (newest first)
146
- history.sort((a, b) => Number(b.blockNumber - a.blockNumber));
219
+ // Fallback to HelperAPI
220
+ console.log(`Fetching transaction history from HelperAPI for chain ${chainId}`);
221
+ return await HelperAPI.getTransactionHistory(walletAddress, "EVM", chainId);
222
+ }
147
223
 
148
- return history;
149
- } catch (error) {
150
- console.error('Error fetching EVM transaction history:', error);
151
- throw error;
224
+ /**
225
+ * Determine transaction type from input data and value (simpler version for RouteScan data)
226
+ */
227
+ function determineTransactionTypeFromInput(input: string, value: string): TransactionType {
228
+ if (input === '0x' || input === '') {
229
+ return BigInt(value) > 0n ? TRANSACTION_TYPE.NATIVE_TRANSFER : TRANSACTION_TYPE.CONTRACT_INTERACTION;
152
230
  }
231
+
232
+ const methodSig = input.slice(0, 10);
233
+
234
+ if (methodSig === '0xa9059cbb') return TRANSACTION_TYPE.TOKEN_TRANSFER; // ERC20 transfer
235
+ if (methodSig === '0x23b872dd') return TRANSACTION_TYPE.TOKEN_TRANSFER; // ERC20 transferFrom
236
+ if (methodSig === '0x42842e0e') return TRANSACTION_TYPE.NFT_TRANSFER; // ERC721 safeTransferFrom
237
+ if (methodSig === '0xf242432a') return TRANSACTION_TYPE.NFT_TRANSFER; // ERC1155 safeTransferFrom
238
+
239
+ if (BigInt(value) > 0n) return TRANSACTION_TYPE.NATIVE_TRANSFER;
240
+
241
+ return TRANSACTION_TYPE.CONTRACT_INTERACTION;
153
242
  }
154
243
 
155
244
  /**
@@ -488,9 +577,9 @@ function determineTransactionType(tx: Transaction, receipt: TransactionReceipt):
488
577
  if (tx.value && tx.value > 0n) return TRANSACTION_TYPE.NATIVE_TRANSFER;
489
578
 
490
579
  // Check logs for common patterns
491
- if (receipt.logs.some(log => log.topics[0]?.includes('Swap'))) return TRANSACTION_TYPE.SWAP;
492
- if (receipt.logs.some(log => log.topics[0]?.includes('Deposit'))) return TRANSACTION_TYPE.DEPOSIT;
493
- if (receipt.logs.some(log => log.topics[0]?.includes('Withdraw'))) return TRANSACTION_TYPE.WITHDRAWAL;
580
+ if (receipt.logs.some(log => (log as any).topics[0]?.includes('Swap'))) return TRANSACTION_TYPE.SWAP;
581
+ if (receipt.logs.some(log => (log as any).topics[0]?.includes('Deposit'))) return TRANSACTION_TYPE.DEPOSIT;
582
+ if (receipt.logs.some(log => (log as any).topics[0]?.includes('Withdraw'))) return TRANSACTION_TYPE.WITHDRAWAL;
494
583
 
495
584
  return TRANSACTION_TYPE.CONTRACT_INTERACTION;
496
585
  }
@@ -511,13 +600,13 @@ async function parseTransferLogs(
511
600
  for (const log of logs) {
512
601
  try {
513
602
  // Try ERC20 Transfer
514
- if (includeTokenTransfers && log.topics.length === 3) {
603
+ if (includeTokenTransfers && (log as any).topics.length === 3) {
515
604
  try {
516
605
  const decoded = decodeEventLog({
517
606
  abi: ERC20_TRANSFER_EVENT,
518
607
  data: log.data,
519
- topics: log.topics,
520
- });
608
+ topics: (log as any).topics,
609
+ }) as any
521
610
 
522
611
  if (decoded.eventName === 'Transfer') {
523
612
  const { from, to, value } = decoded.args as any;
@@ -533,7 +622,8 @@ async function parseTransferLogs(
533
622
  address: log.address,
534
623
  abi: parseAbi(['function decimals() view returns (uint8)']),
535
624
  functionName: 'decimals',
536
- }) as number;
625
+ authorizationList: undefined
626
+ })
537
627
  } catch { }
538
628
 
539
629
  tokens.push({
@@ -550,13 +640,13 @@ async function parseTransferLogs(
550
640
  }
551
641
 
552
642
  // Try ERC721 Transfer (has indexed tokenId)
553
- if (includeNFTTransfers && log.topics.length === 4) {
643
+ if (includeNFTTransfers && (log as any).topics.length === 4) {
554
644
  try {
555
645
  const decoded = decodeEventLog({
556
646
  abi: ERC721_TRANSFER_EVENT,
557
647
  data: log.data,
558
- topics: log.topics,
559
- });
648
+ topics: (log as any).topics,
649
+ }) as any
560
650
 
561
651
  if (decoded.eventName === 'Transfer') {
562
652
  const { from, to, tokenId } = decoded.args as any;
@@ -581,8 +671,8 @@ async function parseTransferLogs(
581
671
  const decoded = decodeEventLog({
582
672
  abi: ERC1155_TRANSFER_SINGLE_EVENT,
583
673
  data: log.data,
584
- topics: log.topics,
585
- });
674
+ topics: (log as any).topics,
675
+ }) as any
586
676
 
587
677
  if (decoded.eventName === 'TransferSingle') {
588
678
  const { from, to, id, value } = decoded.args as any;
@@ -1,4 +1,4 @@
1
- import { Balance, ChainWalletConfig, SUPPORTED_VM, UserTokenBalance, TokenInfo } from '../types'
1
+ import { Balance, ChainWalletConfig, SUPPORTED_VM, UserTokenBalance, TokenInfo, EVMNFT, NFT, NFTCollection } from '../types'
2
2
  import { JsonRpcProvider, Contract, Wallet, TransactionRequest, TransactionResponse, TransactionReceipt, parseUnits, formatUnits, ethers } from 'ethers'
3
3
  import BN from 'bn.js'
4
4
  import { HelperAPI } from '../helpers';
@@ -650,7 +650,8 @@ export const discoverTokens = async (wallet: string, chain: ChainWalletConfig):
650
650
  symbol: token.symbol,
651
651
  decimals: token.decimals,
652
652
  balance: token.balance,
653
- owner: wallet
653
+ owner: wallet,
654
+ logoUrl: token.logo
654
655
  }
655
656
  })
656
657
  return formatBalances
@@ -954,4 +955,162 @@ export function prepareSwapParams(
954
955
 
955
956
  export function convertSlippageForDebonk(slippageBps: number): number {
956
957
  return slippageBps / 100;
958
+ }
959
+
960
+ export const transformEVMNFTToUnified = (nft: EVMNFT): NFT => {
961
+ // Extract image URL from various sources
962
+ const imageUrl = nft.image?.cachedUrl ||
963
+ nft.image?.thumbnailUrl ||
964
+ nft.image?.pngUrl ||
965
+ nft.image?.originalUrl ||
966
+ nft.openSeaMetadata?.imageUrl ||
967
+ nft.raw?.metadata?.image ||
968
+ undefined;
969
+
970
+ // Extract attributes
971
+ const attributes = nft.raw?.metadata?.attributes?.map(attr => ({
972
+ trait_type: attr.trait_type,
973
+ value: attr.value,
974
+ display_type: attr.display_type
975
+ }));
976
+
977
+ return {
978
+ id: `${nft.contract.address}:${nft.tokenId}`,
979
+ name: nft.name || nft.raw?.metadata?.name || 'Unknown',
980
+ symbol: nft.contract.symbol,
981
+ description: nft.description || nft.raw?.metadata?.description || '',
982
+ image: imageUrl,
983
+ uri: nft.raw?.tokenUri || '',
984
+ collection: {
985
+ address: nft.contract.address,
986
+ name: nft.openSeaMetadata?.collectionName || nft.contract.name,
987
+ verified: nft.openSeaMetadata?.safelistRequestStatus === 'verified'
988
+ },
989
+ chainType: 'EVM',
990
+ balance: nft.balance,
991
+ attributes,
992
+ tokenStandard: nft.tokenType,
993
+ isSpam: nft.contract.isSpam,
994
+ raw: {
995
+ evm: nft
996
+ }
997
+ };
998
+ };
999
+
1000
+ export const discoverNFTs = async (wallet: string, chain: ChainWalletConfig): Promise<NFT[]> => {
1001
+ console.log('discoverNFTs: Starting NFT discovery');
1002
+ console.log('Wallet:', wallet);
1003
+ console.log('Chain:', chain.name, 'ChainId:', chain.chainId);
1004
+
1005
+ try {
1006
+ const response = await HelperAPI.getUserNFTs(wallet, chain.vmType ?? "EVM", chain.chainId);
1007
+
1008
+ console.log('discoverNFTs: Successfully fetched', response.data.length, 'NFTs');
1009
+
1010
+ // Filter out spam NFTs if desired (optional)
1011
+ const evmNfts = response.data.filter(nft => !nft.contract.isSpam);
1012
+
1013
+ console.log('discoverNFTs: After spam filtering:', evmNfts.length, 'NFTs');
1014
+
1015
+ // Transform to unified NFT format
1016
+ const nfts = evmNfts.map(transformEVMNFTToUnified);
1017
+
1018
+ return nfts;
1019
+ } catch (error) {
1020
+ console.error('discoverNFTs: Error fetching NFTs:', error);
1021
+ console.error('Error details:', error instanceof Error ? error.message : 'Unknown error');
1022
+ throw error;
1023
+ }
1024
+ }
1025
+
1026
+ export const discoverAllNFTs = async (wallet: string, chain: ChainWalletConfig): Promise<NFT[]> => {
1027
+ console.log('discoverAllNFTs: Starting NFT discovery (including spam)');
1028
+ console.log('Wallet:', wallet);
1029
+ console.log('Chain:', chain.name, 'ChainId:', chain.chainId);
1030
+
1031
+ try {
1032
+ const response = await HelperAPI.getUserNFTs(wallet, chain.vmType ?? "EVM", chain.chainId);
1033
+
1034
+ console.log('discoverAllNFTs: Successfully fetched', response.data.length, 'NFTs (including spam)');
1035
+
1036
+ // Transform to unified NFT format
1037
+ const nfts = response.data.map(transformEVMNFTToUnified);
1038
+
1039
+ return nfts;
1040
+ } catch (error) {
1041
+ console.error('discoverAllNFTs: Error fetching NFTs:', error);
1042
+ console.error('Error details:', error instanceof Error ? error.message : 'Unknown error');
1043
+ throw error;
1044
+ }
1045
+ }
1046
+
1047
+ /**
1048
+ * Get NFT collection details for a specific collection
1049
+ * @param wallet - User's wallet address
1050
+ * @param collectionAddress - The NFT collection contract address
1051
+ * @param chain - Chain configuration
1052
+ * @returns NFTCollection object with collection details and user's NFTs in that collection
1053
+ */
1054
+ export const getNFTCollection = async (
1055
+ wallet: string,
1056
+ collectionAddress: string,
1057
+ chain: ChainWalletConfig
1058
+ ): Promise<NFTCollection | null> => {
1059
+ console.log('getNFTCollection: Starting collection fetch');
1060
+ console.log('Wallet:', wallet);
1061
+ console.log('Collection Address:', collectionAddress);
1062
+ console.log('Chain:', chain.name, 'ChainId:', chain.chainId);
1063
+
1064
+ try {
1065
+ // Fetch all NFTs for the user
1066
+ const allNfts = await discoverAllNFTs(wallet, chain);
1067
+
1068
+ console.log('getNFTCollection: Fetched', allNfts.length, 'total NFTs');
1069
+
1070
+ // Filter NFTs by collection address (case-insensitive comparison)
1071
+ const collectionNfts = allNfts.filter(
1072
+ nft => nft.collection.address.toLowerCase() === collectionAddress.toLowerCase()
1073
+ );
1074
+
1075
+ console.log('getNFTCollection: Found', collectionNfts.length, 'NFTs in collection');
1076
+
1077
+ if (collectionNfts.length === 0) {
1078
+ console.log('getNFTCollection: No NFTs found in this collection');
1079
+ return null;
1080
+ }
1081
+
1082
+ // Extract collection metadata from the first NFT
1083
+ const firstNft = collectionNfts[0];
1084
+ const rawEvmNft = firstNft.raw?.evm;
1085
+
1086
+ const collection: NFTCollection = {
1087
+ address: collectionAddress,
1088
+ name: firstNft.collection.name,
1089
+ symbol: firstNft.symbol,
1090
+ description: undefined,
1091
+ image: rawEvmNft?.openSeaMetadata?.imageUrl,
1092
+ verified: firstNft.collection.verified,
1093
+ chainType: 'EVM',
1094
+ nfts: collectionNfts,
1095
+ totalOwned: collectionNfts.length,
1096
+ contractMetadata: rawEvmNft ? {
1097
+ tokenType: rawEvmNft.contract.tokenType,
1098
+ totalSupply: rawEvmNft.contract.totalSupply,
1099
+ contractDeployer: rawEvmNft.contract.contractDeployer,
1100
+ deployedBlockNumber: rawEvmNft.contract.deployedBlockNumber,
1101
+ } : undefined,
1102
+ openSeaMetadata: rawEvmNft?.openSeaMetadata ? {
1103
+ collectionSlug: rawEvmNft.openSeaMetadata.collectionSlug,
1104
+ floorPrice: rawEvmNft.openSeaMetadata.floorPrice,
1105
+ safelistRequestStatus: rawEvmNft.openSeaMetadata.safelistRequestStatus,
1106
+ } : undefined,
1107
+ };
1108
+
1109
+ console.log('getNFTCollection: Successfully created collection object');
1110
+ return collection;
1111
+ } catch (error) {
1112
+ console.error('getNFTCollection: Error fetching collection:', error);
1113
+ console.error('Error details:', error instanceof Error ? error.message : 'Unknown error');
1114
+ throw error;
1115
+ }
957
1116
  }
@@ -1,4 +1,5 @@
1
- import { vmTypes } from "../types";
1
+ export * from './routeScan';
2
+ import { vmTypes, EVMNFTResponse } from "../types";
2
3
 
3
4
  const BASE_URL = "https://helper.decane.app"
4
5
 
@@ -8,4 +9,15 @@ export class HelperAPI {
8
9
  const data = await res.json();
9
10
  return data;
10
11
  }
12
+
13
+ static async getUserNFTs(wallet: string, vm: vmTypes, chainId: number): Promise<EVMNFTResponse> {
14
+ const res = await fetch("" + BASE_URL + `/${vm}/nfts/discover?wallet=${wallet}&chainId=${chainId}`);
15
+ const data = await res.json();
16
+ return data;
17
+ }
18
+ static async getTransactionHistory(wallet: string, vm: vmTypes, chainId: number, limit: number = 20) {
19
+ const res = await fetch("" + BASE_URL + `/${vm}/transactions/history?wallet=${wallet}&chainId=${chainId}&limit=${limit}`);
20
+ const data = await res.json();
21
+ return data;
22
+ }
11
23
  }