@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.
- package/.claude/settings.local.json +12 -0
- package/SMART_WALLET_GUIDE.md +746 -0
- package/SMART_WALLET_IMPLEMENTATION.md +460 -0
- package/dist/IChainWallet.d.ts +4 -3
- package/dist/IChainWallet.js +5 -0
- package/dist/IChainWallet.js.map +1 -1
- package/dist/constant.js +17 -4
- package/dist/constant.js.map +1 -1
- package/dist/evm/SMART_WALLET_EXAMPLES.d.ts +20 -0
- package/dist/evm/SMART_WALLET_EXAMPLES.js +451 -0
- package/dist/evm/SMART_WALLET_EXAMPLES.js.map +1 -0
- package/dist/evm/aa-service/index.d.ts +16 -0
- package/dist/evm/aa-service/index.js +69 -0
- package/dist/evm/aa-service/index.js.map +1 -0
- package/dist/evm/aa-service/lib/account-adapter.d.ts +26 -0
- package/dist/evm/aa-service/lib/account-adapter.js +53 -0
- package/dist/evm/aa-service/lib/account-adapter.js.map +1 -0
- package/dist/evm/aa-service/lib/kernel-account.d.ts +91 -0
- package/dist/evm/aa-service/lib/kernel-account.js +251 -0
- package/dist/evm/aa-service/lib/kernel-account.js.map +1 -0
- package/dist/evm/aa-service/lib/kernel-modules.d.ts +240 -0
- package/dist/evm/aa-service/lib/kernel-modules.js +409 -0
- package/dist/evm/aa-service/lib/kernel-modules.js.map +1 -0
- package/dist/evm/aa-service/lib/session-keys.d.ts +170 -0
- package/dist/evm/aa-service/lib/session-keys.js +297 -0
- package/dist/evm/aa-service/lib/session-keys.js.map +1 -0
- package/dist/evm/aa-service/lib/type.d.ts +167 -0
- package/dist/evm/aa-service/lib/type.js +43 -0
- package/dist/evm/aa-service/lib/type.js.map +1 -0
- package/dist/evm/aa-service/services/account-abstraction.d.ts +614 -0
- package/dist/evm/aa-service/services/account-abstraction.js +754 -0
- package/dist/evm/aa-service/services/account-abstraction.js.map +1 -0
- package/dist/evm/aa-service/services/bundler.d.ts +29 -0
- package/dist/evm/aa-service/services/bundler.js +168 -0
- package/dist/evm/aa-service/services/bundler.js.map +1 -0
- package/dist/evm/evm.d.ts +68 -3
- package/dist/evm/evm.js +223 -8
- package/dist/evm/evm.js.map +1 -1
- package/dist/evm/index.d.ts +1 -0
- package/dist/evm/index.js +3 -0
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/smartWallet.d.ts +265 -0
- package/dist/evm/smartWallet.js +675 -0
- package/dist/evm/smartWallet.js.map +1 -0
- package/dist/evm/smartWallet.types.d.ts +10 -0
- package/dist/evm/smartWallet.types.js +16 -0
- package/dist/evm/smartWallet.types.js.map +1 -0
- package/dist/evm/transaction.utils.d.ts +10 -10
- package/dist/evm/transaction.utils.js +12 -8
- package/dist/evm/transaction.utils.js.map +1 -1
- package/dist/evm/transactionParsing.js +123 -27
- package/dist/evm/transactionParsing.js.map +1 -1
- package/dist/evm/utils.d.ts +12 -1
- package/dist/evm/utils.js +138 -2
- package/dist/evm/utils.js.map +1 -1
- package/dist/helpers/index.d.ts +4 -1
- package/dist/helpers/index.js +25 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/helpers/routeScan.d.ts +191 -0
- package/dist/helpers/routeScan.js +114 -0
- package/dist/helpers/routeScan.js.map +1 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/svm/svm.d.ts +6 -4
- package/dist/svm/svm.js +33 -19
- package/dist/svm/svm.js.map +1 -1
- package/dist/svm/transactionSender.js +2 -2
- package/dist/svm/transactionSender.js.map +1 -1
- package/dist/svm/utils.d.ts +20 -4
- package/dist/svm/utils.js +232 -12
- package/dist/svm/utils.js.map +1 -1
- package/dist/test.d.ts +1 -6
- package/dist/test.js +47 -16
- package/dist/test.js.map +1 -1
- package/dist/types.d.ts +169 -2
- package/dist/types.js.map +1 -1
- package/dist/vm.js +9 -7
- package/dist/vm.js.map +1 -1
- package/package.json +2 -2
- package/tsconfig.json +4 -3
- package/utils/IChainWallet.ts +4 -3
- package/utils/constant.ts +18 -4
- package/utils/evm/SMART_WALLET_EXAMPLES.ts.bak +591 -0
- package/utils/evm/aa-service/index.ts +85 -0
- package/utils/evm/aa-service/lib/account-adapter.ts +60 -0
- package/utils/evm/aa-service/lib/kernel-account.ts +367 -0
- package/utils/evm/aa-service/lib/kernel-modules.ts +598 -0
- package/utils/evm/aa-service/lib/session-keys.ts +389 -0
- package/utils/evm/aa-service/lib/type.ts +236 -0
- package/utils/evm/aa-service/services/account-abstraction.ts +1015 -0
- package/utils/evm/aa-service/services/bundler.ts +217 -0
- package/utils/evm/evm.ts +281 -13
- package/utils/evm/index.ts +5 -1
- package/utils/evm/smartWallet.ts +797 -0
- package/utils/evm/smartWallet.types.ts +33 -0
- package/utils/evm/transaction.utils.ts +12 -10
- package/utils/evm/transactionParsing.ts +153 -63
- package/utils/evm/utils.ts +161 -2
- package/utils/helpers/index.ts +13 -1
- package/utils/helpers/routeScan.ts +397 -0
- package/utils/index.ts +0 -2
- package/utils/svm/svm.ts +61 -14
- package/utils/svm/utils.ts +317 -14
- package/utils/test.ts +54 -18
- 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
|
-
|
|
480
|
+
// Check ERC721
|
|
481
|
+
const isERC721 = await client.readContract({
|
|
481
482
|
address,
|
|
482
483
|
abi: parseAbi(['function supportsInterface(bytes4) view returns (bool)']),
|
|
483
|
-
|
|
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
|
|
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
|
-
|
|
503
|
+
await client.readContract({
|
|
500
504
|
address,
|
|
501
505
|
abi: parseAbi(['function decimals() view returns (uint8)']),
|
|
502
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
} = options;
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
139
|
-
|
|
187
|
+
0,
|
|
188
|
+
99999999,
|
|
189
|
+
1,
|
|
190
|
+
20, // limit to 20 recent transactions
|
|
191
|
+
'desc' // newest first
|
|
140
192
|
);
|
|
141
193
|
|
|
142
|
-
|
|
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
|
-
|
|
146
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
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;
|
package/utils/evm/utils.ts
CHANGED
|
@@ -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
|
}
|
package/utils/helpers/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
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
|
}
|