@deserialize/multi-vm-wallet 1.4.2 → 1.5.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 +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 +9 -51
- package/dist/evm/evm.js +338 -76
- 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.js +0 -1
- package/dist/savings/saving-manager.d.ts +10 -11
- package/dist/savings/saving-manager.js +79 -22
- 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 +11 -1
- package/dist/svm/svm.js +267 -27
- 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 +6 -98
- 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 +12 -2
- package/dist/vm.js +61 -16
- package/dist/walletBip32.js +15 -70
- 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/evm/evm.ts +554 -8
- 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/saving-manager.ts +526 -16
- package/utils/savings/savings-operations.ts +509 -0
- package/utils/savings/validation.ts +187 -0
- package/utils/svm/svm.ts +476 -5
- package/utils/test.ts +2 -2
- package/utils/transaction-utils.ts +394 -0
- package/utils/types.ts +100 -0
- package/utils/vm-validation.ts +280 -0
- package/utils/vm.ts +197 -10
- package/utils/walletBip32.ts +39 -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/saving-manager.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/svm/svm.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { Connection, Keypair, PublicKey, Transaction, VersionedTransaction } fro
|
|
|
2
2
|
import { SVMDeriveChildPrivateKey } from "../walletBip32";
|
|
3
3
|
import { VM } from "../vm";
|
|
4
4
|
import { ChainWallet } from "../IChainWallet";
|
|
5
|
-
import { Balance, ChainWalletConfig, UserTokenBalance, TokenInfo, TransactionResult, NFT } from "../types";
|
|
5
|
+
import { Balance, ChainWalletConfig, UserTokenBalance, TokenInfo, TransactionResult, NFT, DiscoveredWallet, WalletDiscoveryOptions, WalletDiscoveryResult, PocketDiscoveryOptions } from "../types";
|
|
6
|
+
import { VMValidation, sanitizeError, logSafeError } from "../vm-validation";
|
|
6
7
|
import {
|
|
7
8
|
getSvmNativeBalance,
|
|
8
9
|
getTokenBalance,
|
|
@@ -70,15 +71,24 @@ export class SVMVM extends VM<PublicKey, Keypair, Connection> {
|
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
generatePrivateKey(index: number, seed?: string, mnemonic?: string, derivationPath = this.derivationPath) {
|
|
73
|
-
|
|
74
|
+
// Validate inputs
|
|
75
|
+
VMValidation.validateIndex(index, 'Wallet index');
|
|
76
|
+
VMValidation.validateDerivationPath(derivationPath + index + "'", 'SVM');
|
|
77
|
+
|
|
78
|
+
let _seed: string;
|
|
74
79
|
|
|
75
80
|
if (seed) {
|
|
76
|
-
|
|
81
|
+
VMValidation.validateSeed(seed);
|
|
82
|
+
_seed = seed;
|
|
77
83
|
} else if (mnemonic) {
|
|
78
|
-
|
|
84
|
+
VMValidation.validateMnemonic(mnemonic);
|
|
85
|
+
_seed = VM.mnemonicToSeed(mnemonic);
|
|
79
86
|
} else {
|
|
80
|
-
|
|
87
|
+
// Check if VM has been disposed
|
|
88
|
+
this.checkNotDisposed();
|
|
89
|
+
_seed = this.seed;
|
|
81
90
|
}
|
|
91
|
+
|
|
82
92
|
const privateKey = SVMDeriveChildPrivateKey(_seed, index, derivationPath);
|
|
83
93
|
return { privateKey, index };
|
|
84
94
|
}
|
|
@@ -89,6 +99,467 @@ export class SVMVM extends VM<PublicKey, Keypair, Connection> {
|
|
|
89
99
|
const seed = VM.mnemonicToSeed(mnemonic)
|
|
90
100
|
return new SVMVM(seed)
|
|
91
101
|
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Derive a savings account using BIP-44 account index
|
|
105
|
+
*
|
|
106
|
+
* Main wallet uses: m/44'/501'/0'/0' (account index 0)
|
|
107
|
+
* Savings accounts use: m/44'/501'/N'/0' (account index N)
|
|
108
|
+
*
|
|
109
|
+
* @param accountIndex - The BIP-44 account index (1 for first savings, 2 for second, etc.)
|
|
110
|
+
* @returns Object containing privateKey, address, and derivation path
|
|
111
|
+
*/
|
|
112
|
+
deriveSavingsAccount(accountIndex: number): { privateKey: Keypair; address: PublicKey; derivationPath: string } {
|
|
113
|
+
const derivationPath = `m/44'/501'/${accountIndex}'/0'`;
|
|
114
|
+
const keypair = SVMDeriveChildPrivateKey(this.seed, 0, `m/44'/501'/${accountIndex}'/`);
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
privateKey: keypair,
|
|
118
|
+
address: keypair.publicKey,
|
|
119
|
+
derivationPath
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Discover wallets with native SOL balances using BIP-44 derivation
|
|
125
|
+
*
|
|
126
|
+
* Scans wallet indices to find wallets containing native SOL tokens.
|
|
127
|
+
* Follows BIP-44 gap limit standard: stops after 20 consecutive empty wallets.
|
|
128
|
+
*
|
|
129
|
+
* @param connection - Solana Connection instance
|
|
130
|
+
* @param options - Discovery options (gap limit, parallel checking, callbacks, etc.)
|
|
131
|
+
* @returns Discovery result with found wallets and statistics
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* const vm = new SVMVM(seed);
|
|
136
|
+
* const connection = new Connection('https://api.mainnet-beta.solana.com');
|
|
137
|
+
*
|
|
138
|
+
* // Sequential checking (safer for rate limits)
|
|
139
|
+
* const result = await vm.discoverWallets(connection, {
|
|
140
|
+
* gapLimit: 20,
|
|
141
|
+
* onProgress: (current, total, found) => console.log(`Checked ${current}/${total}, found ${found}`),
|
|
142
|
+
* onDiscovered: (wallet) => console.log(`Found wallet at index ${wallet.index}`)
|
|
143
|
+
* });
|
|
144
|
+
*
|
|
145
|
+
* // Parallel checking (faster but more aggressive)
|
|
146
|
+
* const result = await vm.discoverWallets(connection, {
|
|
147
|
+
* checkInParallel: true,
|
|
148
|
+
* batchSize: 10,
|
|
149
|
+
* gapLimit: 20
|
|
150
|
+
* });
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
async discoverWallets(
|
|
154
|
+
connection: Connection,
|
|
155
|
+
options?: WalletDiscoveryOptions
|
|
156
|
+
): Promise<WalletDiscoveryResult> {
|
|
157
|
+
const startTime = Date.now();
|
|
158
|
+
|
|
159
|
+
// Default options - parallel checking for speed
|
|
160
|
+
const {
|
|
161
|
+
startIndex = 0,
|
|
162
|
+
maxIndex = 100,
|
|
163
|
+
gapLimit = 20,
|
|
164
|
+
minBalance = BigInt(0),
|
|
165
|
+
includeZeroBalance = false,
|
|
166
|
+
includePrivateKeys = false,
|
|
167
|
+
checkInParallel = true, // Default to parallel for speed
|
|
168
|
+
batchSize = 10, // Larger batch for better performance
|
|
169
|
+
checkDelay = checkInParallel ? 200 : 50,
|
|
170
|
+
onProgress,
|
|
171
|
+
onDiscovered
|
|
172
|
+
} = options || {};
|
|
173
|
+
|
|
174
|
+
const discovered: DiscoveredWallet[] = [];
|
|
175
|
+
let consecutiveEmpty = 0;
|
|
176
|
+
let scannedIndices = 0;
|
|
177
|
+
let stoppedByGapLimit = false;
|
|
178
|
+
|
|
179
|
+
if (checkInParallel) {
|
|
180
|
+
// Parallel checking with batches
|
|
181
|
+
for (let i = startIndex; i <= maxIndex; i += batchSize) {
|
|
182
|
+
const batchEnd = Math.min(i + batchSize, maxIndex + 1);
|
|
183
|
+
const batchPromises: Promise<DiscoveredWallet | null>[] = [];
|
|
184
|
+
|
|
185
|
+
// Create batch of parallel checks
|
|
186
|
+
for (let j = i; j < batchEnd; j++) {
|
|
187
|
+
batchPromises.push(this.checkWalletBalance(j, connection));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Wait for batch to complete
|
|
191
|
+
const batchResults = await Promise.all(batchPromises);
|
|
192
|
+
|
|
193
|
+
// Process results
|
|
194
|
+
for (let k = 0; k < batchResults.length; k++) {
|
|
195
|
+
const index = i + k;
|
|
196
|
+
const wallet = batchResults[k];
|
|
197
|
+
scannedIndices++;
|
|
198
|
+
|
|
199
|
+
if (wallet) {
|
|
200
|
+
const hasBalance = wallet.nativeBalance.amount > minBalance;
|
|
201
|
+
|
|
202
|
+
if (hasBalance || includeZeroBalance) {
|
|
203
|
+
if (!includePrivateKeys) {
|
|
204
|
+
delete wallet.privateKey;
|
|
205
|
+
}
|
|
206
|
+
discovered.push(wallet);
|
|
207
|
+
consecutiveEmpty = 0;
|
|
208
|
+
|
|
209
|
+
onDiscovered?.(wallet);
|
|
210
|
+
} else {
|
|
211
|
+
consecutiveEmpty++;
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
consecutiveEmpty++;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
onProgress?.(scannedIndices, maxIndex - startIndex + 1, discovered.length);
|
|
218
|
+
|
|
219
|
+
// Check gap limit
|
|
220
|
+
if (consecutiveEmpty >= gapLimit) {
|
|
221
|
+
stoppedByGapLimit = true;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (stoppedByGapLimit) {
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Delay between batches
|
|
231
|
+
if (batchEnd <= maxIndex) {
|
|
232
|
+
await this.sleep(checkDelay);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
// Sequential checking
|
|
237
|
+
for (let i = startIndex; i <= maxIndex; i++) {
|
|
238
|
+
const wallet = await this.checkWalletBalance(i, connection);
|
|
239
|
+
scannedIndices++;
|
|
240
|
+
|
|
241
|
+
if (wallet) {
|
|
242
|
+
const hasBalance = wallet.nativeBalance.amount > minBalance;
|
|
243
|
+
|
|
244
|
+
if (hasBalance || includeZeroBalance) {
|
|
245
|
+
if (!includePrivateKeys) {
|
|
246
|
+
delete wallet.privateKey;
|
|
247
|
+
}
|
|
248
|
+
discovered.push(wallet);
|
|
249
|
+
consecutiveEmpty = 0;
|
|
250
|
+
|
|
251
|
+
onDiscovered?.(wallet);
|
|
252
|
+
} else {
|
|
253
|
+
consecutiveEmpty++;
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
consecutiveEmpty++;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
onProgress?.(scannedIndices, maxIndex - startIndex + 1, discovered.length);
|
|
260
|
+
|
|
261
|
+
// Check gap limit
|
|
262
|
+
if (consecutiveEmpty >= gapLimit) {
|
|
263
|
+
stoppedByGapLimit = true;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Delay between checks
|
|
268
|
+
if (i < maxIndex) {
|
|
269
|
+
await this.sleep(checkDelay);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Calculate total balance
|
|
275
|
+
const totalBalance = discovered.reduce((sum, wallet) => sum + wallet.nativeBalance.amount, BigInt(0));
|
|
276
|
+
|
|
277
|
+
const duration = Date.now() - startTime;
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
discovered,
|
|
281
|
+
scannedIndices,
|
|
282
|
+
highestIndex: startIndex + scannedIndices - 1,
|
|
283
|
+
totalBalance,
|
|
284
|
+
stoppedByGapLimit,
|
|
285
|
+
duration
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Check balance for a specific wallet index with retry logic
|
|
291
|
+
* @private
|
|
292
|
+
*/
|
|
293
|
+
private async checkWalletBalance(
|
|
294
|
+
index: number,
|
|
295
|
+
connection: Connection,
|
|
296
|
+
maxRetries: number = 3
|
|
297
|
+
): Promise<DiscoveredWallet | null> {
|
|
298
|
+
const derivationPath = `m/44'/501'/${index}'/0'`;
|
|
299
|
+
|
|
300
|
+
// Derive wallet using hardened derivation (required for Solana)
|
|
301
|
+
const keypair = SVMDeriveChildPrivateKey(this.seed, 0, `m/44'/501'/${index}'/`);
|
|
302
|
+
const address = keypair.publicKey;
|
|
303
|
+
|
|
304
|
+
// Retry logic with exponential backoff
|
|
305
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
306
|
+
try {
|
|
307
|
+
// Get balance in lamports
|
|
308
|
+
const balanceLamports = await connection.getBalance(address);
|
|
309
|
+
|
|
310
|
+
// Convert lamports to SOL (9 decimals)
|
|
311
|
+
const balanceSOL = balanceLamports / 1_000_000_000;
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
index,
|
|
315
|
+
address: address.toString(),
|
|
316
|
+
derivationPath,
|
|
317
|
+
nativeBalance: {
|
|
318
|
+
amount: BigInt(balanceLamports),
|
|
319
|
+
formatted: balanceSOL,
|
|
320
|
+
symbol: 'SOL'
|
|
321
|
+
},
|
|
322
|
+
privateKey: base58.encode(keypair.secretKey)
|
|
323
|
+
};
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (attempt === maxRetries - 1) {
|
|
326
|
+
console.error(`Failed to check balance for index ${index} after ${maxRetries} attempts:`, error);
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Exponential backoff: 1s, 2s, 4s
|
|
331
|
+
const backoffMs = 1000 * Math.pow(2, attempt);
|
|
332
|
+
await this.sleep(backoffMs);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Sleep helper for rate limiting
|
|
341
|
+
* @private
|
|
342
|
+
*/
|
|
343
|
+
private sleep(ms: number): Promise<void> {
|
|
344
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Discover savings pockets with native SOL balances using BIP-44 derivation
|
|
349
|
+
*
|
|
350
|
+
* Scans pocket account indices to find pockets containing native SOL.
|
|
351
|
+
* Pockets use derivation path: m/44'/501'/{accountIndex+1}'/0' (all hardened for Solana)
|
|
352
|
+
*
|
|
353
|
+
* @param connection - Solana Connection instance
|
|
354
|
+
* @param options - Discovery options (gap limit, parallel checking, walletIndex, callbacks, etc.)
|
|
355
|
+
* @returns Discovery result with found pockets and statistics
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```typescript
|
|
359
|
+
* const vm = new SVMVM(seed);
|
|
360
|
+
* const connection = new Connection('https://api.mainnet-beta.solana.com');
|
|
361
|
+
*
|
|
362
|
+
* // Discover pockets for wallet index 0
|
|
363
|
+
* const result = await vm.discoverPockets(connection, {
|
|
364
|
+
* walletIndex: 0,
|
|
365
|
+
* gapLimit: 20,
|
|
366
|
+
* onDiscovered: (pocket) => console.log(`Found pocket at account ${pocket.index}`)
|
|
367
|
+
* });
|
|
368
|
+
*
|
|
369
|
+
* console.log(`Found ${result.discovered.length} pockets with SOL`);
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
async discoverPockets(
|
|
373
|
+
connection: Connection,
|
|
374
|
+
options?: PocketDiscoveryOptions
|
|
375
|
+
): Promise<WalletDiscoveryResult> {
|
|
376
|
+
const startTime = Date.now();
|
|
377
|
+
|
|
378
|
+
// Default options - parallel checking for speed
|
|
379
|
+
const {
|
|
380
|
+
startIndex = 0,
|
|
381
|
+
maxIndex = 100,
|
|
382
|
+
gapLimit = 20,
|
|
383
|
+
minBalance = BigInt(0),
|
|
384
|
+
includeZeroBalance = false,
|
|
385
|
+
includePrivateKeys = false,
|
|
386
|
+
checkInParallel = true,
|
|
387
|
+
batchSize = 10,
|
|
388
|
+
checkDelay = checkInParallel ? 200 : 50,
|
|
389
|
+
onProgress,
|
|
390
|
+
onDiscovered,
|
|
391
|
+
walletIndex = 0,
|
|
392
|
+
} = options || {};
|
|
393
|
+
|
|
394
|
+
const discovered: DiscoveredWallet[] = [];
|
|
395
|
+
let consecutiveEmpty = 0;
|
|
396
|
+
let scannedIndices = 0;
|
|
397
|
+
let stoppedByGapLimit = false;
|
|
398
|
+
|
|
399
|
+
if (checkInParallel) {
|
|
400
|
+
// Parallel checking with batches
|
|
401
|
+
for (let i = startIndex; i <= maxIndex; i += batchSize) {
|
|
402
|
+
const batchEnd = Math.min(i + batchSize, maxIndex + 1);
|
|
403
|
+
const batchPromises: Promise<DiscoveredWallet | null>[] = [];
|
|
404
|
+
|
|
405
|
+
// Create batch of parallel checks
|
|
406
|
+
for (let j = i; j < batchEnd; j++) {
|
|
407
|
+
batchPromises.push(this.checkPocketBalance(j, walletIndex, connection));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Wait for batch to complete
|
|
411
|
+
const batchResults = await Promise.all(batchPromises);
|
|
412
|
+
|
|
413
|
+
// Process results
|
|
414
|
+
for (let k = 0; k < batchResults.length; k++) {
|
|
415
|
+
const index = i + k;
|
|
416
|
+
const pocket = batchResults[k];
|
|
417
|
+
scannedIndices++;
|
|
418
|
+
|
|
419
|
+
if (pocket) {
|
|
420
|
+
const hasBalance = pocket.nativeBalance.amount > minBalance;
|
|
421
|
+
|
|
422
|
+
if (hasBalance || includeZeroBalance) {
|
|
423
|
+
if (!includePrivateKeys) {
|
|
424
|
+
delete pocket.privateKey;
|
|
425
|
+
}
|
|
426
|
+
discovered.push(pocket);
|
|
427
|
+
consecutiveEmpty = 0;
|
|
428
|
+
|
|
429
|
+
onDiscovered?.(pocket);
|
|
430
|
+
} else {
|
|
431
|
+
consecutiveEmpty++;
|
|
432
|
+
}
|
|
433
|
+
} else {
|
|
434
|
+
consecutiveEmpty++;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
onProgress?.(scannedIndices, maxIndex - startIndex + 1, discovered.length);
|
|
438
|
+
|
|
439
|
+
// Check gap limit
|
|
440
|
+
if (consecutiveEmpty >= gapLimit) {
|
|
441
|
+
stoppedByGapLimit = true;
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (stoppedByGapLimit) {
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Delay between batches
|
|
451
|
+
if (batchEnd <= maxIndex) {
|
|
452
|
+
await this.sleep(checkDelay);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
// Sequential checking
|
|
457
|
+
for (let i = startIndex; i <= maxIndex; i++) {
|
|
458
|
+
const pocket = await this.checkPocketBalance(i, walletIndex, connection);
|
|
459
|
+
scannedIndices++;
|
|
460
|
+
|
|
461
|
+
if (pocket) {
|
|
462
|
+
const hasBalance = pocket.nativeBalance.amount > minBalance;
|
|
463
|
+
|
|
464
|
+
if (hasBalance || includeZeroBalance) {
|
|
465
|
+
if (!includePrivateKeys) {
|
|
466
|
+
delete pocket.privateKey;
|
|
467
|
+
}
|
|
468
|
+
discovered.push(pocket);
|
|
469
|
+
consecutiveEmpty = 0;
|
|
470
|
+
|
|
471
|
+
onDiscovered?.(pocket);
|
|
472
|
+
} else {
|
|
473
|
+
consecutiveEmpty++;
|
|
474
|
+
}
|
|
475
|
+
} else {
|
|
476
|
+
consecutiveEmpty++;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
onProgress?.(scannedIndices, maxIndex - startIndex + 1, discovered.length);
|
|
480
|
+
|
|
481
|
+
// Check gap limit
|
|
482
|
+
if (consecutiveEmpty >= gapLimit) {
|
|
483
|
+
stoppedByGapLimit = true;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Delay between checks
|
|
488
|
+
if (i < maxIndex) {
|
|
489
|
+
await this.sleep(checkDelay);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Calculate total balance
|
|
495
|
+
const totalBalance = discovered.reduce((sum, pocket) => sum + pocket.nativeBalance.amount, BigInt(0));
|
|
496
|
+
|
|
497
|
+
const duration = Date.now() - startTime;
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
discovered,
|
|
501
|
+
scannedIndices,
|
|
502
|
+
highestIndex: startIndex + scannedIndices - 1,
|
|
503
|
+
totalBalance,
|
|
504
|
+
stoppedByGapLimit,
|
|
505
|
+
duration
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Check balance for a specific pocket at account index
|
|
511
|
+
* @private
|
|
512
|
+
*/
|
|
513
|
+
private async checkPocketBalance(
|
|
514
|
+
accountIndex: number,
|
|
515
|
+
walletIndex: number,
|
|
516
|
+
connection: Connection,
|
|
517
|
+
maxRetries: number = 3
|
|
518
|
+
): Promise<DiscoveredWallet | null> {
|
|
519
|
+
// Pocket derivation: m/44'/501'/{accountIndex+1}'/0' (all hardened for Solana)
|
|
520
|
+
const pocketIndex = accountIndex + 1;
|
|
521
|
+
const derivationPath = `m/44'/501'/${pocketIndex}'/0'`;
|
|
522
|
+
|
|
523
|
+
// Derive wallet using hardened derivation (required for Solana)
|
|
524
|
+
// Note: For pockets, we use pocketIndex for account and walletIndex is not used in derivation
|
|
525
|
+
// This matches the EVM pocket pattern but adapted for Solana's all-hardened requirement
|
|
526
|
+
const keypair = SVMDeriveChildPrivateKey(this.seed, 0, `m/44'/501'/${pocketIndex}'/`);
|
|
527
|
+
const address = keypair.publicKey;
|
|
528
|
+
|
|
529
|
+
// Retry logic with exponential backoff
|
|
530
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
531
|
+
try {
|
|
532
|
+
// Get balance in lamports
|
|
533
|
+
const balanceLamports = await connection.getBalance(address);
|
|
534
|
+
|
|
535
|
+
// Convert lamports to SOL (9 decimals)
|
|
536
|
+
const balanceSOL = balanceLamports / 1_000_000_000;
|
|
537
|
+
|
|
538
|
+
return {
|
|
539
|
+
index: accountIndex,
|
|
540
|
+
address: address.toString(),
|
|
541
|
+
derivationPath,
|
|
542
|
+
nativeBalance: {
|
|
543
|
+
amount: BigInt(balanceLamports),
|
|
544
|
+
formatted: balanceSOL,
|
|
545
|
+
symbol: 'SOL'
|
|
546
|
+
},
|
|
547
|
+
privateKey: base58.encode(keypair.secretKey)
|
|
548
|
+
};
|
|
549
|
+
} catch (error) {
|
|
550
|
+
if (attempt === maxRetries - 1) {
|
|
551
|
+
console.error(`Failed to check pocket at account ${accountIndex} after ${maxRetries} attempts:`, error);
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Exponential backoff: 1s, 2s, 4s
|
|
556
|
+
const backoffMs = 1000 * Math.pow(2, attempt);
|
|
557
|
+
await this.sleep(backoffMs);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
92
563
|
}
|
|
93
564
|
|
|
94
565
|
export class SVMChainWallet extends ChainWallet<PublicKey, Keypair, Connection> {
|
package/utils/test.ts
CHANGED
|
@@ -34,8 +34,8 @@ const pKey = ""
|
|
|
34
34
|
// const testUserKeyPair = Keypair.fromSecretKey(base58.decode(pKey));
|
|
35
35
|
// const x = testUserKeyPair instanceof Keypair;
|
|
36
36
|
|
|
37
|
-
const evmPrivKey = ""
|
|
38
|
-
const evmPrivateKeyExposed = ""
|
|
37
|
+
const evmPrivKey = "0xcd90354282b35344616d6b53684684bef6e8673ed601d562a5866dc67fafd1ef"
|
|
38
|
+
const evmPrivateKeyExposed = "0xcd90354282b35344616d6b53684684bef6e8673ed601d562a5866dc67fafd1ef"
|
|
39
39
|
// const vm = new SVMVM(seed)
|
|
40
40
|
|
|
41
41
|
|