@deserialize/multi-vm-wallet 1.4.12 → 1.5.1
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 +7 -1
- package/BUILD_OPTIMIZATION_PLAN.md +640 -0
- package/BUILD_RESULTS.md +282 -0
- package/BUN_MIGRATION.md +415 -0
- package/CHANGELOG_SECURITY.md +573 -0
- package/IMPLEMENTATION_SUMMARY.md +494 -0
- package/SECURITY_AUDIT.md +1124 -0
- package/bun.lock +553 -0
- package/dist/IChainWallet.js +0 -5
- package/dist/bip32Old.js +0 -885
- package/dist/bip32Small.js +0 -79
- package/dist/bipTest.js +0 -362
- package/dist/constant.js +0 -17
- package/dist/english.js +0 -1
- package/dist/evm/aa-service/index.d.ts +0 -5
- package/dist/evm/aa-service/index.js +0 -14
- package/dist/evm/aa-service/lib/account-adapter.d.ts +0 -22
- package/dist/evm/aa-service/lib/account-adapter.js +0 -24
- package/dist/evm/aa-service/lib/kernel-account.d.ts +0 -30
- package/dist/evm/aa-service/lib/kernel-account.js +2 -67
- package/dist/evm/aa-service/lib/kernel-modules.d.ts +0 -177
- package/dist/evm/aa-service/lib/kernel-modules.js +4 -202
- package/dist/evm/aa-service/lib/session-keys.d.ts +0 -118
- package/dist/evm/aa-service/lib/session-keys.js +7 -151
- package/dist/evm/aa-service/lib/type.d.ts +0 -55
- package/dist/evm/aa-service/lib/type.js +0 -10
- package/dist/evm/aa-service/services/account-abstraction.d.ts +0 -426
- package/dist/evm/aa-service/services/account-abstraction.js +0 -461
- package/dist/evm/aa-service/services/bundler.d.ts +0 -6
- package/dist/evm/aa-service/services/bundler.js +0 -54
- package/dist/evm/evm.d.ts +10 -67
- package/dist/evm/evm.js +339 -102
- package/dist/evm/index.js +0 -3
- package/dist/evm/script.js +3 -17
- package/dist/evm/smartWallet.d.ts +0 -173
- package/dist/evm/smartWallet.js +0 -206
- package/dist/evm/smartWallet.types.d.ts +0 -6
- package/dist/evm/smartWallet.types.js +0 -8
- package/dist/evm/transaction.utils.d.ts +0 -242
- package/dist/evm/transaction.utils.js +4 -320
- package/dist/evm/transactionParsing.d.ts +0 -11
- package/dist/evm/transactionParsing.js +28 -147
- package/dist/evm/utils.d.ts +0 -46
- package/dist/evm/utils.js +1 -57
- package/dist/helpers/index.d.ts +0 -4
- package/dist/helpers/index.js +8 -44
- package/dist/helpers/routeScan.js +0 -1
- package/dist/index.js +0 -1
- package/dist/old.js +0 -884
- package/dist/price.js +0 -1
- package/dist/price.types.js +0 -2
- package/dist/rate-limiter.d.ts +28 -0
- package/dist/rate-limiter.js +95 -0
- package/dist/retry-logic.d.ts +14 -0
- package/dist/retry-logic.js +120 -0
- package/dist/savings/index.d.ts +1 -0
- package/dist/savings/index.js +16 -2
- package/dist/savings/saving-manager.d.ts +46 -0
- package/dist/savings/saving-manager.js +176 -0
- package/dist/savings/savings-operations.d.ts +39 -0
- package/dist/savings/savings-operations.js +141 -0
- package/dist/savings/smart-savings.d.ts +0 -63
- package/dist/savings/smart-savings.js +0 -78
- package/dist/savings/types.d.ts +0 -69
- package/dist/savings/types.js +0 -7
- package/dist/savings/validation.d.ts +9 -0
- package/dist/savings/validation.js +85 -0
- package/dist/svm/constant.js +0 -1
- package/dist/svm/index.js +0 -1
- package/dist/svm/svm.d.ts +7 -13
- package/dist/svm/svm.js +262 -46
- package/dist/svm/transactionParsing.d.ts +0 -7
- package/dist/svm/transactionParsing.js +3 -41
- package/dist/svm/transactionSender.js +0 -9
- package/dist/svm/utils.d.ts +0 -12
- package/dist/svm/utils.js +9 -60
- package/dist/test.d.ts +0 -4
- package/dist/test.js +15 -95
- package/dist/transaction-utils.d.ts +38 -0
- package/dist/transaction-utils.js +168 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.js +0 -1
- package/dist/utils.js +0 -1
- package/dist/vm-validation.d.ts +11 -0
- package/dist/vm-validation.js +151 -0
- package/dist/vm.d.ts +14 -16
- package/dist/vm.js +64 -53
- package/dist/walletBip32.d.ts +2 -0
- package/dist/walletBip32.js +31 -66
- package/package.json +9 -4
- package/test-discovery.ts +235 -0
- package/test-pocket-discovery.ts +84 -0
- package/tsconfig.json +18 -11
- package/tsconfig.prod.json +10 -0
- package/utils/IChainWallet.ts +2 -0
- package/utils/evm/evm.ts +560 -39
- package/utils/rate-limiter.ts +179 -0
- package/utils/retry-logic.ts +271 -0
- package/utils/savings/EXAMPLES.md +883 -0
- package/utils/savings/SECURITY.md +731 -0
- package/utils/savings/index.ts +1 -1
- package/utils/savings/saving-manager.ts +656 -0
- package/utils/savings/savings-operations.ts +509 -0
- package/utils/savings/validation.ts +187 -0
- package/utils/svm/svm.ts +467 -20
- package/utils/test.ts +26 -3
- package/utils/transaction-utils.ts +394 -0
- package/utils/types.ts +100 -0
- package/utils/vm-validation.ts +280 -0
- package/utils/vm.ts +202 -24
- package/utils/walletBip32.ts +63 -3
- package/dist/IChainWallet.js.map +0 -1
- package/dist/bip32.d.ts +0 -9
- package/dist/bip32.js +0 -172
- package/dist/bip32.js.map +0 -1
- package/dist/bip32Old.js.map +0 -1
- package/dist/bip32Small.js.map +0 -1
- package/dist/bipTest.js.map +0 -1
- package/dist/constant.js.map +0 -1
- package/dist/english.js.map +0 -1
- package/dist/evm/SMART_WALLET_EXAMPLES.d.ts +0 -20
- package/dist/evm/SMART_WALLET_EXAMPLES.js +0 -451
- package/dist/evm/SMART_WALLET_EXAMPLES.js.map +0 -1
- package/dist/evm/aa-service/index.js.map +0 -1
- package/dist/evm/aa-service/lib/account-adapter.js.map +0 -1
- package/dist/evm/aa-service/lib/kernel-account.js.map +0 -1
- package/dist/evm/aa-service/lib/kernel-modules.js.map +0 -1
- package/dist/evm/aa-service/lib/session-keys.js.map +0 -1
- package/dist/evm/aa-service/lib/type.js.map +0 -1
- package/dist/evm/aa-service/services/account-abstraction.js.map +0 -1
- package/dist/evm/aa-service/services/bundler.js.map +0 -1
- package/dist/evm/evm.js.map +0 -1
- package/dist/evm/index.js.map +0 -1
- package/dist/evm/script.js.map +0 -1
- package/dist/evm/smartWallet.js.map +0 -1
- package/dist/evm/smartWallet.types.js.map +0 -1
- package/dist/evm/transaction.utils.js.map +0 -1
- package/dist/evm/transactionParsing.js.map +0 -1
- package/dist/evm/utils.js.map +0 -1
- package/dist/helpers/index.js.map +0 -1
- package/dist/helpers/routeScan.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/old.js.map +0 -1
- package/dist/price.js.map +0 -1
- package/dist/price.types.js.map +0 -1
- package/dist/privacy/artifact-manager.d.ts +0 -117
- package/dist/privacy/artifact-manager.js +0 -251
- package/dist/privacy/artifact-manager.js.map +0 -1
- package/dist/privacy/broadcaster-client.d.ts +0 -166
- package/dist/privacy/broadcaster-client.js +0 -261
- package/dist/privacy/broadcaster-client.js.map +0 -1
- package/dist/privacy/index.d.ts +0 -34
- package/dist/privacy/index.js +0 -56
- package/dist/privacy/index.js.map +0 -1
- package/dist/privacy/network-config.d.ts +0 -57
- package/dist/privacy/network-config.js +0 -118
- package/dist/privacy/network-config.js.map +0 -1
- package/dist/privacy/poi-helper.d.ts +0 -161
- package/dist/privacy/poi-helper.js +0 -249
- package/dist/privacy/poi-helper.js.map +0 -1
- package/dist/privacy/railgun-engine.d.ts +0 -135
- package/dist/privacy/railgun-engine.js +0 -205
- package/dist/privacy/railgun-engine.js.map +0 -1
- package/dist/privacy/railgun-privacy-wallet.d.ts +0 -288
- package/dist/privacy/railgun-privacy-wallet.js +0 -539
- package/dist/privacy/railgun-privacy-wallet.js.map +0 -1
- package/dist/privacy/types.d.ts +0 -229
- package/dist/privacy/types.js +0 -26
- package/dist/privacy/types.js.map +0 -1
- package/dist/savings/index.js.map +0 -1
- package/dist/savings/saving-actions.d.ts +0 -0
- package/dist/savings/saving-actions.js +0 -78
- package/dist/savings/saving-actions.js.map +0 -1
- package/dist/savings/savings-manager.d.ts +0 -126
- package/dist/savings/savings-manager.js +0 -234
- package/dist/savings/savings-manager.js.map +0 -1
- package/dist/savings/smart-savings.js.map +0 -1
- package/dist/savings/types.js.map +0 -1
- package/dist/svm/constant.js.map +0 -1
- package/dist/svm/index.js.map +0 -1
- package/dist/svm/svm.js.map +0 -1
- package/dist/svm/transactionParsing.js.map +0 -1
- package/dist/svm/transactionSender.js.map +0 -1
- package/dist/svm/utils.js.map +0 -1
- package/dist/test.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/dist/vm.js.map +0 -1
- package/dist/walletBip32.js.map +0 -1
- package/utils/savings/saving-actions.ts +0 -92
- package/utils/savings/savings-manager.ts +0 -271
package/dist/svm/utils.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
//we will write all the svm utils function here
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.getNFTCollection = exports.fetchWalletNfts = exports.transformSolanaNFTToUnified = exports.validateJupiterTokens = exports.getJupiterTokenList = exports.baseUnitsToUiAmount = exports.uiAmountToBaseUnits = exports.executeJupiterSwap = exports.buildJupiterSwapTransaction = exports.getJupiterQuote = exports.discoverTokens = exports.signAndSendTransaction = exports.sendTransaction = exports.signTransaction = exports.getTransferTokenTransaction = exports.getTransferTokenInx = exports.getTokenInfo = exports.getTransferNativeTransaction = exports.getTransferNativeInx = exports.getTokenAccountAccount = exports.getTokenBalance = exports.getSvmNativeBalance = exports.getProgramIdOfToken = exports.getSureAssociatedTokenAddressAndAccount = exports.createAtaAndIx = exports.createV0Transaction = void 0;
|
|
5
4
|
const spl_token_1 = require("@solana/spl-token");
|
|
@@ -106,7 +105,6 @@ const getProgramIdOfToken = async (owner, token, connection) => {
|
|
|
106
105
|
}
|
|
107
106
|
};
|
|
108
107
|
exports.getProgramIdOfToken = getProgramIdOfToken;
|
|
109
|
-
//get native balance
|
|
110
108
|
const getSvmNativeBalance = async (address, connection) => {
|
|
111
109
|
const balance = await connection.getBalance(address);
|
|
112
110
|
return { balance: new bn_js_1.BN(balance), formatted: balance / web3_js_1.LAMPORTS_PER_SOL, decimal: 9 };
|
|
@@ -114,7 +112,6 @@ const getSvmNativeBalance = async (address, connection) => {
|
|
|
114
112
|
exports.getSvmNativeBalance = getSvmNativeBalance;
|
|
115
113
|
const getTokenBalance = async (address, token, connection) => {
|
|
116
114
|
try {
|
|
117
|
-
// Get the balance from the token account
|
|
118
115
|
const tokenAccount = await (0, exports.getTokenAccountAccount)(token, address, connection);
|
|
119
116
|
if (!tokenAccount) {
|
|
120
117
|
console.log("Token account not found");
|
|
@@ -137,12 +134,8 @@ const getTokenBalance = async (address, token, connection) => {
|
|
|
137
134
|
exports.getTokenBalance = getTokenBalance;
|
|
138
135
|
const getTokenAccountAccount = async (token, address, connection) => {
|
|
139
136
|
try {
|
|
140
|
-
|
|
141
|
-
const associatedTokenAccount = await (0, spl_token_1.getAssociatedTokenAddress)(token, // The token mint address
|
|
142
|
-
address // The user's public key
|
|
143
|
-
);
|
|
137
|
+
const associatedTokenAccount = await (0, spl_token_1.getAssociatedTokenAddress)(token, address);
|
|
144
138
|
console.log('Associated token account:', associatedTokenAccount.toString());
|
|
145
|
-
// Fetch the token account information
|
|
146
139
|
const tokenAccount = await (0, spl_token_1.getAccount)(connection, associatedTokenAccount);
|
|
147
140
|
console.log('Token account retrieved successfully');
|
|
148
141
|
return tokenAccount;
|
|
@@ -157,7 +150,7 @@ const getTransferNativeInx = async (from, to, amount) => {
|
|
|
157
150
|
return web3_js_1.SystemProgram.transfer({
|
|
158
151
|
fromPubkey: from,
|
|
159
152
|
toPubkey: to,
|
|
160
|
-
lamports: amount * web3_js_1.LAMPORTS_PER_SOL,
|
|
153
|
+
lamports: amount * web3_js_1.LAMPORTS_PER_SOL,
|
|
161
154
|
});
|
|
162
155
|
};
|
|
163
156
|
exports.getTransferNativeInx = getTransferNativeInx;
|
|
@@ -176,7 +169,6 @@ const getMetaTokenMetaplexData = async (mintAddress, connection) => {
|
|
|
176
169
|
};
|
|
177
170
|
const fetchMetadataFromUri = async (uri) => {
|
|
178
171
|
try {
|
|
179
|
-
// Convert IPFS URI to HTTP gateway if needed
|
|
180
172
|
let fetchUrl = uri;
|
|
181
173
|
if (uri.startsWith('ipfs://')) {
|
|
182
174
|
fetchUrl = uri.replace('ipfs://', 'https://ipfs.io/ipfs/');
|
|
@@ -203,14 +195,12 @@ const fetchMetadataFromUri = async (uri) => {
|
|
|
203
195
|
const getTokenInfo = async (tokenAddress, connection, programId) => {
|
|
204
196
|
let mint;
|
|
205
197
|
const metaplexData = await getMetaTokenMetaplexData(tokenAddress, connection).catch(() => null);
|
|
206
|
-
// Fetch metadata from URI if available
|
|
207
198
|
let uriMetadata = null;
|
|
208
199
|
if (metaplexData?.uri) {
|
|
209
200
|
uriMetadata = await fetchMetadataFromUri(metaplexData.uri).catch(() => null);
|
|
210
201
|
}
|
|
211
202
|
if (programId) {
|
|
212
203
|
const mint = await (0, spl_token_1.getMint)(connection, tokenAddress, "confirmed", programId);
|
|
213
|
-
// Build social object from metadata
|
|
214
204
|
const social = {};
|
|
215
205
|
if (uriMetadata?.twitter || uriMetadata?.extensions?.twitter) {
|
|
216
206
|
social.twitter = uriMetadata.twitter || uriMetadata.extensions?.twitter;
|
|
@@ -221,7 +211,6 @@ const getTokenInfo = async (tokenAddress, connection, programId) => {
|
|
|
221
211
|
if (uriMetadata?.discord || uriMetadata?.extensions?.discord) {
|
|
222
212
|
social.discord = uriMetadata.discord || uriMetadata.extensions?.discord;
|
|
223
213
|
}
|
|
224
|
-
// Add any other social fields from extensions
|
|
225
214
|
if (uriMetadata?.extensions) {
|
|
226
215
|
Object.keys(uriMetadata.extensions).forEach(key => {
|
|
227
216
|
if (!['twitter', 'telegram', 'discord', 'website'].includes(key) && uriMetadata.extensions[key]) {
|
|
@@ -247,7 +236,6 @@ const getTokenInfo = async (tokenAddress, connection, programId) => {
|
|
|
247
236
|
console.log('error: ', error);
|
|
248
237
|
mint = await (0, spl_token_1.getMint)(connection, tokenAddress, "confirmed", spl_token_1.TOKEN_2022_PROGRAM_ID);
|
|
249
238
|
}
|
|
250
|
-
// Build social object from metadata
|
|
251
239
|
const social = {};
|
|
252
240
|
if (uriMetadata?.twitter || uriMetadata?.extensions?.twitter) {
|
|
253
241
|
social.twitter = uriMetadata.twitter || uriMetadata.extensions?.twitter;
|
|
@@ -258,7 +246,6 @@ const getTokenInfo = async (tokenAddress, connection, programId) => {
|
|
|
258
246
|
if (uriMetadata?.discord || uriMetadata?.extensions?.discord) {
|
|
259
247
|
social.discord = uriMetadata.discord || uriMetadata.extensions?.discord;
|
|
260
248
|
}
|
|
261
|
-
// Add any other social fields from extensions
|
|
262
249
|
if (uriMetadata?.extensions) {
|
|
263
250
|
Object.keys(uriMetadata.extensions).forEach(key => {
|
|
264
251
|
if (!['twitter', 'telegram', 'discord', 'website'].includes(key) && uriMetadata.extensions[key]) {
|
|
@@ -341,7 +328,6 @@ exports.sendTransaction = sendTransaction;
|
|
|
341
328
|
const signAndSendTransaction = async (transaction, connection, signers, options) => {
|
|
342
329
|
console.log('signAndSendTransaction: Starting');
|
|
343
330
|
const signedTx = await (0, exports.signTransaction)(transaction, signers);
|
|
344
|
-
// If feePayer is provided, also sign with feePayer
|
|
345
331
|
if (options?.feePayerSigner) {
|
|
346
332
|
console.log('Signing transaction with fee payer');
|
|
347
333
|
if (transaction instanceof web3_js_1.Transaction) {
|
|
@@ -369,7 +355,6 @@ const discoverTokens = async (ownerAddress, connection) => {
|
|
|
369
355
|
const tokens = await Promise.all(response.value.map(async (accountInfo) => {
|
|
370
356
|
const mintAddress = accountInfo.account.data["parsed"]["info"]["mint"];
|
|
371
357
|
const mint = await (0, exports.getTokenInfo)(new web3_js_1.PublicKey(mintAddress), connection);
|
|
372
|
-
// console.log('mint: ', mint);
|
|
373
358
|
return {
|
|
374
359
|
owner: accountInfo.account.data["parsed"]["info"]["owner"],
|
|
375
360
|
address: accountInfo.account.data["parsed"]["info"]["mint"],
|
|
@@ -383,14 +368,7 @@ const discoverTokens = async (ownerAddress, connection) => {
|
|
|
383
368
|
return tokens;
|
|
384
369
|
};
|
|
385
370
|
exports.discoverTokens = discoverTokens;
|
|
386
|
-
//swap
|
|
387
|
-
//you will. use jupiter for this
|
|
388
371
|
const getJupiterQuote = async (inputMint, outputMint, amount, slippageBps = 50) => {
|
|
389
|
-
// console.log('getJupiterQuote: Starting');
|
|
390
|
-
// console.log('Input mint:', inputMint);
|
|
391
|
-
// console.log('Output mint:', outputMint);
|
|
392
|
-
// console.log('Amount:', amount);
|
|
393
|
-
// console.log('Slippage BPS:', slippageBps);
|
|
394
372
|
const params = new URLSearchParams({
|
|
395
373
|
inputMint,
|
|
396
374
|
outputMint,
|
|
@@ -462,7 +440,6 @@ const buildJupiterSwapTransaction = async (quote, userPublicKey, prioritizationF
|
|
|
462
440
|
try {
|
|
463
441
|
const error = await response.json();
|
|
464
442
|
console.log('Swap build error details:', error);
|
|
465
|
-
// Check if this is the shared accounts error
|
|
466
443
|
if (error.errorCode === 'NOT_SUPPORTED' &&
|
|
467
444
|
error.error?.includes('Simple AMMs are not supported with shared accounts')) {
|
|
468
445
|
console.log('Detected shared accounts incompatibility error');
|
|
@@ -472,7 +449,6 @@ const buildJupiterSwapTransaction = async (quote, userPublicKey, prioritizationF
|
|
|
472
449
|
}
|
|
473
450
|
catch (parseError) {
|
|
474
451
|
console.log('Failed to parse error response:', parseError);
|
|
475
|
-
// Re-throw if it's our custom error
|
|
476
452
|
if (parseError instanceof Error && parseError.message === 'SHARED_ACCOUNTS_NOT_SUPPORTED') {
|
|
477
453
|
throw parseError;
|
|
478
454
|
}
|
|
@@ -505,21 +481,17 @@ const executeJupiterSwap = async (swapParams, connection, payer) => {
|
|
|
505
481
|
let usedSharedAccounts = true;
|
|
506
482
|
try {
|
|
507
483
|
console.log('Building swap transaction with shared accounts enabled...');
|
|
508
|
-
swapResponse = await (0, exports.buildJupiterSwapTransaction)(quote, swapParams.userPublicKey.toString(), undefined, true
|
|
509
|
-
);
|
|
484
|
+
swapResponse = await (0, exports.buildJupiterSwapTransaction)(quote, swapParams.userPublicKey.toString(), undefined, true);
|
|
510
485
|
console.log('Successfully built transaction with shared accounts');
|
|
511
486
|
}
|
|
512
487
|
catch (error) {
|
|
513
488
|
if (error instanceof Error && error.message === 'SHARED_ACCOUNTS_NOT_SUPPORTED') {
|
|
514
489
|
console.log('Shared accounts not supported, retrying without shared accounts...');
|
|
515
|
-
|
|
516
|
-
swapResponse = await (0, exports.buildJupiterSwapTransaction)(quote, swapParams.userPublicKey.toString(), undefined, false // Retry with shared accounts disabled
|
|
517
|
-
);
|
|
490
|
+
swapResponse = await (0, exports.buildJupiterSwapTransaction)(quote, swapParams.userPublicKey.toString(), undefined, false);
|
|
518
491
|
usedSharedAccounts = false;
|
|
519
492
|
console.log('Successfully built transaction without shared accounts');
|
|
520
493
|
}
|
|
521
494
|
else {
|
|
522
|
-
// Re-throw if it's a different error
|
|
523
495
|
throw error;
|
|
524
496
|
}
|
|
525
497
|
}
|
|
@@ -642,8 +614,8 @@ const transformSolanaNFTToUnified = (nft) => {
|
|
|
642
614
|
id: nft.mint,
|
|
643
615
|
name: nft.name,
|
|
644
616
|
symbol: nft.symbol,
|
|
645
|
-
description: '',
|
|
646
|
-
image: undefined,
|
|
617
|
+
description: '',
|
|
618
|
+
image: undefined,
|
|
647
619
|
uri: nft.uri,
|
|
648
620
|
collection: {
|
|
649
621
|
address: nft.mint,
|
|
@@ -651,7 +623,7 @@ const transformSolanaNFTToUnified = (nft) => {
|
|
|
651
623
|
verified: nft.creators?.some(c => c.verified) ?? false,
|
|
652
624
|
},
|
|
653
625
|
chainType: 'SVM',
|
|
654
|
-
balance: '1',
|
|
626
|
+
balance: '1',
|
|
655
627
|
creators: nft.creators,
|
|
656
628
|
sellerFeeBasisPoints: nft.sellerFeeBasisPoints,
|
|
657
629
|
tokenStandard: 'NonFungible',
|
|
@@ -665,16 +637,12 @@ const fetchWalletNfts = async (walletAddress, connection) => {
|
|
|
665
637
|
console.log('fetchWalletNfts: Starting');
|
|
666
638
|
console.log('Wallet address:', walletAddress.toString());
|
|
667
639
|
try {
|
|
668
|
-
// Create UMI instance with the connection's RPC endpoint
|
|
669
640
|
const umi = (0, umi_bundle_defaults_1.createUmi)(connection.rpcEndpoint).use((0, mpl_token_metadata_1.mplTokenMetadata)());
|
|
670
641
|
console.log('UMI instance created with RPC endpoint:', connection.rpcEndpoint);
|
|
671
|
-
// Convert Solana PublicKey to UMI PublicKey
|
|
672
642
|
const owner = (0, umi_1.publicKey)(walletAddress.toString());
|
|
673
643
|
console.log('Fetching NFTs for owner:', owner);
|
|
674
|
-
// Fetch all digital assets owned by the wallet
|
|
675
644
|
const assets = await (0, mpl_token_metadata_1.fetchAllDigitalAssetByOwner)(umi, owner);
|
|
676
645
|
console.log('Fetched assets count:', assets.length);
|
|
677
|
-
// Transform the assets into our SolanaNFT format
|
|
678
646
|
const solanaNfts = assets.map((asset) => {
|
|
679
647
|
const metadata = asset.metadata;
|
|
680
648
|
return {
|
|
@@ -693,7 +661,6 @@ const fetchWalletNfts = async (walletAddress, connection) => {
|
|
|
693
661
|
: undefined,
|
|
694
662
|
};
|
|
695
663
|
});
|
|
696
|
-
// Transform to unified NFT format
|
|
697
664
|
const nfts = solanaNfts.map(exports.transformSolanaNFTToUnified);
|
|
698
665
|
console.log('fetchWalletNfts: Successfully fetched', nfts.length, 'NFTs');
|
|
699
666
|
return nfts;
|
|
@@ -705,45 +672,28 @@ const fetchWalletNfts = async (walletAddress, connection) => {
|
|
|
705
672
|
}
|
|
706
673
|
};
|
|
707
674
|
exports.fetchWalletNfts = fetchWalletNfts;
|
|
708
|
-
/**
|
|
709
|
-
* Get NFT collection details for a specific collection on Solana
|
|
710
|
-
* @param walletAddress - User's wallet public key
|
|
711
|
-
* @param collectionAddress - The NFT collection address (can be collection mint or update authority)
|
|
712
|
-
* @param connection - Solana connection
|
|
713
|
-
* @returns NFTCollection object with collection details and user's NFTs in that collection
|
|
714
|
-
*
|
|
715
|
-
* Note: On Solana, collections can be identified by:
|
|
716
|
-
* - Collection mint address (from collection field in NFT metadata)
|
|
717
|
-
* - Update authority (who can modify the NFT metadata)
|
|
718
|
-
* This function filters by the collection.address field which is typically the collection mint
|
|
719
|
-
*/
|
|
720
675
|
const getNFTCollection = async (walletAddress, collectionAddress, connection) => {
|
|
721
676
|
console.log('getNFTCollection: Starting collection fetch');
|
|
722
677
|
console.log('Wallet address:', walletAddress.toString());
|
|
723
678
|
console.log('Collection Address:', collectionAddress);
|
|
724
679
|
try {
|
|
725
|
-
// Fetch all NFTs for the user
|
|
726
680
|
const allNfts = await (0, exports.fetchWalletNfts)(walletAddress, connection);
|
|
727
681
|
console.log('getNFTCollection: Fetched', allNfts.length, 'total NFTs');
|
|
728
|
-
// Filter NFTs by collection address (case-insensitive comparison)
|
|
729
|
-
// On Solana, we match against the collection.address which is set from the mint
|
|
730
682
|
const collectionNfts = allNfts.filter(nft => nft.collection.address.toLowerCase() === collectionAddress.toLowerCase());
|
|
731
683
|
console.log('getNFTCollection: Found', collectionNfts.length, 'NFTs in collection');
|
|
732
684
|
if (collectionNfts.length === 0) {
|
|
733
685
|
console.log('getNFTCollection: No NFTs found in this collection');
|
|
734
686
|
return null;
|
|
735
687
|
}
|
|
736
|
-
// Extract collection metadata from the first NFT
|
|
737
688
|
const firstNft = collectionNfts[0];
|
|
738
689
|
const rawSvmNft = firstNft.raw?.svm;
|
|
739
|
-
// Aggregate creators from all NFTs (they should be the same for a collection)
|
|
740
690
|
const creators = firstNft.creators;
|
|
741
691
|
const collection = {
|
|
742
692
|
address: collectionAddress,
|
|
743
693
|
name: firstNft.collection.name,
|
|
744
694
|
symbol: firstNft.symbol,
|
|
745
|
-
description: undefined,
|
|
746
|
-
image: undefined,
|
|
695
|
+
description: undefined,
|
|
696
|
+
image: undefined,
|
|
747
697
|
verified: firstNft.collection.verified,
|
|
748
698
|
chainType: 'SVM',
|
|
749
699
|
nfts: collectionNfts,
|
|
@@ -764,4 +714,3 @@ const getNFTCollection = async (walletAddress, collectionAddress, connection) =>
|
|
|
764
714
|
}
|
|
765
715
|
};
|
|
766
716
|
exports.getNFTCollection = getNFTCollection;
|
|
767
|
-
//# sourceMappingURL=utils.js.map
|
package/dist/test.d.ts
CHANGED
package/dist/test.js
CHANGED
|
@@ -7,20 +7,10 @@ const chains_1 = require("viem/chains");
|
|
|
7
7
|
const account_abstraction_1 = require("viem/account-abstraction");
|
|
8
8
|
const constants_1 = require("@zerodev/sdk/constants");
|
|
9
9
|
const aa_service_1 = require("./evm/aa-service");
|
|
10
|
-
|
|
11
|
-
// console.log('mnemonic: ', mnemonic);
|
|
12
|
-
// const seed = VM.mnemonicToSeed(mnemonic)
|
|
10
|
+
const savings_1 = require("./savings");
|
|
13
11
|
const pKey = "";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const evmPrivKey = "0xc9ca95aa5f40bae8b0f741ec89acc07b837590b2f9f818d3439df98c6e4f8dbe";
|
|
17
|
-
const evmPrivateKeyExposed = "0x92cccc7792024dcac5992e5d2986dade41770acfd3dab0fe98ee953ed1bf0c3a";
|
|
18
|
-
// const vm = new SVMVM(seed)
|
|
19
|
-
// const vmFromMnemonic = SVMVM.fromMnemonic(mnemonic)
|
|
20
|
-
// const keyFromMnemonic = vmFromMnemonic.generatePrivateKey(0)
|
|
21
|
-
// console.log('keyFromMnemonic: ', keyFromMnemonic.privateKey.publicKey);
|
|
22
|
-
// const key = vm.generatePrivateKey(0)
|
|
23
|
-
// console.log('key: ', key.privateKey.publicKey);
|
|
12
|
+
const evmPrivKey = "0xcd90354282b35344616d6b53684684bef6e8673ed601d562a5866dc67fafd1ef";
|
|
13
|
+
const evmPrivateKeyExposed = "0xcd90354282b35344616d6b53684684bef6e8673ed601d562a5866dc67fafd1ef";
|
|
24
14
|
const chainConfig = {
|
|
25
15
|
chainId: 123456789,
|
|
26
16
|
name: "Solana",
|
|
@@ -34,7 +24,6 @@ const chainConfig = {
|
|
|
34
24
|
const evmChainConfig = {
|
|
35
25
|
chainId: 8453,
|
|
36
26
|
name: "Base",
|
|
37
|
-
// rpcUrl: "https://base-mainnet.g.alchemy.com/v2/TFdA4BilCnKIwaqtypk0d",
|
|
38
27
|
rpcUrl: chains_1.base.rpcUrls.default.http[0],
|
|
39
28
|
explorerUrl: "https://explorer.ethereum.com",
|
|
40
29
|
nativeToken: { name: "Ethereum", symbol: "ETH", decimals: 18 },
|
|
@@ -69,11 +58,9 @@ const OgChainConfig = {
|
|
|
69
58
|
vmType: "EVM",
|
|
70
59
|
logoUrl: ""
|
|
71
60
|
};
|
|
72
|
-
// const wallet = new SVMChainWallet(chainConfig, testUserKeyPair, 0)
|
|
73
61
|
const wallet = new evm_1.EVMChainWallet(evmChainConfig, evmPrivateKeyExposed, 0);
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
const walletB = new evm_1.EVMChainWallet(evmChainConfig, evmPrivKey, 0); // wallet that has gas
|
|
62
|
+
const walletA = new evm_1.EVMChainWallet(evmChainConfig, evmPrivateKeyExposed, 0);
|
|
63
|
+
const walletB = new evm_1.EVMChainWallet(evmChainConfig, evmPrivKey, 0);
|
|
77
64
|
const SEPOLIA_USDC_ADDRESS = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238';
|
|
78
65
|
const BASE_USDC_ADDRESS = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913';
|
|
79
66
|
const bundlerUrl = `https://api.pimlico.io/v2/${evmChainConfig.chainId}/rpc?apikey=pim_3UEhbTpS98H1XFm8p9kRxg`;
|
|
@@ -84,28 +71,21 @@ const testSessionKeys = async () => {
|
|
|
84
71
|
console.log('walletA native balance: ', walletANativeBalance);
|
|
85
72
|
const walletBNativeBalance = await walletB.getNativeBalance();
|
|
86
73
|
console.log('walletB native balance: ', walletBNativeBalance);
|
|
87
|
-
//wallet B USDC balance
|
|
88
74
|
const walletBUSDCBalance = await walletB.getTokenBalance(BASE_USDC_ADDRESS);
|
|
89
75
|
console.log('walletB USDC balance: ', walletBUSDCBalance);
|
|
90
76
|
console.log("walletB address: ", walletB.address);
|
|
91
|
-
//extend wallet A wallet to a smart wallet with AA support
|
|
92
77
|
const extendedWalletA = await walletA.extend({
|
|
93
78
|
bundlerUrl: bundlerUrl
|
|
94
79
|
});
|
|
95
|
-
// console.log('extendedWalletA: ', extendedWalletA);
|
|
96
80
|
const initializedExtendedWalletA = await extendedWalletA.initialize();
|
|
97
|
-
//now we want to test session keys
|
|
98
81
|
const sessionKeys = await initializedExtendedWalletA.generateSessionKey();
|
|
99
82
|
console.log('sessionKeys: ', sessionKeys);
|
|
100
|
-
|
|
101
|
-
const usdcPermission = initializedExtendedWalletA.createUSDCPermission(BASE_USDC_ADDRESS, "1", // approve up to 100 USDC,
|
|
102
|
-
RECIPIENT);
|
|
83
|
+
const usdcPermission = initializedExtendedWalletA.createUSDCPermission(BASE_USDC_ADDRESS, "1", RECIPIENT);
|
|
103
84
|
console.log('usdcPermission: ', usdcPermission);
|
|
104
85
|
const sessionKeyApproval = await initializedExtendedWalletA.approveSessionKey({
|
|
105
86
|
sessionKeyAddress: sessionKeys.address,
|
|
106
87
|
sessionKeyPrivateKey: sessionKeys.privateKey,
|
|
107
88
|
permissions: [usdcPermission],
|
|
108
|
-
// useSudoPolicy: true,
|
|
109
89
|
});
|
|
110
90
|
const data = (0, viem_1.encodeFunctionData)({
|
|
111
91
|
abi: (0, viem_1.parseAbi)([
|
|
@@ -113,14 +93,13 @@ const testSessionKeys = async () => {
|
|
|
113
93
|
]),
|
|
114
94
|
functionName: 'transfer',
|
|
115
95
|
args: [RECIPIENT, (0, viem_1.parseUnits)('0.01', 6)]
|
|
116
|
-
});
|
|
96
|
+
});
|
|
117
97
|
console.log('sessionKeyApproval: ', sessionKeyApproval);
|
|
118
98
|
const sessionKeyAccount = await (0, aa_service_1.deserializeSessionKey)({
|
|
119
99
|
approval: sessionKeyApproval,
|
|
120
100
|
sessionKeySigner: sessionKeys.signer,
|
|
121
101
|
chain: chains_1.base
|
|
122
102
|
});
|
|
123
|
-
//send transaction using session key
|
|
124
103
|
const sessionAccount = {
|
|
125
104
|
account: sessionKeyAccount,
|
|
126
105
|
address: sessionKeyAccount.address,
|
|
@@ -129,82 +108,23 @@ const testSessionKeys = async () => {
|
|
|
129
108
|
sessionAccount: sessionAccount
|
|
130
109
|
});
|
|
131
110
|
console.log('hash: ', hash);
|
|
132
|
-
//get session key client
|
|
133
111
|
};
|
|
134
112
|
const testModule = async () => {
|
|
135
113
|
const extendedWalletA = await walletA.extend({
|
|
136
114
|
bundlerUrl: bundlerUrl
|
|
137
115
|
});
|
|
138
116
|
const initializedExtendedWalletA = await extendedWalletA.initialize();
|
|
139
|
-
//this is where we will test installation of modules
|
|
140
117
|
};
|
|
141
118
|
const testPrice = async () => {
|
|
142
119
|
walletA.getPrices(['0x98d0baa52b2D063E780DE12F615f963Fe8537553']).then(console.log);
|
|
143
120
|
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// symbol: NATIVE_MINT.toBase58(),
|
|
153
|
-
// decimals: 9
|
|
154
|
-
// }, toBuy, 0.005,).then(res => console.log(res))
|
|
155
|
-
// console.log('wal: ', wal.address);
|
|
156
|
-
// wal.getNativeBalance().then(e => console.log(e))
|
|
121
|
+
const testSavingsPocket = async () => {
|
|
122
|
+
const mnemonic = evm_1.EVMVM.generateMnemonicFromPrivateKey(evmPrivateKeyExposed);
|
|
123
|
+
console.log('mnemonic: ', mnemonic);
|
|
124
|
+
const savingsManager = new savings_1.SavingsManager(mnemonic, evmChainConfig, wallet.index);
|
|
125
|
+
const pocket0 = savingsManager.getPocket(0);
|
|
126
|
+
const balance = await savingsManager.getPocketTokenBalance([], 0);
|
|
127
|
+
console.log('balance: ', balance);
|
|
128
|
+
};
|
|
157
129
|
const RPC_URL = chainConfig.rpcUrl;
|
|
158
130
|
const connection = new web3_js_1.Connection(RPC_URL);
|
|
159
|
-
// const evmConnection = new JsonRpcProvider(evmChainConfig.rpcUrl, {
|
|
160
|
-
// chainId: evmChainConfig.chainId
|
|
161
|
-
// });
|
|
162
|
-
/**
|
|
163
|
-
* Fetches and logs the token metadata for a given mint address.
|
|
164
|
-
* @param mintAddress - The mint address of the token.
|
|
165
|
-
*/
|
|
166
|
-
// get transaction history
|
|
167
|
-
// getTransactionHistory(
|
|
168
|
-
// connection,
|
|
169
|
-
// wallet.address,
|
|
170
|
-
// {
|
|
171
|
-
// limit: 2,
|
|
172
|
-
// before: "5RKG5zKJdz9PqWSav1J358hm1GtfnV1QnYcrw3sRpY7aCgT7f4HTKnp4c9pXrJRujcHHisu3Z6jdtbzq5aTRbikq"
|
|
173
|
-
// }
|
|
174
|
-
// ).then((history: any) => {
|
|
175
|
-
// console.log("Transaction History:", history);
|
|
176
|
-
// }).catch((error: any) => {
|
|
177
|
-
// console.error("Error fetching transaction history:", error);
|
|
178
|
-
// });
|
|
179
|
-
// const client = createPublicClient({
|
|
180
|
-
// chain: base,
|
|
181
|
-
// transport: http(base.rpcUrls.default.http[0]),
|
|
182
|
-
// })
|
|
183
|
-
// getEVMTransactionHistory(client as PublicClient, "0x9C82CE0e125F61AdE50BC0c19638F6Ba93d71D5e", {
|
|
184
|
-
// startBlock: BigInt(37427020)
|
|
185
|
-
// // before: "0xabc..."
|
|
186
|
-
// }).then((history: any) => {
|
|
187
|
-
// console.log("EVM Transaction History:", history);
|
|
188
|
-
// }).catch((error: any) => {
|
|
189
|
-
// console.error("Error fetching EVM transaction history:", error);
|
|
190
|
-
// });
|
|
191
|
-
// discoverNFTs("0x498581ff718922c3f8e6a244956af099b2652b2b", evmChainConfig).then(nfts => {
|
|
192
|
-
// console.log("Discovered NFTs:", nfts);
|
|
193
|
-
// }).catch(error => {
|
|
194
|
-
// console.error("Error discovering NFTs:", error);
|
|
195
|
-
// });
|
|
196
|
-
// fetchWalletNfts(new PublicKey("LebronkTYWjc5J1gtqntdMcbhBXgwRgYcCxMA8JMA17"), connection).then(nfts => {
|
|
197
|
-
// console.log("Discovered NFTs:", nfts);
|
|
198
|
-
// }).catch(error => {
|
|
199
|
-
// console.error("Error discovering NFTs:", error);
|
|
200
|
-
// });
|
|
201
|
-
// discoverTokens(new PublicKey("8AXoqNjEVyhhe43ckDEsYiphWxTBH2oWXkKLZAbCNWLL"), connection).then(tokens => {
|
|
202
|
-
// console.log("Discovered Tokens:", tokens);
|
|
203
|
-
// }).catch(error => {
|
|
204
|
-
// console.error("Error discovering Tokens:", error);
|
|
205
|
-
// });
|
|
206
|
-
// //IIFE
|
|
207
|
-
// (async () => {
|
|
208
|
-
// //GET TOKEN LOGO using @solana/spl-token-metadata
|
|
209
|
-
// })();
|
|
210
|
-
//# sourceMappingURL=test.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { JsonRpcProvider } from 'ethers';
|
|
2
|
+
export interface AmountValidationOptions {
|
|
3
|
+
maxAmount?: bigint;
|
|
4
|
+
minAmount?: bigint;
|
|
5
|
+
requirePositive?: boolean;
|
|
6
|
+
allowFullBalance?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function validateTransferAmount(amount: bigint, balance: bigint, options?: AmountValidationOptions): void;
|
|
9
|
+
export declare function waitForTransaction(txResponse: any, confirmations?: number, timeoutMs?: number): Promise<any>;
|
|
10
|
+
export declare class NonceManager {
|
|
11
|
+
private pendingNonces;
|
|
12
|
+
private provider;
|
|
13
|
+
constructor(provider: JsonRpcProvider);
|
|
14
|
+
getNextNonce(address: string, forceRefresh?: boolean): Promise<number>;
|
|
15
|
+
releaseNonce(address: string, nonce: number): void;
|
|
16
|
+
clearNonces(address: string): void;
|
|
17
|
+
clearAll(): void;
|
|
18
|
+
}
|
|
19
|
+
export declare function estimateGasWithMargin(provider: JsonRpcProvider, tx: any, marginPercent?: number): Promise<bigint>;
|
|
20
|
+
export declare function calculateMaxTransferAmount(balance: bigint, gasLimit: bigint, gasPrice: bigint): bigint;
|
|
21
|
+
export declare function checkSufficientGas(balance: bigint, amount: bigint, gasLimit: bigint, gasPrice: bigint): boolean;
|
|
22
|
+
export declare class TransactionBuilder {
|
|
23
|
+
private provider;
|
|
24
|
+
private tx;
|
|
25
|
+
constructor(provider: JsonRpcProvider);
|
|
26
|
+
to(address: string): this;
|
|
27
|
+
value(amount: bigint): this;
|
|
28
|
+
data(data: string): this;
|
|
29
|
+
gasLimit(limit: bigint): this;
|
|
30
|
+
gasPrice(price: bigint): this;
|
|
31
|
+
maxFeePerGas(fee: bigint): this;
|
|
32
|
+
maxPriorityFeePerGas(fee: bigint): this;
|
|
33
|
+
nonce(nonce: number): this;
|
|
34
|
+
build(options?: {
|
|
35
|
+
addGasMargin?: boolean;
|
|
36
|
+
marginPercent?: number;
|
|
37
|
+
}): Promise<any>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransactionBuilder = exports.NonceManager = void 0;
|
|
4
|
+
exports.validateTransferAmount = validateTransferAmount;
|
|
5
|
+
exports.waitForTransaction = waitForTransaction;
|
|
6
|
+
exports.estimateGasWithMargin = estimateGasWithMargin;
|
|
7
|
+
exports.calculateMaxTransferAmount = calculateMaxTransferAmount;
|
|
8
|
+
exports.checkSufficientGas = checkSufficientGas;
|
|
9
|
+
function validateTransferAmount(amount, balance, options) {
|
|
10
|
+
const { maxAmount, minAmount = 1n, requirePositive = true, allowFullBalance = false } = options || {};
|
|
11
|
+
if (requirePositive && amount <= 0n) {
|
|
12
|
+
throw new Error(`Amount must be positive, got: ${amount}`);
|
|
13
|
+
}
|
|
14
|
+
if (amount < minAmount) {
|
|
15
|
+
throw new Error(`Amount below minimum: ${amount} < ${minAmount}`);
|
|
16
|
+
}
|
|
17
|
+
if (maxAmount && amount > maxAmount) {
|
|
18
|
+
throw new Error(`Amount exceeds maximum: ${amount} > ${maxAmount}`);
|
|
19
|
+
}
|
|
20
|
+
if (amount > balance) {
|
|
21
|
+
throw new Error(`Insufficient balance: ${amount} > ${balance}`);
|
|
22
|
+
}
|
|
23
|
+
if (!allowFullBalance && amount === balance) {
|
|
24
|
+
throw new Error('Transferring entire balance requires explicit confirmation. ' +
|
|
25
|
+
'Set allowFullBalance: true to proceed, or leave some balance for gas fees.');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function waitForTransaction(txResponse, confirmations = 1, timeoutMs = 60000) {
|
|
29
|
+
const txHash = txResponse.hash;
|
|
30
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Transaction confirmation timeout after ${timeoutMs}ms`)), timeoutMs));
|
|
31
|
+
const confirmationPromise = txResponse.wait(confirmations);
|
|
32
|
+
try {
|
|
33
|
+
const receipt = await Promise.race([
|
|
34
|
+
confirmationPromise,
|
|
35
|
+
timeoutPromise
|
|
36
|
+
]);
|
|
37
|
+
return receipt;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error.message.includes('timeout')) {
|
|
41
|
+
throw new Error(`Transaction not confirmed after ${timeoutMs}ms. ` +
|
|
42
|
+
`Hash: ${txHash}. Check block explorer for status.`);
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
class NonceManager {
|
|
48
|
+
pendingNonces = new Map();
|
|
49
|
+
provider;
|
|
50
|
+
constructor(provider) {
|
|
51
|
+
this.provider = provider;
|
|
52
|
+
}
|
|
53
|
+
async getNextNonce(address, forceRefresh = false) {
|
|
54
|
+
if (forceRefresh) {
|
|
55
|
+
this.pendingNonces.delete(address);
|
|
56
|
+
}
|
|
57
|
+
const chainNonce = await this.provider.getTransactionCount(address, 'latest');
|
|
58
|
+
const pendingNonce = this.pendingNonces.get(address);
|
|
59
|
+
const nextNonce = pendingNonce !== undefined
|
|
60
|
+
? Math.max(chainNonce, pendingNonce)
|
|
61
|
+
: chainNonce;
|
|
62
|
+
this.pendingNonces.set(address, nextNonce + 1);
|
|
63
|
+
return nextNonce;
|
|
64
|
+
}
|
|
65
|
+
releaseNonce(address, nonce) {
|
|
66
|
+
const pending = this.pendingNonces.get(address);
|
|
67
|
+
if (pending === nonce + 1) {
|
|
68
|
+
this.pendingNonces.set(address, nonce);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
clearNonces(address) {
|
|
72
|
+
this.pendingNonces.delete(address);
|
|
73
|
+
}
|
|
74
|
+
clearAll() {
|
|
75
|
+
this.pendingNonces.clear();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.NonceManager = NonceManager;
|
|
79
|
+
async function estimateGasWithMargin(provider, tx, marginPercent = 20) {
|
|
80
|
+
try {
|
|
81
|
+
const estimate = await provider.estimateGas(tx);
|
|
82
|
+
const margin = (estimate * BigInt(marginPercent)) / 100n;
|
|
83
|
+
const withMargin = estimate + margin;
|
|
84
|
+
try {
|
|
85
|
+
const block = await provider.getBlock('latest');
|
|
86
|
+
if (block && block.gasLimit) {
|
|
87
|
+
const blockGasLimit = BigInt(block.gasLimit.toString());
|
|
88
|
+
return withMargin > blockGasLimit ? blockGasLimit : withMargin;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
}
|
|
93
|
+
return withMargin;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.warn('Gas estimation failed, using conservative default:', error.message);
|
|
97
|
+
if (tx.data && tx.data !== '0x' && tx.data.length > 2) {
|
|
98
|
+
return 500000n;
|
|
99
|
+
}
|
|
100
|
+
return 21000n;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function calculateMaxTransferAmount(balance, gasLimit, gasPrice) {
|
|
104
|
+
const gasCost = gasLimit * gasPrice;
|
|
105
|
+
if (balance <= gasCost) {
|
|
106
|
+
return 0n;
|
|
107
|
+
}
|
|
108
|
+
return balance - gasCost;
|
|
109
|
+
}
|
|
110
|
+
function checkSufficientGas(balance, amount, gasLimit, gasPrice) {
|
|
111
|
+
const gasCost = gasLimit * gasPrice;
|
|
112
|
+
const totalCost = amount + gasCost;
|
|
113
|
+
if (totalCost > balance) {
|
|
114
|
+
const shortfall = totalCost - balance;
|
|
115
|
+
throw new Error(`Insufficient balance for transaction + gas. ` +
|
|
116
|
+
`Need: ${totalCost}, Have: ${balance}, Short: ${shortfall}`);
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
class TransactionBuilder {
|
|
121
|
+
provider;
|
|
122
|
+
tx = {};
|
|
123
|
+
constructor(provider) {
|
|
124
|
+
this.provider = provider;
|
|
125
|
+
}
|
|
126
|
+
to(address) {
|
|
127
|
+
this.tx.to = address;
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
value(amount) {
|
|
131
|
+
this.tx.value = amount;
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
data(data) {
|
|
135
|
+
this.tx.data = data;
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
gasLimit(limit) {
|
|
139
|
+
this.tx.gasLimit = limit;
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
gasPrice(price) {
|
|
143
|
+
this.tx.gasPrice = price;
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
maxFeePerGas(fee) {
|
|
147
|
+
this.tx.maxFeePerGas = fee;
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
maxPriorityFeePerGas(fee) {
|
|
151
|
+
this.tx.maxPriorityFeePerGas = fee;
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
nonce(nonce) {
|
|
155
|
+
this.tx.nonce = nonce;
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
async build(options) {
|
|
159
|
+
if (!this.tx.to) {
|
|
160
|
+
throw new Error('Transaction must have a "to" address');
|
|
161
|
+
}
|
|
162
|
+
if (!this.tx.gasLimit && options?.addGasMargin) {
|
|
163
|
+
this.tx.gasLimit = await estimateGasWithMargin(this.provider, this.tx, options.marginPercent);
|
|
164
|
+
}
|
|
165
|
+
return { ...this.tx };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.TransactionBuilder = TransactionBuilder;
|