@t402/wdk 2.4.0 → 2.6.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/dist/cjs/adapters/index.d.ts +198 -1
- package/dist/cjs/adapters/index.js +255 -0
- package/dist/cjs/adapters/index.js.map +1 -1
- package/dist/cjs/adapters/svm-adapter.d.ts +146 -2
- package/dist/cjs/adapters/svm-adapter.js +255 -2
- package/dist/cjs/adapters/svm-adapter.js.map +1 -1
- package/dist/cjs/adapters/ton-adapter.d.ts +57 -2
- package/dist/cjs/adapters/ton-adapter.js +75 -2
- package/dist/cjs/adapters/ton-adapter.js.map +1 -1
- package/dist/cjs/adapters/tron-adapter.d.ts +57 -2
- package/dist/cjs/adapters/tron-adapter.js +101 -0
- package/dist/cjs/adapters/tron-adapter.js.map +1 -1
- package/dist/cjs/index-DnEI5M6d.d.ts +1798 -0
- package/dist/cjs/index.d.ts +702 -1118
- package/dist/cjs/index.js +3905 -246
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/integrations/index.d.ts +9 -0
- package/dist/cjs/integrations/index.js +249 -0
- package/dist/cjs/integrations/index.js.map +1 -0
- package/dist/cjs/testing/index.d.ts +62 -0
- package/dist/cjs/testing/index.js +129 -0
- package/dist/cjs/testing/index.js.map +1 -0
- package/dist/cjs/types-BwK8Xgvg.d.ts +967 -0
- package/dist/esm/adapters/index.d.mts +198 -1
- package/dist/esm/adapters/index.mjs +14 -3
- package/dist/esm/adapters/svm-adapter.d.mts +146 -2
- package/dist/esm/adapters/svm-adapter.mjs +18 -3
- package/dist/esm/adapters/ton-adapter.d.mts +57 -2
- package/dist/esm/adapters/ton-adapter.mjs +8 -3
- package/dist/esm/adapters/tron-adapter.d.mts +57 -2
- package/dist/esm/adapters/tron-adapter.mjs +2 -1
- package/dist/esm/chunk-2KWVW77U.mjs +353 -0
- package/dist/esm/chunk-2KWVW77U.mjs.map +1 -0
- package/dist/esm/chunk-7CG77QAN.mjs +153 -0
- package/dist/esm/chunk-7CG77QAN.mjs.map +1 -0
- package/dist/esm/chunk-BJTO5JO5.mjs +11 -0
- package/dist/esm/chunk-BJTO5JO5.mjs.map +1 -0
- package/dist/esm/{chunk-YWBJJV5M.mjs → chunk-KWX6CJIH.mjs} +72 -1
- package/dist/esm/chunk-KWX6CJIH.mjs.map +1 -0
- package/dist/esm/{chunk-HB2DGKQ3.mjs → chunk-QZKUU2O6.mjs} +102 -1
- package/dist/esm/chunk-QZKUU2O6.mjs.map +1 -0
- package/dist/esm/chunk-TVSNUSFZ.mjs +219 -0
- package/dist/esm/chunk-TVSNUSFZ.mjs.map +1 -0
- package/dist/esm/index-D5kvtDfm.d.mts +1798 -0
- package/dist/esm/index.d.mts +702 -1118
- package/dist/esm/index.mjs +2934 -70
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/integrations/index.d.mts +9 -0
- package/dist/esm/integrations/index.mjs +16 -0
- package/dist/esm/integrations/index.mjs.map +1 -0
- package/dist/esm/testing/index.d.mts +62 -0
- package/dist/esm/testing/index.mjs +101 -0
- package/dist/esm/testing/index.mjs.map +1 -0
- package/dist/esm/types-BwK8Xgvg.d.mts +967 -0
- package/package.json +69 -20
- package/dist/cjs/types-V7c-qhn6.d.ts +0 -489
- package/dist/esm/chunk-HB2DGKQ3.mjs.map +0 -1
- package/dist/esm/chunk-MCFHZSF7.mjs +0 -107
- package/dist/esm/chunk-MCFHZSF7.mjs.map +0 -1
- package/dist/esm/chunk-YWBJJV5M.mjs.map +0 -1
- package/dist/esm/types-V7c-qhn6.d.mts +0 -489
package/dist/cjs/index.js
CHANGED
|
@@ -30,24 +30,36 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AmountLimitProvider: () => AmountLimitProvider,
|
|
33
34
|
BalanceCache: () => BalanceCache,
|
|
34
35
|
BalanceError: () => BalanceError,
|
|
36
|
+
BlacklistProvider: () => BlacklistProvider,
|
|
35
37
|
BridgeError: () => BridgeError,
|
|
38
|
+
BridgeTracker: () => BridgeTracker,
|
|
39
|
+
CHAIN_REGISTRY: () => CHAIN_REGISTRY,
|
|
36
40
|
CHAIN_TOKENS: () => CHAIN_TOKENS,
|
|
37
41
|
ChainError: () => ChainError,
|
|
42
|
+
ComplianceManager: () => ComplianceManager,
|
|
38
43
|
DEFAULT_BALANCE_CACHE_CONFIG: () => DEFAULT_BALANCE_CACHE_CONFIG,
|
|
39
44
|
DEFAULT_CACHE_CONFIG: () => DEFAULT_CACHE_CONFIG,
|
|
40
45
|
DEFAULT_CHAINS: () => DEFAULT_CHAINS,
|
|
41
46
|
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
42
47
|
DEFAULT_RPC_ENDPOINTS: () => DEFAULT_RPC_ENDPOINTS,
|
|
48
|
+
FailoverProvider: () => FailoverProvider,
|
|
43
49
|
HardwareWalletError: () => HardwareWalletError,
|
|
44
50
|
HardwareWalletErrorCode: () => HardwareWalletErrorCode,
|
|
51
|
+
InMemoryIdempotencyManager: () => InMemoryIdempotencyManager,
|
|
52
|
+
InMemoryReceiptStore: () => InMemoryReceiptStore,
|
|
45
53
|
LAYERZERO_ENDPOINT_IDS: () => import_evm3.LAYERZERO_ENDPOINT_IDS,
|
|
46
54
|
LedgerSigner: () => LedgerSigner,
|
|
47
55
|
MockWDKSigner: () => MockWDKSigner,
|
|
56
|
+
MoonpayOnRampProvider: () => MoonpayOnRampProvider,
|
|
57
|
+
NonceManager: () => NonceManager,
|
|
48
58
|
RPCError: () => RPCError,
|
|
59
|
+
SUPPORTED_WDK_RANGE: () => SUPPORTED_WDK_RANGE,
|
|
49
60
|
SignerError: () => SignerError,
|
|
50
61
|
SigningError: () => SigningError,
|
|
62
|
+
T402EventEmitter: () => T402EventEmitter,
|
|
51
63
|
T402WDK: () => T402WDK,
|
|
52
64
|
TTLCache: () => TTLCache,
|
|
53
65
|
TransactionError: () => TransactionError,
|
|
@@ -66,34 +78,336 @@ __export(index_exports, {
|
|
|
66
78
|
WDKTronSignerAdapter: () => WDKTronSignerAdapter,
|
|
67
79
|
WDK_COMPATIBILITY: () => WDK_COMPATIBILITY,
|
|
68
80
|
WdkBridge: () => WdkBridge,
|
|
81
|
+
WdkIndexerVerifier: () => WdkIndexerVerifier,
|
|
82
|
+
WebhookManager: () => WebhookManager,
|
|
83
|
+
buildVersionedTransaction: () => buildVersionedTransaction,
|
|
69
84
|
checkWalletEvmCompatibility: () => checkWalletEvmCompatibility,
|
|
70
85
|
checkWdkCompatibility: () => checkWdkCompatibility,
|
|
86
|
+
compareSemver: () => compareSemver,
|
|
87
|
+
createBackup: () => createBackup,
|
|
88
|
+
createCorrelationId: () => createCorrelationId,
|
|
71
89
|
createDirectBridge: () => createDirectBridge,
|
|
90
|
+
createFacilitatorSigners: () => createFacilitatorSigners,
|
|
91
|
+
createFailoverProvider: () => createFailoverProvider,
|
|
92
|
+
createIndexerVerifier: () => createIndexerVerifier,
|
|
72
93
|
createLedgerSigner: () => createLedgerSigner,
|
|
94
|
+
createSIWxSigners: () => createSIWxSigners,
|
|
73
95
|
createTrezorSigner: () => createTrezorSigner,
|
|
74
96
|
createWDKSigner: () => createWDKSigner,
|
|
75
97
|
createWDKSvmSigner: () => createWDKSvmSigner,
|
|
76
98
|
createWDKTonSigner: () => createWDKTonSigner,
|
|
77
99
|
createWDKTronSigner: () => createWDKTronSigner,
|
|
100
|
+
createWdkA2APaymentClient: () => createWdkA2APaymentClient,
|
|
101
|
+
createWdkMoneyParser: () => createWdkMoneyParser,
|
|
102
|
+
decryptSeed: () => decryptSeed,
|
|
103
|
+
defaultLogger: () => defaultLogger,
|
|
104
|
+
deriveATAAddress: () => deriveATAAddress,
|
|
78
105
|
detectHardwareWalletSupport: () => detectHardwareWalletSupport,
|
|
106
|
+
encryptSeed: () => encryptSeed,
|
|
107
|
+
generateIdempotencyKey: () => generateIdempotencyKey,
|
|
79
108
|
getBridgeableChains: () => import_evm3.getBridgeableChains,
|
|
80
109
|
getChainFromNetwork: () => getChainFromNetwork,
|
|
81
110
|
getChainId: () => getChainId,
|
|
111
|
+
getChainsByFamily: () => getChainsByFamily,
|
|
112
|
+
getJettonWalletAddress: () => getJettonWalletAddress,
|
|
113
|
+
getMoonpayCurrencyCode: () => getMoonpayCurrencyCode,
|
|
82
114
|
getNetworkFromChain: () => getNetworkFromChain,
|
|
83
115
|
getPreferredToken: () => getPreferredToken,
|
|
116
|
+
getPricingProvider: () => getPricingProvider,
|
|
117
|
+
getRecentPriorityFees: () => getRecentPriorityFees,
|
|
118
|
+
getRegistryByCaip2: () => getRegistryByCaip2,
|
|
119
|
+
getSecretManager: () => getSecretManager,
|
|
120
|
+
getTokenProgram: () => getTokenProgram,
|
|
121
|
+
getTransferFee: () => getTransferFee,
|
|
84
122
|
getUsdt0Chains: () => getUsdt0Chains,
|
|
85
123
|
getWalletModuleMinVersion: () => getWalletModuleMinVersion,
|
|
86
124
|
hasErrorCode: () => hasErrorCode,
|
|
87
125
|
isHardwareWalletSupported: () => isHardwareWalletSupported,
|
|
126
|
+
isPricingProviderRegistered: () => isPricingProviderRegistered,
|
|
88
127
|
isWDKError: () => isWDKError,
|
|
128
|
+
mapLayerZeroStatus: () => mapLayerZeroStatus,
|
|
129
|
+
noopLogger: () => noopLogger,
|
|
89
130
|
normalizeChainConfig: () => normalizeChainConfig,
|
|
131
|
+
parseSemver: () => parseSemver,
|
|
132
|
+
registerPricingProvider: () => registerPricingProvider,
|
|
133
|
+
registerSecretManager: () => registerSecretManager,
|
|
134
|
+
resolveATA: () => resolveATA,
|
|
135
|
+
resolveAssetForNetwork: () => resolveAssetForNetwork,
|
|
136
|
+
resolveRpcUrl: () => resolveRpcUrl,
|
|
137
|
+
rotateSeedPassword: () => rotateSeedPassword,
|
|
138
|
+
satisfiesSemverRange: () => satisfiesSemverRange,
|
|
90
139
|
supportsBridging: () => import_evm3.supportsBridging,
|
|
140
|
+
toAtomicUnits: () => toAtomicUnits,
|
|
141
|
+
toFacilitatorWdkSigner: () => toFacilitatorWdkSigner,
|
|
142
|
+
toSIWxSigner: () => toSIWxSigner,
|
|
143
|
+
transferWithPriorityFee: () => transferWithPriorityFee,
|
|
144
|
+
validatePaymentAddress: () => validatePaymentAddress,
|
|
145
|
+
verifyBackup: () => verifyBackup,
|
|
146
|
+
waitForJettonTransfer: () => waitForJettonTransfer,
|
|
91
147
|
withRetry: () => withRetry,
|
|
92
148
|
withTimeout: () => withTimeout,
|
|
93
149
|
wrapError: () => wrapError
|
|
94
150
|
});
|
|
95
151
|
module.exports = __toCommonJS(index_exports);
|
|
96
152
|
|
|
153
|
+
// src/secret.ts
|
|
154
|
+
var _secretManager = null;
|
|
155
|
+
async function encryptSeed(seedPhrase, password) {
|
|
156
|
+
if (!seedPhrase || typeof seedPhrase !== "string") {
|
|
157
|
+
throw new Error("Seed phrase is required and must be a string");
|
|
158
|
+
}
|
|
159
|
+
if (!password || typeof password !== "string") {
|
|
160
|
+
throw new Error("Password is required and must be a string");
|
|
161
|
+
}
|
|
162
|
+
const crypto = await import("crypto");
|
|
163
|
+
const salt = crypto.randomBytes(32);
|
|
164
|
+
const iv = crypto.randomBytes(16);
|
|
165
|
+
const key = crypto.pbkdf2Sync(password, salt, 1e5, 32, "sha256");
|
|
166
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
167
|
+
const encrypted = Buffer.concat([cipher.update(seedPhrase, "utf8"), cipher.final()]);
|
|
168
|
+
const authTag = cipher.getAuthTag();
|
|
169
|
+
return {
|
|
170
|
+
ciphertext: Buffer.concat([encrypted, authTag]).toString("base64"),
|
|
171
|
+
algorithm: "aes-256-gcm",
|
|
172
|
+
kdf: {
|
|
173
|
+
salt: salt.toString("base64"),
|
|
174
|
+
iterations: 1e5,
|
|
175
|
+
keyLength: 32,
|
|
176
|
+
hash: "sha256"
|
|
177
|
+
},
|
|
178
|
+
iv: iv.toString("base64"),
|
|
179
|
+
version: 1
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
async function decryptSeed(encrypted, password) {
|
|
183
|
+
if (!encrypted || typeof encrypted !== "object") {
|
|
184
|
+
throw new Error("Encrypted seed data is required");
|
|
185
|
+
}
|
|
186
|
+
if (!password || typeof password !== "string") {
|
|
187
|
+
throw new Error("Password is required and must be a string");
|
|
188
|
+
}
|
|
189
|
+
const crypto = await import("crypto");
|
|
190
|
+
const salt = Buffer.from(encrypted.kdf.salt, "base64");
|
|
191
|
+
const iv = Buffer.from(encrypted.iv, "base64");
|
|
192
|
+
const key = crypto.pbkdf2Sync(
|
|
193
|
+
password,
|
|
194
|
+
salt,
|
|
195
|
+
encrypted.kdf.iterations,
|
|
196
|
+
encrypted.kdf.keyLength,
|
|
197
|
+
encrypted.kdf.hash
|
|
198
|
+
);
|
|
199
|
+
const data = Buffer.from(encrypted.ciphertext, "base64");
|
|
200
|
+
const authTag = data.subarray(-16);
|
|
201
|
+
const ciphertext = data.subarray(0, -16);
|
|
202
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
203
|
+
decipher.setAuthTag(authTag);
|
|
204
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
205
|
+
}
|
|
206
|
+
async function rotateSeedPassword(encrypted, oldPassword, newPassword, options) {
|
|
207
|
+
if (!newPassword || typeof newPassword !== "string") {
|
|
208
|
+
throw new Error("New password is required and must be a string");
|
|
209
|
+
}
|
|
210
|
+
const manager = getSecretManager();
|
|
211
|
+
const seedPhrase = await manager.decrypt(encrypted, oldPassword);
|
|
212
|
+
const iterations = options?.iterations ?? encrypted.kdf.iterations;
|
|
213
|
+
const newVersion = options?.iterations && options.iterations !== encrypted.kdf.iterations ? 2 : encrypted.version;
|
|
214
|
+
const crypto = await import("crypto");
|
|
215
|
+
const salt = crypto.randomBytes(32);
|
|
216
|
+
const iv = crypto.randomBytes(16);
|
|
217
|
+
const key = crypto.pbkdf2Sync(newPassword, salt, iterations, 32, "sha256");
|
|
218
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
219
|
+
const encryptedData = Buffer.concat([cipher.update(seedPhrase, "utf8"), cipher.final()]);
|
|
220
|
+
const authTag = cipher.getAuthTag();
|
|
221
|
+
return {
|
|
222
|
+
ciphertext: Buffer.concat([encryptedData, authTag]).toString("base64"),
|
|
223
|
+
algorithm: "aes-256-gcm",
|
|
224
|
+
kdf: {
|
|
225
|
+
salt: salt.toString("base64"),
|
|
226
|
+
iterations,
|
|
227
|
+
keyLength: 32,
|
|
228
|
+
hash: "sha256"
|
|
229
|
+
},
|
|
230
|
+
iv: iv.toString("base64"),
|
|
231
|
+
version: newVersion
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
function registerSecretManager(manager) {
|
|
235
|
+
_secretManager = manager;
|
|
236
|
+
}
|
|
237
|
+
function getSecretManager() {
|
|
238
|
+
if (_secretManager) {
|
|
239
|
+
return _secretManager;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
encrypt: encryptSeed,
|
|
243
|
+
decrypt: decryptSeed
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
async function createBackup(seedPhrase, password, metadata) {
|
|
247
|
+
if (!seedPhrase || typeof seedPhrase !== "string") {
|
|
248
|
+
throw new Error("Seed phrase is required and must be a string");
|
|
249
|
+
}
|
|
250
|
+
if (!password || typeof password !== "string") {
|
|
251
|
+
throw new Error("Password is required and must be a string");
|
|
252
|
+
}
|
|
253
|
+
const manager = getSecretManager();
|
|
254
|
+
const encrypted = await manager.encrypt(seedPhrase, password);
|
|
255
|
+
const envelope = {
|
|
256
|
+
encrypted,
|
|
257
|
+
metadata
|
|
258
|
+
};
|
|
259
|
+
return JSON.stringify(envelope, null, 2);
|
|
260
|
+
}
|
|
261
|
+
async function verifyBackup(backup, password, expectedAddresses) {
|
|
262
|
+
let envelope;
|
|
263
|
+
try {
|
|
264
|
+
envelope = JSON.parse(backup);
|
|
265
|
+
} catch {
|
|
266
|
+
return { valid: false, addressMatch: false };
|
|
267
|
+
}
|
|
268
|
+
if (!envelope.encrypted || !envelope.metadata) {
|
|
269
|
+
return { valid: false, addressMatch: false };
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
const manager = getSecretManager();
|
|
273
|
+
await manager.decrypt(envelope.encrypted, password);
|
|
274
|
+
} catch {
|
|
275
|
+
return { valid: false, addressMatch: false };
|
|
276
|
+
}
|
|
277
|
+
let addressMatch = true;
|
|
278
|
+
if (expectedAddresses && envelope.metadata.addressHints) {
|
|
279
|
+
for (const [chain, expectedAddr] of Object.entries(expectedAddresses)) {
|
|
280
|
+
const hint = envelope.metadata.addressHints[chain];
|
|
281
|
+
if (hint && hint.toLowerCase() !== expectedAddr.toLowerCase()) {
|
|
282
|
+
addressMatch = false;
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return { valid: true, addressMatch, metadata: envelope.metadata };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/events.ts
|
|
291
|
+
var T402EventEmitter = class {
|
|
292
|
+
handlers = /* @__PURE__ */ new Map();
|
|
293
|
+
on(event, handler) {
|
|
294
|
+
let set = this.handlers.get(event);
|
|
295
|
+
if (!set) {
|
|
296
|
+
set = /* @__PURE__ */ new Set();
|
|
297
|
+
this.handlers.set(event, set);
|
|
298
|
+
}
|
|
299
|
+
set.add(handler);
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
off(event, handler) {
|
|
303
|
+
const set = this.handlers.get(event);
|
|
304
|
+
if (set) {
|
|
305
|
+
set.delete(handler);
|
|
306
|
+
if (set.size === 0) {
|
|
307
|
+
this.handlers.delete(event);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
once(event, handler) {
|
|
313
|
+
const wrapper = (data) => {
|
|
314
|
+
this.off(event, wrapper);
|
|
315
|
+
handler(data);
|
|
316
|
+
};
|
|
317
|
+
return this.on(event, wrapper);
|
|
318
|
+
}
|
|
319
|
+
emit(event, data) {
|
|
320
|
+
const set = this.handlers.get(event);
|
|
321
|
+
if (!set || set.size === 0) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
for (const handler of set) {
|
|
325
|
+
handler(data);
|
|
326
|
+
}
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
removeAllListeners(event) {
|
|
330
|
+
if (event) {
|
|
331
|
+
this.handlers.delete(event);
|
|
332
|
+
} else {
|
|
333
|
+
this.handlers.clear();
|
|
334
|
+
}
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
listenerCount(event) {
|
|
338
|
+
return this.handlers.get(event)?.size ?? 0;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// src/receipts.ts
|
|
343
|
+
var InMemoryReceiptStore = class {
|
|
344
|
+
receipts = /* @__PURE__ */ new Map();
|
|
345
|
+
async save(receipt) {
|
|
346
|
+
this.receipts.set(receipt.id, receipt);
|
|
347
|
+
}
|
|
348
|
+
async getById(id) {
|
|
349
|
+
return this.receipts.get(id) ?? null;
|
|
350
|
+
}
|
|
351
|
+
async query(filter) {
|
|
352
|
+
let results = Array.from(this.receipts.values());
|
|
353
|
+
results = applyFilter(results, filter);
|
|
354
|
+
return results;
|
|
355
|
+
}
|
|
356
|
+
async getAll() {
|
|
357
|
+
return Array.from(this.receipts.values());
|
|
358
|
+
}
|
|
359
|
+
async count(filter) {
|
|
360
|
+
if (!filter) {
|
|
361
|
+
return this.receipts.size;
|
|
362
|
+
}
|
|
363
|
+
const filtered = applyFilter(Array.from(this.receipts.values()), filter);
|
|
364
|
+
return filtered.length;
|
|
365
|
+
}
|
|
366
|
+
async clear() {
|
|
367
|
+
this.receipts.clear();
|
|
368
|
+
}
|
|
369
|
+
async exportJSON() {
|
|
370
|
+
return JSON.stringify(Array.from(this.receipts.values()), null, 2);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
function applyFilter(receipts, filter) {
|
|
374
|
+
if (!filter) return receipts;
|
|
375
|
+
let results = receipts;
|
|
376
|
+
if (filter.network !== void 0) {
|
|
377
|
+
results = results.filter((r) => r.network === filter.network);
|
|
378
|
+
}
|
|
379
|
+
if (filter.chainFamily !== void 0) {
|
|
380
|
+
results = results.filter((r) => r.chainFamily === filter.chainFamily);
|
|
381
|
+
}
|
|
382
|
+
if (filter.success !== void 0) {
|
|
383
|
+
results = results.filter((r) => r.success === filter.success);
|
|
384
|
+
}
|
|
385
|
+
if (filter.fromDate !== void 0) {
|
|
386
|
+
const from = filter.fromDate;
|
|
387
|
+
results = results.filter((r) => r.timestamp >= from);
|
|
388
|
+
}
|
|
389
|
+
if (filter.toDate !== void 0) {
|
|
390
|
+
const to = filter.toDate;
|
|
391
|
+
results = results.filter((r) => r.timestamp <= to);
|
|
392
|
+
}
|
|
393
|
+
if (filter.minAmount !== void 0) {
|
|
394
|
+
const min = BigInt(filter.minAmount);
|
|
395
|
+
results = results.filter((r) => BigInt(r.amount) >= min);
|
|
396
|
+
}
|
|
397
|
+
if (filter.maxAmount !== void 0) {
|
|
398
|
+
const max = BigInt(filter.maxAmount);
|
|
399
|
+
results = results.filter((r) => BigInt(r.amount) <= max);
|
|
400
|
+
}
|
|
401
|
+
results.sort((a, b) => a.timestamp > b.timestamp ? -1 : a.timestamp < b.timestamp ? 1 : 0);
|
|
402
|
+
if (filter.offset !== void 0 && filter.offset > 0) {
|
|
403
|
+
results = results.slice(filter.offset);
|
|
404
|
+
}
|
|
405
|
+
if (filter.limit !== void 0 && filter.limit > 0) {
|
|
406
|
+
results = results.slice(0, filter.limit);
|
|
407
|
+
}
|
|
408
|
+
return results;
|
|
409
|
+
}
|
|
410
|
+
|
|
97
411
|
// src/adapters/ton-adapter.ts
|
|
98
412
|
var WDKTonAddress = class {
|
|
99
413
|
constructor(_address) {
|
|
@@ -200,6 +514,75 @@ var WDKTonSignerAdapter = class {
|
|
|
200
514
|
return this._account;
|
|
201
515
|
}
|
|
202
516
|
};
|
|
517
|
+
async function waitForJettonTransfer(apiEndpoint, params) {
|
|
518
|
+
const timeout = params.timeoutMs ?? 12e4;
|
|
519
|
+
const pollInterval = params.pollIntervalMs ?? 3e3;
|
|
520
|
+
const startTime = Date.now();
|
|
521
|
+
params.onStatusChange?.("pending");
|
|
522
|
+
while (Date.now() - startTime < timeout) {
|
|
523
|
+
try {
|
|
524
|
+
const response = await fetch(
|
|
525
|
+
`${apiEndpoint}/getTransactions?hash=${encodeURIComponent(params.externalMessageHash)}&limit=1`
|
|
526
|
+
);
|
|
527
|
+
if (!response.ok) {
|
|
528
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
const data = await response.json();
|
|
532
|
+
if (data.ok && data.result && data.result.length > 0) {
|
|
533
|
+
const tx = data.result[0];
|
|
534
|
+
if (tx.out_msgs && tx.out_msgs.length > 0) {
|
|
535
|
+
params.onStatusChange?.("confirming");
|
|
536
|
+
const txHash = tx.transaction_id?.hash ?? params.externalMessageHash;
|
|
537
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
538
|
+
params.onStatusChange?.("completed");
|
|
539
|
+
return {
|
|
540
|
+
success: true,
|
|
541
|
+
status: "completed",
|
|
542
|
+
transactionHash: txHash
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
} catch {
|
|
547
|
+
}
|
|
548
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
549
|
+
}
|
|
550
|
+
params.onStatusChange?.("timeout");
|
|
551
|
+
return {
|
|
552
|
+
success: false,
|
|
553
|
+
status: "timeout",
|
|
554
|
+
error: `Jetton transfer not confirmed within ${timeout}ms`
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
async function getJettonWalletAddress(apiEndpoint, ownerAddress, jettonMaster) {
|
|
558
|
+
const response = await fetch(`${apiEndpoint}/runGetMethod`, {
|
|
559
|
+
method: "POST",
|
|
560
|
+
headers: { "Content-Type": "application/json" },
|
|
561
|
+
body: JSON.stringify({
|
|
562
|
+
address: jettonMaster,
|
|
563
|
+
method: "get_wallet_address",
|
|
564
|
+
stack: [["tvm.Slice", ownerAddress]]
|
|
565
|
+
})
|
|
566
|
+
});
|
|
567
|
+
if (!response.ok) {
|
|
568
|
+
throw new Error(`Failed to resolve Jetton wallet address: ${response.status}`);
|
|
569
|
+
}
|
|
570
|
+
const result = await response.json();
|
|
571
|
+
if (!result.ok || !result.result) {
|
|
572
|
+
throw new Error("Failed to resolve Jetton wallet address: invalid response");
|
|
573
|
+
}
|
|
574
|
+
if (result.result.exit_code !== void 0 && result.result.exit_code !== 0) {
|
|
575
|
+
throw new Error(`Jetton master GET method failed with exit code ${result.result.exit_code}`);
|
|
576
|
+
}
|
|
577
|
+
if (!result.result.stack || result.result.stack.length === 0) {
|
|
578
|
+
throw new Error("Failed to resolve Jetton wallet address: empty stack");
|
|
579
|
+
}
|
|
580
|
+
const walletAddress = result.result.stack[0]?.[1];
|
|
581
|
+
if (!walletAddress) {
|
|
582
|
+
throw new Error("Failed to parse Jetton wallet address from response");
|
|
583
|
+
}
|
|
584
|
+
return walletAddress;
|
|
585
|
+
}
|
|
203
586
|
async function createWDKTonSigner(account) {
|
|
204
587
|
const adapter = new WDKTonSignerAdapter(account);
|
|
205
588
|
await adapter.initialize();
|
|
@@ -302,6 +685,245 @@ var WDKSvmSignerAdapter = class {
|
|
|
302
685
|
return this._account.transfer(params);
|
|
303
686
|
}
|
|
304
687
|
};
|
|
688
|
+
var COMPUTE_BUDGET_PROGRAM_ID = "ComputeBudget111111111111111111111111111111";
|
|
689
|
+
var TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
|
690
|
+
var TOKEN_2022_PROGRAM_ID = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb";
|
|
691
|
+
var ASSOCIATED_TOKEN_PROGRAM_ID = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
|
|
692
|
+
function buildVersionedTransaction(adapter, params) {
|
|
693
|
+
if (!adapter.isInitialized) {
|
|
694
|
+
throw new Error("Adapter must be initialized before building transactions");
|
|
695
|
+
}
|
|
696
|
+
const allInstructions = [];
|
|
697
|
+
if (params.priorityFee) {
|
|
698
|
+
allInstructions.push(
|
|
699
|
+
createSetComputeUnitLimitInstruction(params.priorityFee.computeUnits ?? 2e5)
|
|
700
|
+
);
|
|
701
|
+
allInstructions.push(createSetComputeUnitPriceInstruction(params.priorityFee.microLamports));
|
|
702
|
+
}
|
|
703
|
+
allInstructions.push(...params.instructions);
|
|
704
|
+
const lookupTableCount = params.addressLookupTableAccounts?.length ?? 0;
|
|
705
|
+
const header = {
|
|
706
|
+
version: 0,
|
|
707
|
+
numSigners: 1,
|
|
708
|
+
numReadonlySignedAccounts: 0,
|
|
709
|
+
numReadonlyUnsignedAccounts: 0,
|
|
710
|
+
feePayer: adapter.address,
|
|
711
|
+
instructions: allInstructions,
|
|
712
|
+
lookupTableCount
|
|
713
|
+
};
|
|
714
|
+
return serializeVersionedMessage(header);
|
|
715
|
+
}
|
|
716
|
+
async function transferWithPriorityFee(adapter, params) {
|
|
717
|
+
if (!adapter.isInitialized) {
|
|
718
|
+
throw new Error("Adapter must be initialized before transferring");
|
|
719
|
+
}
|
|
720
|
+
return adapter.transfer({
|
|
721
|
+
token: params.token,
|
|
722
|
+
recipient: params.recipient,
|
|
723
|
+
amount: params.amount
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
async function getRecentPriorityFees(rpcUrl) {
|
|
727
|
+
const response = await fetch(rpcUrl, {
|
|
728
|
+
method: "POST",
|
|
729
|
+
headers: { "Content-Type": "application/json" },
|
|
730
|
+
body: JSON.stringify({
|
|
731
|
+
jsonrpc: "2.0",
|
|
732
|
+
id: 1,
|
|
733
|
+
method: "getRecentPrioritizationFees",
|
|
734
|
+
params: []
|
|
735
|
+
})
|
|
736
|
+
});
|
|
737
|
+
if (!response.ok) {
|
|
738
|
+
throw new Error(`RPC request failed: ${response.status}`);
|
|
739
|
+
}
|
|
740
|
+
const result = await response.json();
|
|
741
|
+
if (result.error) {
|
|
742
|
+
throw new Error(`RPC error: ${result.error.message}`);
|
|
743
|
+
}
|
|
744
|
+
const fees = result.result ?? [];
|
|
745
|
+
if (fees.length === 0) {
|
|
746
|
+
return { low: 0, medium: 0, high: 0 };
|
|
747
|
+
}
|
|
748
|
+
const sorted = fees.map((f) => f.prioritizationFee).sort((a, b) => a - b);
|
|
749
|
+
const p25 = sorted[Math.floor(sorted.length * 0.25)] ?? 0;
|
|
750
|
+
const p50 = sorted[Math.floor(sorted.length * 0.5)] ?? 0;
|
|
751
|
+
const p75 = sorted[Math.floor(sorted.length * 0.75)] ?? 0;
|
|
752
|
+
return { low: p25, medium: p50, high: p75 };
|
|
753
|
+
}
|
|
754
|
+
async function resolveATA(rpcUrl, owner, mint) {
|
|
755
|
+
const ataAddress = deriveATAAddress(owner, mint);
|
|
756
|
+
const response = await fetch(rpcUrl, {
|
|
757
|
+
method: "POST",
|
|
758
|
+
headers: { "Content-Type": "application/json" },
|
|
759
|
+
body: JSON.stringify({
|
|
760
|
+
jsonrpc: "2.0",
|
|
761
|
+
id: 1,
|
|
762
|
+
method: "getAccountInfo",
|
|
763
|
+
params: [ataAddress, { encoding: "base64" }]
|
|
764
|
+
})
|
|
765
|
+
});
|
|
766
|
+
if (!response.ok) {
|
|
767
|
+
throw new Error(`RPC request failed: ${response.status}`);
|
|
768
|
+
}
|
|
769
|
+
const result = await response.json();
|
|
770
|
+
if (result.error) {
|
|
771
|
+
throw new Error(`RPC error: ${result.error.message}`);
|
|
772
|
+
}
|
|
773
|
+
const exists = result.result?.value != null;
|
|
774
|
+
if (exists) {
|
|
775
|
+
return { address: ataAddress, exists: true };
|
|
776
|
+
}
|
|
777
|
+
return {
|
|
778
|
+
address: ataAddress,
|
|
779
|
+
exists: false,
|
|
780
|
+
createInstruction: {
|
|
781
|
+
programId: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
782
|
+
keys: [
|
|
783
|
+
{ pubkey: owner, isSigner: true, isWritable: true },
|
|
784
|
+
{ pubkey: ataAddress, isSigner: false, isWritable: true },
|
|
785
|
+
{ pubkey: owner, isSigner: false, isWritable: false },
|
|
786
|
+
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
787
|
+
{ pubkey: "11111111111111111111111111111111", isSigner: false, isWritable: false },
|
|
788
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }
|
|
789
|
+
],
|
|
790
|
+
data: new Uint8Array(0)
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
async function getTokenProgram(rpcUrl, mint) {
|
|
795
|
+
const response = await fetch(rpcUrl, {
|
|
796
|
+
method: "POST",
|
|
797
|
+
headers: { "Content-Type": "application/json" },
|
|
798
|
+
body: JSON.stringify({
|
|
799
|
+
jsonrpc: "2.0",
|
|
800
|
+
id: 1,
|
|
801
|
+
method: "getAccountInfo",
|
|
802
|
+
params: [mint, { encoding: "jsonParsed" }]
|
|
803
|
+
})
|
|
804
|
+
});
|
|
805
|
+
if (!response.ok) {
|
|
806
|
+
throw new Error(`RPC request failed: ${response.status}`);
|
|
807
|
+
}
|
|
808
|
+
const result = await response.json();
|
|
809
|
+
if (result.error) {
|
|
810
|
+
throw new Error(`RPC error: ${result.error.message}`);
|
|
811
|
+
}
|
|
812
|
+
if (!result.result?.value) {
|
|
813
|
+
throw new Error(`Mint account not found: ${mint}`);
|
|
814
|
+
}
|
|
815
|
+
const owner = result.result.value.owner;
|
|
816
|
+
if (owner === TOKEN_2022_PROGRAM_ID) {
|
|
817
|
+
return "Token-2022";
|
|
818
|
+
}
|
|
819
|
+
return "Token";
|
|
820
|
+
}
|
|
821
|
+
async function getTransferFee(rpcUrl, mint, amount) {
|
|
822
|
+
const response = await fetch(rpcUrl, {
|
|
823
|
+
method: "POST",
|
|
824
|
+
headers: { "Content-Type": "application/json" },
|
|
825
|
+
body: JSON.stringify({
|
|
826
|
+
jsonrpc: "2.0",
|
|
827
|
+
id: 1,
|
|
828
|
+
method: "getAccountInfo",
|
|
829
|
+
params: [mint, { encoding: "jsonParsed" }]
|
|
830
|
+
})
|
|
831
|
+
});
|
|
832
|
+
if (!response.ok) {
|
|
833
|
+
throw new Error(`RPC request failed: ${response.status}`);
|
|
834
|
+
}
|
|
835
|
+
const result = await response.json();
|
|
836
|
+
if (result.error) {
|
|
837
|
+
throw new Error(`RPC error: ${result.error.message}`);
|
|
838
|
+
}
|
|
839
|
+
if (!result.result?.value) {
|
|
840
|
+
throw new Error(`Mint account not found: ${mint}`);
|
|
841
|
+
}
|
|
842
|
+
const extensions = result.result.value.data?.parsed?.info?.extensions ?? [];
|
|
843
|
+
const transferFeeExt = extensions.find((e) => e.extension === "transferFeeConfig");
|
|
844
|
+
if (!transferFeeExt?.state) {
|
|
845
|
+
return { fee: 0n, netAmount: amount, transferFeeBasisPoints: 0, maximumFee: 0n };
|
|
846
|
+
}
|
|
847
|
+
const feeConfig = transferFeeExt.state.newerTransferFee ?? transferFeeExt.state.olderTransferFee;
|
|
848
|
+
if (!feeConfig) {
|
|
849
|
+
return { fee: 0n, netAmount: amount, transferFeeBasisPoints: 0, maximumFee: 0n };
|
|
850
|
+
}
|
|
851
|
+
const basisPoints = feeConfig.transferFeeBasisPoints;
|
|
852
|
+
const maxFee = BigInt(feeConfig.maximumFee);
|
|
853
|
+
let fee = amount * BigInt(basisPoints) / 10000n;
|
|
854
|
+
if (fee > maxFee) fee = maxFee;
|
|
855
|
+
return {
|
|
856
|
+
fee,
|
|
857
|
+
netAmount: amount - fee,
|
|
858
|
+
transferFeeBasisPoints: basisPoints,
|
|
859
|
+
maximumFee: maxFee
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
function createSetComputeUnitLimitInstruction(units) {
|
|
863
|
+
const data = new Uint8Array(5);
|
|
864
|
+
data[0] = 2;
|
|
865
|
+
const view = new DataView(data.buffer);
|
|
866
|
+
view.setUint32(1, units, true);
|
|
867
|
+
return {
|
|
868
|
+
programId: COMPUTE_BUDGET_PROGRAM_ID,
|
|
869
|
+
keys: [],
|
|
870
|
+
data
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function createSetComputeUnitPriceInstruction(microLamports) {
|
|
874
|
+
const data = new Uint8Array(9);
|
|
875
|
+
data[0] = 3;
|
|
876
|
+
const view = new DataView(data.buffer);
|
|
877
|
+
view.setUint32(1, microLamports & 4294967295, true);
|
|
878
|
+
view.setUint32(5, Math.floor(microLamports / 4294967296) & 4294967295, true);
|
|
879
|
+
return {
|
|
880
|
+
programId: COMPUTE_BUDGET_PROGRAM_ID,
|
|
881
|
+
keys: [],
|
|
882
|
+
data
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
function deriveATAAddress(owner, mint) {
|
|
886
|
+
return `ata:${owner}:${mint}`;
|
|
887
|
+
}
|
|
888
|
+
function serializeVersionedMessage(header) {
|
|
889
|
+
const encoder = new TextEncoder();
|
|
890
|
+
const parts = [];
|
|
891
|
+
parts.push(new Uint8Array([128]));
|
|
892
|
+
parts.push(
|
|
893
|
+
new Uint8Array([
|
|
894
|
+
header.numSigners,
|
|
895
|
+
header.numReadonlySignedAccounts,
|
|
896
|
+
header.numReadonlyUnsignedAccounts
|
|
897
|
+
])
|
|
898
|
+
);
|
|
899
|
+
const feePayerBytes = encoder.encode(header.feePayer);
|
|
900
|
+
parts.push(new Uint8Array([feePayerBytes.length]));
|
|
901
|
+
parts.push(feePayerBytes);
|
|
902
|
+
parts.push(new Uint8Array([header.instructions.length]));
|
|
903
|
+
for (const ix of header.instructions) {
|
|
904
|
+
const pidBytes = encoder.encode(ix.programId);
|
|
905
|
+
parts.push(new Uint8Array([pidBytes.length]));
|
|
906
|
+
parts.push(pidBytes);
|
|
907
|
+
parts.push(new Uint8Array([ix.keys.length]));
|
|
908
|
+
for (const key of ix.keys) {
|
|
909
|
+
const keyBytes = encoder.encode(key.pubkey);
|
|
910
|
+
parts.push(new Uint8Array([keyBytes.length]));
|
|
911
|
+
parts.push(keyBytes);
|
|
912
|
+
parts.push(new Uint8Array([key.isSigner ? 1 : 0, key.isWritable ? 1 : 0]));
|
|
913
|
+
}
|
|
914
|
+
parts.push(new Uint8Array([ix.data.length]));
|
|
915
|
+
parts.push(ix.data);
|
|
916
|
+
}
|
|
917
|
+
parts.push(new Uint8Array([header.lookupTableCount]));
|
|
918
|
+
const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
|
|
919
|
+
const result = new Uint8Array(totalLength);
|
|
920
|
+
let offset = 0;
|
|
921
|
+
for (const part of parts) {
|
|
922
|
+
result.set(part, offset);
|
|
923
|
+
offset += part.length;
|
|
924
|
+
}
|
|
925
|
+
return result;
|
|
926
|
+
}
|
|
305
927
|
async function createWDKSvmSigner(account) {
|
|
306
928
|
const adapter = new WDKSvmSignerAdapter(account);
|
|
307
929
|
await adapter.initialize();
|
|
@@ -314,6 +936,7 @@ var WDKTronSignerAdapter = class {
|
|
|
314
936
|
_address = null;
|
|
315
937
|
_initialized = false;
|
|
316
938
|
_rpcUrl;
|
|
939
|
+
_energyProvider = null;
|
|
317
940
|
constructor(account, rpcUrl = "https://api.trongrid.io") {
|
|
318
941
|
if (!account) {
|
|
319
942
|
throw new Error("WDK TRON account is required");
|
|
@@ -415,6 +1038,106 @@ var WDKTronSignerAdapter = class {
|
|
|
415
1038
|
);
|
|
416
1039
|
}
|
|
417
1040
|
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Estimate the energy required for a TRC20 transfer.
|
|
1043
|
+
*
|
|
1044
|
+
* Uses the `wallet/triggerconstantcontract` API to simulate the transfer
|
|
1045
|
+
* and return the energy/bandwidth requirements.
|
|
1046
|
+
*
|
|
1047
|
+
* @param params - Transaction parameters to simulate
|
|
1048
|
+
* @returns Energy estimation result
|
|
1049
|
+
*/
|
|
1050
|
+
async estimateEnergy(params) {
|
|
1051
|
+
if (!this._address) {
|
|
1052
|
+
throw new Error("TRON signer not initialized. Call initialize() first.");
|
|
1053
|
+
}
|
|
1054
|
+
const functionSelector = "transfer(address,uint256)";
|
|
1055
|
+
const toAddressHex = this.addressToHex(params.to).slice(2).padStart(64, "0");
|
|
1056
|
+
const amountHex = BigInt(params.amount).toString(16).padStart(64, "0");
|
|
1057
|
+
const parameter = toAddressHex + amountHex;
|
|
1058
|
+
try {
|
|
1059
|
+
const response = await fetch(`${this._rpcUrl}/wallet/triggerconstantcontract`, {
|
|
1060
|
+
method: "POST",
|
|
1061
|
+
headers: { "Content-Type": "application/json" },
|
|
1062
|
+
body: JSON.stringify({
|
|
1063
|
+
owner_address: this.addressToHex(this._address),
|
|
1064
|
+
contract_address: this.addressToHex(params.contractAddress),
|
|
1065
|
+
function_selector: functionSelector,
|
|
1066
|
+
parameter
|
|
1067
|
+
})
|
|
1068
|
+
});
|
|
1069
|
+
if (!response.ok) {
|
|
1070
|
+
throw new Error(`Energy estimation failed: ${response.status}`);
|
|
1071
|
+
}
|
|
1072
|
+
const result = await response.json();
|
|
1073
|
+
if (result.result?.code && result.result.code !== "SUCCESS") {
|
|
1074
|
+
throw new Error(`Energy estimation failed: ${result.result.message ?? result.result.code}`);
|
|
1075
|
+
}
|
|
1076
|
+
const energyRequired = (result.energy_used ?? 0) + (result.energy_penalty ?? 0);
|
|
1077
|
+
const resourceResponse = await fetch(`${this._rpcUrl}/wallet/getaccountresource`, {
|
|
1078
|
+
method: "POST",
|
|
1079
|
+
headers: { "Content-Type": "application/json" },
|
|
1080
|
+
body: JSON.stringify({
|
|
1081
|
+
address: this.addressToHex(this._address)
|
|
1082
|
+
})
|
|
1083
|
+
});
|
|
1084
|
+
let energyAvailable = 0;
|
|
1085
|
+
if (resourceResponse.ok) {
|
|
1086
|
+
const resources = await resourceResponse.json();
|
|
1087
|
+
energyAvailable = (resources.EnergyLimit ?? 0) - (resources.EnergyUsed ?? 0);
|
|
1088
|
+
}
|
|
1089
|
+
const bandwidthRequired = 350;
|
|
1090
|
+
const trxCostIfNoEnergy = BigInt(Math.max(0, energyRequired - energyAvailable)) * 420n;
|
|
1091
|
+
return {
|
|
1092
|
+
energyRequired,
|
|
1093
|
+
energyAvailable,
|
|
1094
|
+
trxCostIfNoEnergy,
|
|
1095
|
+
bandwidthRequired
|
|
1096
|
+
};
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
throw new Error(
|
|
1099
|
+
`Failed to estimate energy: ${error instanceof Error ? error.message : String(error)}`
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Sign a TRC20 transfer with dynamic fee limit estimation.
|
|
1105
|
+
*
|
|
1106
|
+
* Instead of using a hardcoded 100 TRX fee limit, estimates the actual
|
|
1107
|
+
* energy cost and adds a 20% margin.
|
|
1108
|
+
*
|
|
1109
|
+
* @param params - Transaction parameters
|
|
1110
|
+
* @returns Hex-encoded signed transaction
|
|
1111
|
+
*/
|
|
1112
|
+
async signTransactionWithEstimation(params) {
|
|
1113
|
+
if (!params.feeLimit) {
|
|
1114
|
+
const estimate = await this.estimateEnergy(params);
|
|
1115
|
+
const estimatedFee = estimate.trxCostIfNoEnergy;
|
|
1116
|
+
const feeWithMargin = estimatedFee + estimatedFee * 20n / 100n;
|
|
1117
|
+
const feeLimitSun = Number(
|
|
1118
|
+
feeWithMargin < 10000000n ? 10000000n : feeWithMargin > 150000000n ? 150000000n : feeWithMargin
|
|
1119
|
+
);
|
|
1120
|
+
return this.signTransaction({ ...params, feeLimit: feeLimitSun });
|
|
1121
|
+
}
|
|
1122
|
+
return this.signTransaction(params);
|
|
1123
|
+
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Register an external energy delegation provider.
|
|
1126
|
+
*
|
|
1127
|
+
* Energy providers can delegate bandwidth and energy to this account
|
|
1128
|
+
* to reduce TRX costs for TRC20 transfers.
|
|
1129
|
+
*
|
|
1130
|
+
* @param provider - Energy delegation provider
|
|
1131
|
+
*/
|
|
1132
|
+
registerEnergyProvider(provider) {
|
|
1133
|
+
this._energyProvider = provider;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Get the registered energy provider, if any
|
|
1137
|
+
*/
|
|
1138
|
+
getEnergyProvider() {
|
|
1139
|
+
return this._energyProvider;
|
|
1140
|
+
}
|
|
418
1141
|
/**
|
|
419
1142
|
* Build a TRC20 transfer transaction
|
|
420
1143
|
*/
|
|
@@ -499,82 +1222,228 @@ async function createWDKTronSigner(account, rpcUrl) {
|
|
|
499
1222
|
return adapter;
|
|
500
1223
|
}
|
|
501
1224
|
|
|
502
|
-
// src/
|
|
503
|
-
var
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
_cache = /* @__PURE__ */ new Map();
|
|
511
|
-
_config;
|
|
512
|
-
_cleanupInterval = null;
|
|
513
|
-
constructor(config = {}) {
|
|
514
|
-
this._config = { ...DEFAULT_CACHE_CONFIG, ...config };
|
|
515
|
-
if (this._config.maxSize > 0) {
|
|
516
|
-
this._startCleanup();
|
|
1225
|
+
// src/adapters/spark-adapter.ts
|
|
1226
|
+
var WDKSparkSignerAdapter = class {
|
|
1227
|
+
_account;
|
|
1228
|
+
_address = null;
|
|
1229
|
+
_initialized = false;
|
|
1230
|
+
constructor(account) {
|
|
1231
|
+
if (!account) {
|
|
1232
|
+
throw new Error("Spark wallet account is required");
|
|
517
1233
|
}
|
|
1234
|
+
this._account = account;
|
|
518
1235
|
}
|
|
519
1236
|
/**
|
|
520
|
-
* Get
|
|
521
|
-
*
|
|
522
|
-
* @param key - Cache key
|
|
523
|
-
* @returns The cached value or undefined if not found/expired
|
|
1237
|
+
* Get the wallet address
|
|
1238
|
+
* @throws Error if not initialized
|
|
524
1239
|
*/
|
|
525
|
-
get(
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (Date.now() > entry.expiresAt) {
|
|
531
|
-
this._cache.delete(key);
|
|
532
|
-
return void 0;
|
|
533
|
-
}
|
|
534
|
-
if (this._config.refreshOnAccess) {
|
|
535
|
-
entry.expiresAt = Date.now() + this._config.defaultTTL;
|
|
1240
|
+
get address() {
|
|
1241
|
+
if (!this._address) {
|
|
1242
|
+
throw new Error(
|
|
1243
|
+
"Spark signer not initialized. Call initialize() first or use createWDKSparkSigner()."
|
|
1244
|
+
);
|
|
536
1245
|
}
|
|
537
|
-
return
|
|
1246
|
+
return this._address;
|
|
538
1247
|
}
|
|
539
1248
|
/**
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
* @param key - Cache key
|
|
543
|
-
* @param value - Value to cache
|
|
544
|
-
* @param ttl - TTL in milliseconds (optional, uses default if not provided)
|
|
1249
|
+
* Check if the adapter is initialized
|
|
545
1250
|
*/
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
this._evictOldest();
|
|
549
|
-
}
|
|
550
|
-
const expiresAt = Date.now() + (ttl ?? this._config.defaultTTL);
|
|
551
|
-
this._cache.set(key, { value, expiresAt });
|
|
1251
|
+
get isInitialized() {
|
|
1252
|
+
return this._initialized;
|
|
552
1253
|
}
|
|
553
1254
|
/**
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
* @param key - Cache key
|
|
557
|
-
* @returns true if key exists and is not expired
|
|
1255
|
+
* Initialize the adapter by fetching the address
|
|
1256
|
+
* Must be called before using the signer
|
|
558
1257
|
*/
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
return false;
|
|
563
|
-
}
|
|
564
|
-
if (Date.now() > entry.expiresAt) {
|
|
565
|
-
this._cache.delete(key);
|
|
566
|
-
return false;
|
|
1258
|
+
async initialize() {
|
|
1259
|
+
if (this._initialized) {
|
|
1260
|
+
return;
|
|
567
1261
|
}
|
|
568
|
-
|
|
1262
|
+
this._address = await this._account.getAddress();
|
|
1263
|
+
this._initialized = true;
|
|
569
1264
|
}
|
|
570
1265
|
/**
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
* @
|
|
574
|
-
* @returns true if key was deleted
|
|
1266
|
+
* Sign a message using the Spark wallet
|
|
1267
|
+
* @param message - Message to sign (string or bytes)
|
|
1268
|
+
* @returns Signature string
|
|
575
1269
|
*/
|
|
576
|
-
|
|
577
|
-
return this.
|
|
1270
|
+
async signMessage(message) {
|
|
1271
|
+
return this._account.signMessage(message);
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Get the wallet balance in satoshis
|
|
1275
|
+
*/
|
|
1276
|
+
async getBalance() {
|
|
1277
|
+
return this._account.getBalance();
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Send a transaction via the Spark network
|
|
1281
|
+
* @param params - Transaction parameters
|
|
1282
|
+
* @returns Transaction result with hash
|
|
1283
|
+
*/
|
|
1284
|
+
async sendTransaction(params) {
|
|
1285
|
+
return this._account.sendTransaction(params);
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
async function createWDKSparkSigner(account) {
|
|
1289
|
+
const adapter = new WDKSparkSignerAdapter(account);
|
|
1290
|
+
await adapter.initialize();
|
|
1291
|
+
return adapter;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// src/adapters/btc-adapter.ts
|
|
1295
|
+
var WDKBtcSignerAdapter = class {
|
|
1296
|
+
_account;
|
|
1297
|
+
_address = null;
|
|
1298
|
+
_initialized = false;
|
|
1299
|
+
constructor(account) {
|
|
1300
|
+
if (!account) {
|
|
1301
|
+
throw new Error("WDK Bitcoin account is required");
|
|
1302
|
+
}
|
|
1303
|
+
this._account = account;
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Get the wallet address
|
|
1307
|
+
* @throws Error if not initialized
|
|
1308
|
+
*/
|
|
1309
|
+
get address() {
|
|
1310
|
+
if (!this._address) {
|
|
1311
|
+
throw new Error(
|
|
1312
|
+
"Bitcoin signer not initialized. Call initialize() first or use createWDKBtcSigner()."
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
return this._address;
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Check if the adapter is initialized
|
|
1319
|
+
*/
|
|
1320
|
+
get isInitialized() {
|
|
1321
|
+
return this._initialized;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Initialize the adapter by fetching the address
|
|
1325
|
+
* Must be called before using the signer
|
|
1326
|
+
*/
|
|
1327
|
+
async initialize() {
|
|
1328
|
+
if (this._initialized) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
this._address = await this._account.getAddress();
|
|
1332
|
+
this._initialized = true;
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Sign a message using the Bitcoin wallet
|
|
1336
|
+
* @param message - Message string to sign
|
|
1337
|
+
* @returns Signature string
|
|
1338
|
+
*/
|
|
1339
|
+
async signMessage(message) {
|
|
1340
|
+
return this._account.signMessage(message);
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Sign a Partially Signed Bitcoin Transaction (PSBT)
|
|
1344
|
+
* @param psbt - PSBT bytes to sign
|
|
1345
|
+
* @returns Signed PSBT bytes
|
|
1346
|
+
*/
|
|
1347
|
+
async signPsbt(psbt) {
|
|
1348
|
+
return this._account.signPsbt(psbt);
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Get the wallet balance in satoshis
|
|
1352
|
+
*/
|
|
1353
|
+
async getBalance() {
|
|
1354
|
+
return this._account.getBalance();
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Send a Bitcoin transaction
|
|
1358
|
+
* @param params - Transaction parameters
|
|
1359
|
+
* @returns Transaction hash
|
|
1360
|
+
*/
|
|
1361
|
+
async sendTransaction(params) {
|
|
1362
|
+
return this._account.sendTransaction(params);
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
async function createWDKBtcSigner(account) {
|
|
1366
|
+
const adapter = new WDKBtcSignerAdapter(account);
|
|
1367
|
+
await adapter.initialize();
|
|
1368
|
+
return adapter;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// src/cache.ts
|
|
1372
|
+
var DEFAULT_CACHE_CONFIG = {
|
|
1373
|
+
defaultTTL: 3e4,
|
|
1374
|
+
// 30 seconds
|
|
1375
|
+
maxSize: 1e3,
|
|
1376
|
+
refreshOnAccess: false
|
|
1377
|
+
};
|
|
1378
|
+
var TTLCache = class {
|
|
1379
|
+
_cache = /* @__PURE__ */ new Map();
|
|
1380
|
+
_config;
|
|
1381
|
+
_cleanupInterval = null;
|
|
1382
|
+
constructor(config = {}) {
|
|
1383
|
+
this._config = { ...DEFAULT_CACHE_CONFIG, ...config };
|
|
1384
|
+
if (this._config.maxSize > 0) {
|
|
1385
|
+
this._startCleanup();
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Get a value from the cache
|
|
1390
|
+
*
|
|
1391
|
+
* @param key - Cache key
|
|
1392
|
+
* @returns The cached value or undefined if not found/expired
|
|
1393
|
+
*/
|
|
1394
|
+
get(key) {
|
|
1395
|
+
const entry = this._cache.get(key);
|
|
1396
|
+
if (!entry) {
|
|
1397
|
+
return void 0;
|
|
1398
|
+
}
|
|
1399
|
+
if (Date.now() > entry.expiresAt) {
|
|
1400
|
+
this._cache.delete(key);
|
|
1401
|
+
return void 0;
|
|
1402
|
+
}
|
|
1403
|
+
if (this._config.refreshOnAccess) {
|
|
1404
|
+
entry.expiresAt = Date.now() + this._config.defaultTTL;
|
|
1405
|
+
}
|
|
1406
|
+
return entry.value;
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Set a value in the cache
|
|
1410
|
+
*
|
|
1411
|
+
* @param key - Cache key
|
|
1412
|
+
* @param value - Value to cache
|
|
1413
|
+
* @param ttl - TTL in milliseconds (optional, uses default if not provided)
|
|
1414
|
+
*/
|
|
1415
|
+
set(key, value, ttl) {
|
|
1416
|
+
if (this._cache.size >= this._config.maxSize) {
|
|
1417
|
+
this._evictOldest();
|
|
1418
|
+
}
|
|
1419
|
+
const expiresAt = Date.now() + (ttl ?? this._config.defaultTTL);
|
|
1420
|
+
this._cache.set(key, { value, expiresAt });
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Check if a key exists and is not expired
|
|
1424
|
+
*
|
|
1425
|
+
* @param key - Cache key
|
|
1426
|
+
* @returns true if key exists and is not expired
|
|
1427
|
+
*/
|
|
1428
|
+
has(key) {
|
|
1429
|
+
const entry = this._cache.get(key);
|
|
1430
|
+
if (!entry) {
|
|
1431
|
+
return false;
|
|
1432
|
+
}
|
|
1433
|
+
if (Date.now() > entry.expiresAt) {
|
|
1434
|
+
this._cache.delete(key);
|
|
1435
|
+
return false;
|
|
1436
|
+
}
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Delete a key from the cache
|
|
1441
|
+
*
|
|
1442
|
+
* @param key - Cache key
|
|
1443
|
+
* @returns true if key was deleted
|
|
1444
|
+
*/
|
|
1445
|
+
delete(key) {
|
|
1446
|
+
return this._cache.delete(key);
|
|
578
1447
|
}
|
|
579
1448
|
/**
|
|
580
1449
|
* Delete all keys matching a prefix
|
|
@@ -1117,6 +1986,145 @@ var CHAIN_TOKENS = {
|
|
|
1117
1986
|
}
|
|
1118
1987
|
]
|
|
1119
1988
|
};
|
|
1989
|
+
var CHAIN_REGISTRY = {
|
|
1990
|
+
// --- EVM chains (derived from existing constants) ---
|
|
1991
|
+
ethereum: {
|
|
1992
|
+
family: "evm",
|
|
1993
|
+
chainId: 1,
|
|
1994
|
+
caip2: "eip155:1",
|
|
1995
|
+
rpcEndpoints: ["https://eth.drpc.org"],
|
|
1996
|
+
tokens: (CHAIN_TOKENS.ethereum ?? []).map((t) => ({
|
|
1997
|
+
address: t.address,
|
|
1998
|
+
symbol: t.symbol,
|
|
1999
|
+
decimals: t.decimals
|
|
2000
|
+
}))
|
|
2001
|
+
},
|
|
2002
|
+
arbitrum: {
|
|
2003
|
+
family: "evm",
|
|
2004
|
+
chainId: 42161,
|
|
2005
|
+
caip2: "eip155:42161",
|
|
2006
|
+
rpcEndpoints: ["https://arb1.arbitrum.io/rpc"],
|
|
2007
|
+
tokens: (CHAIN_TOKENS.arbitrum ?? []).map((t) => ({
|
|
2008
|
+
address: t.address,
|
|
2009
|
+
symbol: t.symbol,
|
|
2010
|
+
decimals: t.decimals
|
|
2011
|
+
}))
|
|
2012
|
+
},
|
|
2013
|
+
base: {
|
|
2014
|
+
family: "evm",
|
|
2015
|
+
chainId: 8453,
|
|
2016
|
+
caip2: "eip155:8453",
|
|
2017
|
+
rpcEndpoints: ["https://mainnet.base.org"],
|
|
2018
|
+
tokens: (CHAIN_TOKENS.base ?? []).map((t) => ({
|
|
2019
|
+
address: t.address,
|
|
2020
|
+
symbol: t.symbol,
|
|
2021
|
+
decimals: t.decimals
|
|
2022
|
+
}))
|
|
2023
|
+
},
|
|
2024
|
+
ink: {
|
|
2025
|
+
family: "evm",
|
|
2026
|
+
chainId: 57073,
|
|
2027
|
+
caip2: "eip155:57073",
|
|
2028
|
+
rpcEndpoints: ["https://rpc-gel.inkonchain.com"],
|
|
2029
|
+
tokens: (CHAIN_TOKENS.ink ?? []).map((t) => ({
|
|
2030
|
+
address: t.address,
|
|
2031
|
+
symbol: t.symbol,
|
|
2032
|
+
decimals: t.decimals
|
|
2033
|
+
}))
|
|
2034
|
+
},
|
|
2035
|
+
berachain: {
|
|
2036
|
+
family: "evm",
|
|
2037
|
+
chainId: 80094,
|
|
2038
|
+
caip2: "eip155:80094",
|
|
2039
|
+
rpcEndpoints: [],
|
|
2040
|
+
tokens: (CHAIN_TOKENS.berachain ?? []).map((t) => ({
|
|
2041
|
+
address: t.address,
|
|
2042
|
+
symbol: t.symbol,
|
|
2043
|
+
decimals: t.decimals
|
|
2044
|
+
}))
|
|
2045
|
+
},
|
|
2046
|
+
unichain: {
|
|
2047
|
+
family: "evm",
|
|
2048
|
+
chainId: 130,
|
|
2049
|
+
caip2: "eip155:130",
|
|
2050
|
+
rpcEndpoints: [],
|
|
2051
|
+
tokens: (CHAIN_TOKENS.unichain ?? []).map((t) => ({
|
|
2052
|
+
address: t.address,
|
|
2053
|
+
symbol: t.symbol,
|
|
2054
|
+
decimals: t.decimals
|
|
2055
|
+
}))
|
|
2056
|
+
},
|
|
2057
|
+
optimism: {
|
|
2058
|
+
family: "evm",
|
|
2059
|
+
chainId: 10,
|
|
2060
|
+
caip2: "eip155:10",
|
|
2061
|
+
rpcEndpoints: ["https://mainnet.optimism.io"],
|
|
2062
|
+
tokens: (CHAIN_TOKENS.optimism ?? []).map((t) => ({
|
|
2063
|
+
address: t.address,
|
|
2064
|
+
symbol: t.symbol,
|
|
2065
|
+
decimals: t.decimals
|
|
2066
|
+
}))
|
|
2067
|
+
},
|
|
2068
|
+
polygon: {
|
|
2069
|
+
family: "evm",
|
|
2070
|
+
chainId: 137,
|
|
2071
|
+
caip2: "eip155:137",
|
|
2072
|
+
rpcEndpoints: ["https://polygon-rpc.com"],
|
|
2073
|
+
tokens: (CHAIN_TOKENS.polygon ?? []).map((t) => ({
|
|
2074
|
+
address: t.address,
|
|
2075
|
+
symbol: t.symbol,
|
|
2076
|
+
decimals: t.decimals
|
|
2077
|
+
}))
|
|
2078
|
+
},
|
|
2079
|
+
// --- Non-EVM chains ---
|
|
2080
|
+
ton: {
|
|
2081
|
+
family: "ton",
|
|
2082
|
+
caip2: "ton:mainnet",
|
|
2083
|
+
rpcEndpoints: ["https://toncenter.com/api/v2/jsonRPC"],
|
|
2084
|
+
tokens: [
|
|
2085
|
+
{
|
|
2086
|
+
address: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs",
|
|
2087
|
+
symbol: "USDT",
|
|
2088
|
+
decimals: 6
|
|
2089
|
+
}
|
|
2090
|
+
]
|
|
2091
|
+
},
|
|
2092
|
+
tron: {
|
|
2093
|
+
family: "tron",
|
|
2094
|
+
caip2: "tron:mainnet",
|
|
2095
|
+
rpcEndpoints: ["https://api.trongrid.io"],
|
|
2096
|
+
tokens: [
|
|
2097
|
+
{
|
|
2098
|
+
address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
|
|
2099
|
+
symbol: "USDT",
|
|
2100
|
+
decimals: 6
|
|
2101
|
+
}
|
|
2102
|
+
]
|
|
2103
|
+
},
|
|
2104
|
+
solana: {
|
|
2105
|
+
family: "svm",
|
|
2106
|
+
caip2: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
2107
|
+
rpcEndpoints: ["https://api.mainnet-beta.solana.com"],
|
|
2108
|
+
tokens: [
|
|
2109
|
+
{
|
|
2110
|
+
address: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
|
|
2111
|
+
symbol: "USDT",
|
|
2112
|
+
decimals: 6
|
|
2113
|
+
}
|
|
2114
|
+
]
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
function getRegistryByCaip2(caip2) {
|
|
2118
|
+
for (const entry of Object.values(CHAIN_REGISTRY)) {
|
|
2119
|
+
if (entry.caip2 === caip2) {
|
|
2120
|
+
return entry;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
return void 0;
|
|
2124
|
+
}
|
|
2125
|
+
function getChainsByFamily(family) {
|
|
2126
|
+
return Object.entries(CHAIN_REGISTRY).filter(([, entry]) => entry.family === family).map(([name]) => name);
|
|
2127
|
+
}
|
|
1120
2128
|
function normalizeChainConfig(chainName, config) {
|
|
1121
2129
|
const defaultConfig = DEFAULT_CHAINS[chainName];
|
|
1122
2130
|
if (typeof config === "string") {
|
|
@@ -1127,8 +2135,9 @@ function normalizeChainConfig(chainName, config) {
|
|
|
1127
2135
|
name: chainName
|
|
1128
2136
|
};
|
|
1129
2137
|
}
|
|
2138
|
+
const resolvedProvider = Array.isArray(config.provider) ? config.provider[0] : config.provider;
|
|
1130
2139
|
return {
|
|
1131
|
-
provider:
|
|
2140
|
+
provider: resolvedProvider,
|
|
1132
2141
|
chainId: config.chainId ?? defaultConfig?.chainId ?? 1,
|
|
1133
2142
|
network: config.network ?? defaultConfig?.network ?? `eip155:${config.chainId}`,
|
|
1134
2143
|
name: chainName
|
|
@@ -1143,6 +2152,11 @@ function getChainFromNetwork(network) {
|
|
|
1143
2152
|
return chain;
|
|
1144
2153
|
}
|
|
1145
2154
|
}
|
|
2155
|
+
for (const [chain, entry] of Object.entries(CHAIN_REGISTRY)) {
|
|
2156
|
+
if (entry.caip2 === network) {
|
|
2157
|
+
return chain;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
1146
2160
|
return void 0;
|
|
1147
2161
|
}
|
|
1148
2162
|
function getChainId(chain) {
|
|
@@ -1805,6 +2819,65 @@ var WDKSigner = class {
|
|
|
1805
2819
|
);
|
|
1806
2820
|
}
|
|
1807
2821
|
}
|
|
2822
|
+
/**
|
|
2823
|
+
* Sign an EIP-2612 permit for gasless token approvals
|
|
2824
|
+
*
|
|
2825
|
+
* @param params - Permit parameters
|
|
2826
|
+
* @returns The permit signature components (v, r, s)
|
|
2827
|
+
* @throws {SigningError} If signing fails
|
|
2828
|
+
*/
|
|
2829
|
+
async signPermit(params) {
|
|
2830
|
+
if (!params.token || !params.token.startsWith("0x")) {
|
|
2831
|
+
throw new SigningError(
|
|
2832
|
+
4003 /* INVALID_TYPED_DATA */,
|
|
2833
|
+
`Invalid token address: ${params.token}`,
|
|
2834
|
+
{ operation: "signTypedData", context: { chain: this._chain } }
|
|
2835
|
+
);
|
|
2836
|
+
}
|
|
2837
|
+
if (!params.spender || !params.spender.startsWith("0x")) {
|
|
2838
|
+
throw new SigningError(
|
|
2839
|
+
4003 /* INVALID_TYPED_DATA */,
|
|
2840
|
+
`Invalid spender address: ${params.spender}`,
|
|
2841
|
+
{ operation: "signTypedData", context: { chain: this._chain } }
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2844
|
+
const chainId = this.getChainId();
|
|
2845
|
+
const nonce = params.nonce ?? 0n;
|
|
2846
|
+
const typedData = {
|
|
2847
|
+
domain: {
|
|
2848
|
+
name: params.tokenName ?? "Tether USD",
|
|
2849
|
+
version: params.tokenVersion ?? "1",
|
|
2850
|
+
chainId: BigInt(chainId),
|
|
2851
|
+
verifyingContract: params.token
|
|
2852
|
+
},
|
|
2853
|
+
types: {
|
|
2854
|
+
Permit: [
|
|
2855
|
+
{ name: "owner", type: "address" },
|
|
2856
|
+
{ name: "spender", type: "address" },
|
|
2857
|
+
{ name: "value", type: "uint256" },
|
|
2858
|
+
{ name: "nonce", type: "uint256" },
|
|
2859
|
+
{ name: "deadline", type: "uint256" }
|
|
2860
|
+
]
|
|
2861
|
+
},
|
|
2862
|
+
primaryType: "Permit",
|
|
2863
|
+
message: {
|
|
2864
|
+
owner: this.address,
|
|
2865
|
+
spender: params.spender,
|
|
2866
|
+
value: params.value.toString(),
|
|
2867
|
+
nonce: nonce.toString(),
|
|
2868
|
+
deadline: params.deadline.toString()
|
|
2869
|
+
}
|
|
2870
|
+
};
|
|
2871
|
+
const signature = await this.signTypedData(typedData);
|
|
2872
|
+
const sigHex = signature.slice(2);
|
|
2873
|
+
const r = `0x${sigHex.slice(0, 64)}`;
|
|
2874
|
+
const s = `0x${sigHex.slice(64, 128)}`;
|
|
2875
|
+
let v = parseInt(sigHex.slice(128, 130), 16);
|
|
2876
|
+
if (v < 27) {
|
|
2877
|
+
v += 27;
|
|
2878
|
+
}
|
|
2879
|
+
return { v, r, s };
|
|
2880
|
+
}
|
|
1808
2881
|
/**
|
|
1809
2882
|
* Send a transaction (for advanced use cases)
|
|
1810
2883
|
*
|
|
@@ -1875,34 +2948,429 @@ var MockWDKSigner = class {
|
|
|
1875
2948
|
|
|
1876
2949
|
// src/t402wdk.ts
|
|
1877
2950
|
var import_evm = require("@t402/evm");
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2951
|
+
|
|
2952
|
+
// src/pricing.ts
|
|
2953
|
+
var _registeredPricingProvider = null;
|
|
2954
|
+
function registerPricingProvider(provider) {
|
|
2955
|
+
_registeredPricingProvider = provider;
|
|
2956
|
+
}
|
|
2957
|
+
function getPricingProvider() {
|
|
2958
|
+
return _registeredPricingProvider;
|
|
2959
|
+
}
|
|
2960
|
+
function isPricingProviderRegistered() {
|
|
2961
|
+
return _registeredPricingProvider !== null;
|
|
2962
|
+
}
|
|
2963
|
+
var STABLECOIN_SYMBOLS = /* @__PURE__ */ new Set(["USDT", "USDT0", "USDC", "DAI", "BUSD"]);
|
|
2964
|
+
var DEFAULT_TTL = 6e4;
|
|
2965
|
+
var DEFAULT_FIAT = ["USD", "EUR", "GBP", "JPY"];
|
|
2966
|
+
function createWdkMoneyParser(config) {
|
|
2967
|
+
const cacheTTL = config?.cacheTTL ?? DEFAULT_TTL;
|
|
2968
|
+
const supportedFiat = new Set(config?.supportedFiat ?? DEFAULT_FIAT);
|
|
2969
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2970
|
+
return async (amount, network) => {
|
|
2971
|
+
if (!Number.isFinite(amount) || amount <= 0) return null;
|
|
2972
|
+
const chain = getChainFromNetwork(network);
|
|
2973
|
+
if (!chain) return null;
|
|
2974
|
+
const tokenInfo = getPreferredToken(chain);
|
|
2975
|
+
if (!tokenInfo) return null;
|
|
2976
|
+
if (STABLECOIN_SYMBOLS.has(tokenInfo.symbol.toUpperCase())) {
|
|
2977
|
+
return {
|
|
2978
|
+
amount: toAtomicUnits(amount, tokenInfo.decimals),
|
|
2979
|
+
asset: tokenInfo.address
|
|
2980
|
+
};
|
|
2981
|
+
}
|
|
2982
|
+
const currency = "USD";
|
|
2983
|
+
if (!supportedFiat.has(currency)) return null;
|
|
2984
|
+
const cacheKey = `${currency}_${tokenInfo.symbol}`;
|
|
2985
|
+
const cached = cache.get(cacheKey);
|
|
2986
|
+
const now = Date.now();
|
|
2987
|
+
let rate;
|
|
2988
|
+
if (cached && now - cached.fetchedAt < cacheTTL) {
|
|
2989
|
+
rate = cached.rate;
|
|
2990
|
+
} else {
|
|
2991
|
+
rate = await fetchRate(currency, tokenInfo.symbol);
|
|
2992
|
+
cache.set(cacheKey, { rate, fetchedAt: now });
|
|
2993
|
+
}
|
|
2994
|
+
const convertedAmount = amount * rate;
|
|
2995
|
+
return {
|
|
2996
|
+
amount: toAtomicUnits(convertedAmount, tokenInfo.decimals),
|
|
2997
|
+
asset: tokenInfo.address
|
|
2998
|
+
};
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
function toAtomicUnits(amount, decimals) {
|
|
3002
|
+
const factor = 10 ** decimals;
|
|
3003
|
+
const atomic = Math.round(amount * factor);
|
|
3004
|
+
return atomic.toString();
|
|
3005
|
+
}
|
|
3006
|
+
function resolveAssetForNetwork(token, network) {
|
|
3007
|
+
const chain = getChainFromNetwork(network);
|
|
3008
|
+
if (!chain) return null;
|
|
3009
|
+
const upper = token.toUpperCase();
|
|
3010
|
+
if (upper === "USDT0" || upper === "USDT") {
|
|
3011
|
+
return USDT0_ADDRESSES[chain] ?? null;
|
|
3012
|
+
}
|
|
3013
|
+
if (upper === "USDC") {
|
|
3014
|
+
return USDC_ADDRESSES[chain] ?? null;
|
|
3015
|
+
}
|
|
3016
|
+
return null;
|
|
3017
|
+
}
|
|
3018
|
+
async function fetchRate(fromCurrency, toToken) {
|
|
3019
|
+
if (_registeredPricingProvider) {
|
|
3020
|
+
try {
|
|
3021
|
+
return await _registeredPricingProvider.getRate(fromCurrency, toToken);
|
|
3022
|
+
} catch {
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
if (fromCurrency.toUpperCase() === "USD") {
|
|
3026
|
+
return 1;
|
|
3027
|
+
}
|
|
3028
|
+
const placeholderRates = {
|
|
3029
|
+
EUR: 1.08,
|
|
3030
|
+
GBP: 1.27,
|
|
3031
|
+
JPY: 67e-4
|
|
3032
|
+
};
|
|
3033
|
+
return placeholderRates[fromCurrency.toUpperCase()] ?? 1;
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
// src/failover.ts
|
|
3037
|
+
var DEFAULT_HEALTH_CHECK_INTERVAL = 3e4;
|
|
3038
|
+
var DEFAULT_REQUEST_TIMEOUT = 5e3;
|
|
3039
|
+
var DEFAULT_MAX_FAILURES = 3;
|
|
3040
|
+
var FailoverProvider = class {
|
|
3041
|
+
providers;
|
|
3042
|
+
currentIndex = 0;
|
|
3043
|
+
healthCheckTimer;
|
|
3044
|
+
maxFailures;
|
|
3045
|
+
requestTimeout;
|
|
3046
|
+
constructor(config) {
|
|
3047
|
+
if (!config.urls || config.urls.length === 0) {
|
|
3048
|
+
throw new Error("FailoverProvider requires at least one URL");
|
|
3049
|
+
}
|
|
3050
|
+
this.maxFailures = config.maxFailures ?? DEFAULT_MAX_FAILURES;
|
|
3051
|
+
this.requestTimeout = config.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT;
|
|
3052
|
+
this.providers = config.urls.map((url) => ({
|
|
3053
|
+
url,
|
|
3054
|
+
healthy: true,
|
|
3055
|
+
lastChecked: Date.now(),
|
|
3056
|
+
consecutiveFailures: 0
|
|
3057
|
+
}));
|
|
3058
|
+
const interval = config.healthCheckInterval ?? DEFAULT_HEALTH_CHECK_INTERVAL;
|
|
3059
|
+
if (interval > 0) {
|
|
3060
|
+
this.startHealthChecks(interval);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* Get current active RPC URL
|
|
3065
|
+
*/
|
|
3066
|
+
getCurrentUrl() {
|
|
3067
|
+
return this.providers[this.currentIndex].url;
|
|
3068
|
+
}
|
|
3069
|
+
/**
|
|
3070
|
+
* Report a failure on the current URL.
|
|
3071
|
+
* Auto-switches to next healthy provider if failure threshold is reached.
|
|
1903
3072
|
*
|
|
1904
|
-
*
|
|
1905
|
-
|
|
3073
|
+
* @returns The new URL if switched, or null if no switch occurred
|
|
3074
|
+
*/
|
|
3075
|
+
reportFailure() {
|
|
3076
|
+
const provider = this.providers[this.currentIndex];
|
|
3077
|
+
provider.consecutiveFailures++;
|
|
3078
|
+
provider.lastChecked = Date.now();
|
|
3079
|
+
if (provider.consecutiveFailures >= this.maxFailures) {
|
|
3080
|
+
provider.healthy = false;
|
|
3081
|
+
return this.switchToNext();
|
|
3082
|
+
}
|
|
3083
|
+
return null;
|
|
3084
|
+
}
|
|
3085
|
+
/**
|
|
3086
|
+
* Report success on the current URL, reset failure counter.
|
|
3087
|
+
*/
|
|
3088
|
+
reportSuccess() {
|
|
3089
|
+
const provider = this.providers[this.currentIndex];
|
|
3090
|
+
provider.consecutiveFailures = 0;
|
|
3091
|
+
provider.healthy = true;
|
|
3092
|
+
provider.lastChecked = Date.now();
|
|
3093
|
+
}
|
|
3094
|
+
/**
|
|
3095
|
+
* Get all provider statuses
|
|
3096
|
+
*/
|
|
3097
|
+
getStatus() {
|
|
3098
|
+
return this.providers.map((p) => ({ ...p }));
|
|
3099
|
+
}
|
|
3100
|
+
/**
|
|
3101
|
+
* Force switch to next healthy provider.
|
|
3102
|
+
*
|
|
3103
|
+
* @returns The new URL, or null if no healthy providers available
|
|
3104
|
+
*/
|
|
3105
|
+
switchToNext() {
|
|
3106
|
+
const startIndex = this.currentIndex;
|
|
3107
|
+
for (let i = 1; i <= this.providers.length; i++) {
|
|
3108
|
+
const nextIndex = (startIndex + i) % this.providers.length;
|
|
3109
|
+
if (this.providers[nextIndex].healthy) {
|
|
3110
|
+
this.currentIndex = nextIndex;
|
|
3111
|
+
return this.providers[nextIndex].url;
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
return null;
|
|
3115
|
+
}
|
|
3116
|
+
/**
|
|
3117
|
+
* Get the request timeout in milliseconds
|
|
3118
|
+
*/
|
|
3119
|
+
getRequestTimeout() {
|
|
3120
|
+
return this.requestTimeout;
|
|
3121
|
+
}
|
|
3122
|
+
/**
|
|
3123
|
+
* Stop health checks and clean up resources
|
|
3124
|
+
*/
|
|
3125
|
+
dispose() {
|
|
3126
|
+
if (this.healthCheckTimer) {
|
|
3127
|
+
clearInterval(this.healthCheckTimer);
|
|
3128
|
+
this.healthCheckTimer = void 0;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
startHealthChecks(interval) {
|
|
3132
|
+
this.healthCheckTimer = setInterval(() => {
|
|
3133
|
+
for (const provider of this.providers) {
|
|
3134
|
+
if (!provider.healthy) {
|
|
3135
|
+
this.checkHealth(provider);
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
}, interval);
|
|
3139
|
+
if (this.healthCheckTimer.unref) {
|
|
3140
|
+
this.healthCheckTimer.unref();
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
async checkHealth(provider) {
|
|
3144
|
+
try {
|
|
3145
|
+
const controller = new AbortController();
|
|
3146
|
+
const timeout = setTimeout(() => controller.abort(), this.requestTimeout);
|
|
3147
|
+
const response = await fetch(provider.url, {
|
|
3148
|
+
method: "POST",
|
|
3149
|
+
headers: { "Content-Type": "application/json" },
|
|
3150
|
+
body: JSON.stringify({
|
|
3151
|
+
jsonrpc: "2.0",
|
|
3152
|
+
method: "eth_chainId",
|
|
3153
|
+
params: [],
|
|
3154
|
+
id: 1
|
|
3155
|
+
}),
|
|
3156
|
+
signal: controller.signal
|
|
3157
|
+
});
|
|
3158
|
+
clearTimeout(timeout);
|
|
3159
|
+
if (response.ok) {
|
|
3160
|
+
provider.healthy = true;
|
|
3161
|
+
provider.consecutiveFailures = 0;
|
|
3162
|
+
provider.lastChecked = Date.now();
|
|
3163
|
+
return true;
|
|
3164
|
+
}
|
|
3165
|
+
} catch {
|
|
3166
|
+
}
|
|
3167
|
+
provider.lastChecked = Date.now();
|
|
3168
|
+
return false;
|
|
3169
|
+
}
|
|
3170
|
+
};
|
|
3171
|
+
function createFailoverProvider(config) {
|
|
3172
|
+
if (typeof config === "string") return null;
|
|
3173
|
+
if (Array.isArray(config)) return new FailoverProvider({ urls: config });
|
|
3174
|
+
return new FailoverProvider(config);
|
|
3175
|
+
}
|
|
3176
|
+
function resolveRpcUrl(config) {
|
|
3177
|
+
if (typeof config === "string") return config;
|
|
3178
|
+
if (Array.isArray(config)) return config[0];
|
|
3179
|
+
return config.urls[0];
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
// src/t402wdk.ts
|
|
3183
|
+
var SUPPORTED_WDK_RANGE = ">=1.0.0-beta.5 <2.0.0";
|
|
3184
|
+
function parseSemver(version) {
|
|
3185
|
+
const match = version.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
|
|
3186
|
+
if (!match) return null;
|
|
3187
|
+
return {
|
|
3188
|
+
major: parseInt(match[1], 10),
|
|
3189
|
+
minor: parseInt(match[2], 10),
|
|
3190
|
+
patch: parseInt(match[3], 10),
|
|
3191
|
+
prerelease: match[4] ?? ""
|
|
3192
|
+
};
|
|
3193
|
+
}
|
|
3194
|
+
function comparePrereleases(a, b) {
|
|
3195
|
+
if (a === b) return 0;
|
|
3196
|
+
if (a === "" && b !== "") return 1;
|
|
3197
|
+
if (a !== "" && b === "") return -1;
|
|
3198
|
+
const aParts = a.split(".");
|
|
3199
|
+
const bParts = b.split(".");
|
|
3200
|
+
const len = Math.max(aParts.length, bParts.length);
|
|
3201
|
+
for (let i = 0; i < len; i++) {
|
|
3202
|
+
const ap = aParts[i] ?? "";
|
|
3203
|
+
const bp = bParts[i] ?? "";
|
|
3204
|
+
const aNum = /^\d+$/.test(ap);
|
|
3205
|
+
const bNum = /^\d+$/.test(bp);
|
|
3206
|
+
if (aNum && bNum) {
|
|
3207
|
+
const diff = parseInt(ap, 10) - parseInt(bp, 10);
|
|
3208
|
+
if (diff !== 0) return diff;
|
|
3209
|
+
} else if (aNum !== bNum) {
|
|
3210
|
+
return aNum ? -1 : 1;
|
|
3211
|
+
} else {
|
|
3212
|
+
if (ap < bp) return -1;
|
|
3213
|
+
if (ap > bp) return 1;
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
return 0;
|
|
3217
|
+
}
|
|
3218
|
+
function compareSemver(a, b) {
|
|
3219
|
+
const pa = parseSemver(a);
|
|
3220
|
+
const pb = parseSemver(b);
|
|
3221
|
+
if (!pa || !pb) return 0;
|
|
3222
|
+
if (pa.major !== pb.major) return pa.major - pb.major > 0 ? 1 : -1;
|
|
3223
|
+
if (pa.minor !== pb.minor) return pa.minor - pb.minor > 0 ? 1 : -1;
|
|
3224
|
+
if (pa.patch !== pb.patch) return pa.patch - pb.patch > 0 ? 1 : -1;
|
|
3225
|
+
return comparePrereleases(pa.prerelease, pb.prerelease);
|
|
3226
|
+
}
|
|
3227
|
+
function satisfiesSemverRange(version, range) {
|
|
3228
|
+
const parsed = parseSemver(version);
|
|
3229
|
+
if (!parsed) return false;
|
|
3230
|
+
const constraints = range.trim().split(/\s+/);
|
|
3231
|
+
for (const constraint of constraints) {
|
|
3232
|
+
const match = constraint.match(/^(>=|<=|>|<|=)(.+)$/);
|
|
3233
|
+
if (!match) continue;
|
|
3234
|
+
const [, op, target] = match;
|
|
3235
|
+
const cmp = compareSemver(version, target);
|
|
3236
|
+
switch (op) {
|
|
3237
|
+
case ">=":
|
|
3238
|
+
if (cmp < 0) return false;
|
|
3239
|
+
break;
|
|
3240
|
+
case ">":
|
|
3241
|
+
if (cmp <= 0) return false;
|
|
3242
|
+
break;
|
|
3243
|
+
case "<=":
|
|
3244
|
+
if (cmp > 0) return false;
|
|
3245
|
+
break;
|
|
3246
|
+
case "<":
|
|
3247
|
+
if (cmp >= 0) return false;
|
|
3248
|
+
break;
|
|
3249
|
+
case "=":
|
|
3250
|
+
if (cmp !== 0) return false;
|
|
3251
|
+
break;
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
return true;
|
|
3255
|
+
}
|
|
3256
|
+
var T402WDK = class _T402WDK {
|
|
3257
|
+
_wdk = null;
|
|
3258
|
+
_normalizedChains = /* @__PURE__ */ new Map();
|
|
3259
|
+
_seedPhrase;
|
|
3260
|
+
_signerCache = /* @__PURE__ */ new Map();
|
|
3261
|
+
_balanceCache;
|
|
3262
|
+
_initializationError = null;
|
|
3263
|
+
_events = new T402EventEmitter();
|
|
3264
|
+
_receiptStore = new InMemoryReceiptStore();
|
|
3265
|
+
_disposed = false;
|
|
3266
|
+
// Instance-level module references (#204 multi-instance)
|
|
3267
|
+
_wdkConstructor = null;
|
|
3268
|
+
_walletManagerEvm = null;
|
|
3269
|
+
_bridgeUsdt0Evm = null;
|
|
3270
|
+
_walletModules = {};
|
|
3271
|
+
_protocolModules = {};
|
|
3272
|
+
_fiatOnRampProvider = null;
|
|
3273
|
+
_middlewares = /* @__PURE__ */ new Map();
|
|
3274
|
+
// Retry config (#202 network resilience)
|
|
3275
|
+
_retryConfig;
|
|
3276
|
+
// Failover providers (#195)
|
|
3277
|
+
_failoverProviders = /* @__PURE__ */ new Map();
|
|
3278
|
+
// Static defaults for backward compatibility (#204)
|
|
3279
|
+
static _defaultModules = {};
|
|
3280
|
+
// Legacy static accessors for tests that access _WDK, _WalletManagerEvm, etc.
|
|
3281
|
+
static get _WDK() {
|
|
3282
|
+
return _T402WDK._defaultModules.wdk ?? null;
|
|
3283
|
+
}
|
|
3284
|
+
static set _WDK(val) {
|
|
3285
|
+
if (val === null) {
|
|
3286
|
+
delete _T402WDK._defaultModules.wdk;
|
|
3287
|
+
} else {
|
|
3288
|
+
_T402WDK._defaultModules.wdk = val;
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
static get _WalletManagerEvm() {
|
|
3292
|
+
return _T402WDK._defaultModules.walletManagerEvm ?? null;
|
|
3293
|
+
}
|
|
3294
|
+
static set _WalletManagerEvm(val) {
|
|
3295
|
+
if (val === null) {
|
|
3296
|
+
delete _T402WDK._defaultModules.walletManagerEvm;
|
|
3297
|
+
if (_T402WDK._defaultModules.wallets) {
|
|
3298
|
+
delete _T402WDK._defaultModules.wallets.evm;
|
|
3299
|
+
}
|
|
3300
|
+
} else {
|
|
3301
|
+
_T402WDK._defaultModules.walletManagerEvm = val;
|
|
3302
|
+
if (!_T402WDK._defaultModules.wallets) {
|
|
3303
|
+
_T402WDK._defaultModules.wallets = {};
|
|
3304
|
+
}
|
|
3305
|
+
_T402WDK._defaultModules.wallets.evm = val;
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
static get _BridgeUsdt0Evm() {
|
|
3309
|
+
return _T402WDK._defaultModules.bridgeUsdt0Evm ?? null;
|
|
3310
|
+
}
|
|
3311
|
+
static set _BridgeUsdt0Evm(val) {
|
|
3312
|
+
if (val === null) {
|
|
3313
|
+
delete _T402WDK._defaultModules.bridgeUsdt0Evm;
|
|
3314
|
+
if (_T402WDK._defaultModules.protocols) {
|
|
3315
|
+
delete _T402WDK._defaultModules.protocols.bridgeUsdt0Evm;
|
|
3316
|
+
}
|
|
3317
|
+
} else {
|
|
3318
|
+
_T402WDK._defaultModules.bridgeUsdt0Evm = val;
|
|
3319
|
+
if (!_T402WDK._defaultModules.protocols) {
|
|
3320
|
+
_T402WDK._defaultModules.protocols = {};
|
|
3321
|
+
}
|
|
3322
|
+
_T402WDK._defaultModules.protocols.bridgeUsdt0Evm = val;
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
static get _WalletModules() {
|
|
3326
|
+
return _T402WDK._defaultModules.wallets ?? {};
|
|
3327
|
+
}
|
|
3328
|
+
static set _WalletModules(val) {
|
|
3329
|
+
_T402WDK._defaultModules.wallets = val;
|
|
3330
|
+
}
|
|
3331
|
+
static get _ProtocolModules() {
|
|
3332
|
+
return _T402WDK._defaultModules.protocols ?? {};
|
|
3333
|
+
}
|
|
3334
|
+
static set _ProtocolModules(val) {
|
|
3335
|
+
_T402WDK._defaultModules.protocols = val;
|
|
3336
|
+
}
|
|
3337
|
+
static get _fiatOnRampProvider() {
|
|
3338
|
+
return _T402WDK._defaultModules.fiatOnRampProvider ?? null;
|
|
3339
|
+
}
|
|
3340
|
+
static set _fiatOnRampProvider(val) {
|
|
3341
|
+
if (val === null) {
|
|
3342
|
+
delete _T402WDK._defaultModules.fiatOnRampProvider;
|
|
3343
|
+
} else {
|
|
3344
|
+
_T402WDK._defaultModules.fiatOnRampProvider = val;
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
static get _middlewares() {
|
|
3348
|
+
if (!_T402WDK._defaultModules.middlewares) {
|
|
3349
|
+
_T402WDK._defaultModules.middlewares = /* @__PURE__ */ new Map();
|
|
3350
|
+
}
|
|
3351
|
+
return _T402WDK._defaultModules.middlewares;
|
|
3352
|
+
}
|
|
3353
|
+
static set _middlewares(val) {
|
|
3354
|
+
_T402WDK._defaultModules.middlewares = val;
|
|
3355
|
+
}
|
|
3356
|
+
// HD path-derived signer cache
|
|
3357
|
+
_pathSignerCache = /* @__PURE__ */ new Map();
|
|
3358
|
+
// Multi-chain signer caches
|
|
3359
|
+
_tonSignerCache = /* @__PURE__ */ new Map();
|
|
3360
|
+
_svmSignerCache = /* @__PURE__ */ new Map();
|
|
3361
|
+
_tronSignerCache = /* @__PURE__ */ new Map();
|
|
3362
|
+
_sparkSignerCache = /* @__PURE__ */ new Map();
|
|
3363
|
+
_btcSignerCache = /* @__PURE__ */ new Map();
|
|
3364
|
+
/**
|
|
3365
|
+
* Register the Tether WDK modules
|
|
3366
|
+
*
|
|
3367
|
+
* This must be called before creating T402WDK instances if you want
|
|
3368
|
+
* to use the actual WDK. Otherwise, a mock implementation is used.
|
|
3369
|
+
*
|
|
3370
|
+
* Supports two registration patterns:
|
|
3371
|
+
*
|
|
3372
|
+
* 1. Legacy (EVM-only):
|
|
3373
|
+
* ```typescript
|
|
1906
3374
|
* T402WDK.registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm);
|
|
1907
3375
|
* ```
|
|
1908
3376
|
*
|
|
@@ -1931,6 +3399,14 @@ var T402WDK = class _T402WDK {
|
|
|
1931
3399
|
if (typeof WDK !== "function") {
|
|
1932
3400
|
throw new WDKInitializationError("WDK must be a constructor function");
|
|
1933
3401
|
}
|
|
3402
|
+
const wdkVersion = WDK.version;
|
|
3403
|
+
if (wdkVersion && typeof wdkVersion === "string") {
|
|
3404
|
+
if (!satisfiesSemverRange(wdkVersion, SUPPORTED_WDK_RANGE)) {
|
|
3405
|
+
throw new WDKInitializationError(
|
|
3406
|
+
`WDK version ${wdkVersion} is not supported. Required: ${SUPPORTED_WDK_RANGE}`
|
|
3407
|
+
);
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
1934
3410
|
_T402WDK._WDK = WDK;
|
|
1935
3411
|
if (modulesOrWalletManager && typeof modulesOrWalletManager === "object" && ("wallets" in modulesOrWalletManager || "protocols" in modulesOrWalletManager)) {
|
|
1936
3412
|
const modules = modulesOrWalletManager;
|
|
@@ -1981,6 +3457,18 @@ var T402WDK = class _T402WDK {
|
|
|
1981
3457
|
static isTronRegistered() {
|
|
1982
3458
|
return _T402WDK._WalletModules.tron !== void 0;
|
|
1983
3459
|
}
|
|
3460
|
+
/**
|
|
3461
|
+
* Check if Spark wallet manager is registered
|
|
3462
|
+
*/
|
|
3463
|
+
static isSparkRegistered() {
|
|
3464
|
+
return _T402WDK._WalletModules.spark !== void 0;
|
|
3465
|
+
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Check if Bitcoin wallet manager is registered
|
|
3468
|
+
*/
|
|
3469
|
+
static isBtcRegistered() {
|
|
3470
|
+
return _T402WDK._WalletModules.btc !== void 0;
|
|
3471
|
+
}
|
|
1984
3472
|
/**
|
|
1985
3473
|
* Get all registered wallet modules
|
|
1986
3474
|
*/
|
|
@@ -1997,6 +3485,62 @@ var T402WDK = class _T402WDK {
|
|
|
1997
3485
|
(key) => _T402WDK._ProtocolModules[key] !== void 0
|
|
1998
3486
|
);
|
|
1999
3487
|
}
|
|
3488
|
+
/**
|
|
3489
|
+
* Register a fiat on-ramp provider
|
|
3490
|
+
*
|
|
3491
|
+
* @param provider - A FiatOnRampProvider implementation (e.g., MoonpayOnRampProvider)
|
|
3492
|
+
*
|
|
3493
|
+
* @example
|
|
3494
|
+
* ```typescript
|
|
3495
|
+
* import { T402WDK, MoonpayOnRampProvider } from '@t402/wdk';
|
|
3496
|
+
*
|
|
3497
|
+
* T402WDK.registerFiatOnRamp(new MoonpayOnRampProvider({ apiKey: 'pk_test_...' }));
|
|
3498
|
+
* ```
|
|
3499
|
+
*/
|
|
3500
|
+
static registerFiatOnRamp(provider) {
|
|
3501
|
+
if (!provider || typeof provider.getQuote !== "function") {
|
|
3502
|
+
throw new WDKInitializationError("A valid FiatOnRampProvider is required");
|
|
3503
|
+
}
|
|
3504
|
+
_T402WDK._fiatOnRampProvider = provider;
|
|
3505
|
+
}
|
|
3506
|
+
/**
|
|
3507
|
+
* Check if a fiat on-ramp provider is registered
|
|
3508
|
+
*/
|
|
3509
|
+
static isFiatOnRampRegistered() {
|
|
3510
|
+
return _T402WDK._fiatOnRampProvider !== null;
|
|
3511
|
+
}
|
|
3512
|
+
/**
|
|
3513
|
+
* Register a pricing provider for fiat-to-crypto rate conversion
|
|
3514
|
+
*/
|
|
3515
|
+
static registerPricingProvider(provider) {
|
|
3516
|
+
registerPricingProvider(provider);
|
|
3517
|
+
}
|
|
3518
|
+
/**
|
|
3519
|
+
* Check if a pricing provider is registered
|
|
3520
|
+
*/
|
|
3521
|
+
static isPricingProviderRegistered() {
|
|
3522
|
+
return isPricingProviderRegistered();
|
|
3523
|
+
}
|
|
3524
|
+
/**
|
|
3525
|
+
* Register a middleware for a chain
|
|
3526
|
+
*/
|
|
3527
|
+
static registerMiddleware(chain, fn) {
|
|
3528
|
+
const existing = _T402WDK._middlewares.get(chain) ?? [];
|
|
3529
|
+
existing.push(fn);
|
|
3530
|
+
_T402WDK._middlewares.set(chain, existing);
|
|
3531
|
+
}
|
|
3532
|
+
/**
|
|
3533
|
+
* Get registered middlewares for a chain
|
|
3534
|
+
*/
|
|
3535
|
+
static getMiddlewares(chain) {
|
|
3536
|
+
return _T402WDK._middlewares.get(chain) ?? [];
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Clear all middlewares
|
|
3540
|
+
*/
|
|
3541
|
+
static clearMiddlewares() {
|
|
3542
|
+
_T402WDK._middlewares.clear();
|
|
3543
|
+
}
|
|
2000
3544
|
/**
|
|
2001
3545
|
* Generate a new random seed phrase
|
|
2002
3546
|
*
|
|
@@ -2047,6 +3591,131 @@ var T402WDK = class _T402WDK {
|
|
|
2047
3591
|
_T402WDK.registerWDK(WDK, config.modules);
|
|
2048
3592
|
return new _T402WDK(config.seedPhrase, config.chains, config.options);
|
|
2049
3593
|
}
|
|
3594
|
+
/**
|
|
3595
|
+
* Auto-discover installed WDK packages using dynamic imports.
|
|
3596
|
+
*
|
|
3597
|
+
* Probes known `@tetherto/wdk-*` packages and returns the ones that
|
|
3598
|
+
* are installed and importable.
|
|
3599
|
+
*
|
|
3600
|
+
* @returns Discovery result with available/unavailable packages and ready-to-use modules config
|
|
3601
|
+
*
|
|
3602
|
+
* @example
|
|
3603
|
+
* ```typescript
|
|
3604
|
+
* const result = await T402WDK.autoDiscover();
|
|
3605
|
+
* console.log('Found:', result.available);
|
|
3606
|
+
* console.log('Missing:', result.unavailable);
|
|
3607
|
+
* ```
|
|
3608
|
+
*/
|
|
3609
|
+
static async autoDiscover() {
|
|
3610
|
+
const walletPackages = {
|
|
3611
|
+
evm: "@tetherto/wdk-wallet-evm",
|
|
3612
|
+
solana: "@tetherto/wdk-wallet-solana",
|
|
3613
|
+
ton: "@tetherto/wdk-wallet-ton",
|
|
3614
|
+
tron: "@tetherto/wdk-wallet-tron",
|
|
3615
|
+
btc: "@tetherto/wdk-wallet-btc",
|
|
3616
|
+
spark: "@buildonspark/spark-sdk",
|
|
3617
|
+
evmErc4337: "@tetherto/wdk-wallet-evm-erc-4337",
|
|
3618
|
+
tonGasless: "@tetherto/wdk-wallet-ton-gasless",
|
|
3619
|
+
tronGasfree: "@tetherto/wdk-wallet-tron-gasfree"
|
|
3620
|
+
};
|
|
3621
|
+
const protocolPackages = {
|
|
3622
|
+
bridgeUsdt0Evm: "@tetherto/wdk-protocol-bridge-usdt0-evm",
|
|
3623
|
+
bridgeUsdt0Ton: "@tetherto/wdk-protocol-bridge-usdt0-ton",
|
|
3624
|
+
swapVeloraEvm: "@tetherto/wdk-protocol-swap-velora-evm",
|
|
3625
|
+
lendingAaveEvm: "@tetherto/wdk-protocol-lending-aave-evm"
|
|
3626
|
+
};
|
|
3627
|
+
const available = [];
|
|
3628
|
+
const unavailable = [];
|
|
3629
|
+
const wallets = {};
|
|
3630
|
+
const protocols = {};
|
|
3631
|
+
const walletEntries = Object.entries(walletPackages);
|
|
3632
|
+
const walletResults = await Promise.allSettled(
|
|
3633
|
+
walletEntries.map(async ([key, pkg]) => {
|
|
3634
|
+
const mod = await import(
|
|
3635
|
+
/* @vite-ignore */
|
|
3636
|
+
pkg
|
|
3637
|
+
);
|
|
3638
|
+
return { key, pkg, mod: mod.default ?? mod };
|
|
3639
|
+
})
|
|
3640
|
+
);
|
|
3641
|
+
for (let i = 0; i < walletResults.length; i++) {
|
|
3642
|
+
const result = walletResults[i];
|
|
3643
|
+
if (result.status === "fulfilled") {
|
|
3644
|
+
const { key, pkg, mod } = result.value;
|
|
3645
|
+
wallets[key] = mod;
|
|
3646
|
+
available.push(pkg);
|
|
3647
|
+
} else {
|
|
3648
|
+
unavailable.push(walletEntries[i][1]);
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
const protocolEntries = Object.entries(protocolPackages);
|
|
3652
|
+
const protocolResults = await Promise.allSettled(
|
|
3653
|
+
protocolEntries.map(async ([key, pkg]) => {
|
|
3654
|
+
const mod = await import(
|
|
3655
|
+
/* @vite-ignore */
|
|
3656
|
+
pkg
|
|
3657
|
+
);
|
|
3658
|
+
return { key, pkg, mod: mod.default ?? mod };
|
|
3659
|
+
})
|
|
3660
|
+
);
|
|
3661
|
+
for (let i = 0; i < protocolResults.length; i++) {
|
|
3662
|
+
const result = protocolResults[i];
|
|
3663
|
+
if (result.status === "fulfilled") {
|
|
3664
|
+
const { key, pkg, mod } = result.value;
|
|
3665
|
+
protocols[key] = mod;
|
|
3666
|
+
available.push(pkg);
|
|
3667
|
+
} else {
|
|
3668
|
+
unavailable.push(protocolEntries[i][1]);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
return {
|
|
3672
|
+
discovered: { wallets, protocols },
|
|
3673
|
+
available,
|
|
3674
|
+
unavailable
|
|
3675
|
+
};
|
|
3676
|
+
}
|
|
3677
|
+
/**
|
|
3678
|
+
* Auto-discover installed WDK modules, then create a fully configured T402WDK.
|
|
3679
|
+
*
|
|
3680
|
+
* Combines `autoDiscover()` + `create()` in one call. Any explicit
|
|
3681
|
+
* modules you pass in `config.modules` take precedence over discovered ones.
|
|
3682
|
+
*
|
|
3683
|
+
* @param config - Same as `T402WDKCreateConfig` but `modules` is optional/partial
|
|
3684
|
+
* @returns A ready-to-use T402WDK instance
|
|
3685
|
+
*
|
|
3686
|
+
* @example
|
|
3687
|
+
* ```typescript
|
|
3688
|
+
* const wdk = await T402WDK.autoCreate({
|
|
3689
|
+
* seedPhrase: 'your twelve word seed phrase ...',
|
|
3690
|
+
* chains: { arbitrum: 'https://arb1.arbitrum.io/rpc' },
|
|
3691
|
+
* });
|
|
3692
|
+
* ```
|
|
3693
|
+
*/
|
|
3694
|
+
static async autoCreate(config) {
|
|
3695
|
+
const { discovered } = await _T402WDK.autoDiscover();
|
|
3696
|
+
const mergedModules = {
|
|
3697
|
+
wallets: { ...discovered.wallets, ...config.modules?.wallets },
|
|
3698
|
+
protocols: { ...discovered.protocols, ...config.modules?.protocols }
|
|
3699
|
+
};
|
|
3700
|
+
let WDKRef;
|
|
3701
|
+
try {
|
|
3702
|
+
const wdkMod = await import(
|
|
3703
|
+
/* @vite-ignore */
|
|
3704
|
+
"@tetherto/wdk"
|
|
3705
|
+
);
|
|
3706
|
+
WDKRef = wdkMod.default ?? wdkMod;
|
|
3707
|
+
} catch {
|
|
3708
|
+
throw new WDKInitializationError(
|
|
3709
|
+
"@tetherto/wdk package not found. Install it with: npm install @tetherto/wdk"
|
|
3710
|
+
);
|
|
3711
|
+
}
|
|
3712
|
+
return _T402WDK.create(WDKRef, {
|
|
3713
|
+
seedPhrase: config.seedPhrase,
|
|
3714
|
+
chains: config.chains,
|
|
3715
|
+
modules: mergedModules,
|
|
3716
|
+
options: config.options
|
|
3717
|
+
});
|
|
3718
|
+
}
|
|
2050
3719
|
/**
|
|
2051
3720
|
* Create a T402WDK from a pre-configured @tetherto/wdk instance.
|
|
2052
3721
|
*
|
|
@@ -2066,6 +3735,31 @@ var T402WDK = class _T402WDK {
|
|
|
2066
3735
|
instance._initializationError = null;
|
|
2067
3736
|
return instance;
|
|
2068
3737
|
}
|
|
3738
|
+
/**
|
|
3739
|
+
* Create a T402WDK instance from an encrypted seed.
|
|
3740
|
+
*
|
|
3741
|
+
* @example
|
|
3742
|
+
* ```typescript
|
|
3743
|
+
* const encrypted = JSON.parse(fs.readFileSync('seed.enc.json', 'utf8'))
|
|
3744
|
+
* const wdk = await T402WDK.fromEncryptedSeed(encrypted, 'my-password', {
|
|
3745
|
+
* arbitrum: 'https://arb1.arbitrum.io/rpc',
|
|
3746
|
+
* })
|
|
3747
|
+
* ```
|
|
3748
|
+
*/
|
|
3749
|
+
static async fromEncryptedSeed(encrypted, password, config, options) {
|
|
3750
|
+
const seedPhrase = await decryptSeed(encrypted, password);
|
|
3751
|
+
return new _T402WDK(seedPhrase, config, options);
|
|
3752
|
+
}
|
|
3753
|
+
/**
|
|
3754
|
+
* Encrypt the current seed phrase for secure storage.
|
|
3755
|
+
*
|
|
3756
|
+
* @param password - Password to encrypt with
|
|
3757
|
+
* @returns Encrypted seed data suitable for JSON serialization
|
|
3758
|
+
*/
|
|
3759
|
+
async encryptSeed(password) {
|
|
3760
|
+
this.assertNotDisposed();
|
|
3761
|
+
return encryptSeed(this._seedPhrase, password);
|
|
3762
|
+
}
|
|
2069
3763
|
/**
|
|
2070
3764
|
* Get all signers as an array ready for T402 HTTP clients.
|
|
2071
3765
|
*
|
|
@@ -2079,6 +3773,7 @@ var T402WDK = class _T402WDK {
|
|
|
2079
3773
|
* ```
|
|
2080
3774
|
*/
|
|
2081
3775
|
async getAllSigners(options) {
|
|
3776
|
+
this.assertNotDisposed();
|
|
2082
3777
|
const accountIndex = options?.accountIndex ?? 0;
|
|
2083
3778
|
const schemes = options?.schemes ?? ["exact"];
|
|
2084
3779
|
const includeNonEvm = options?.includeNonEvm ?? true;
|
|
@@ -2102,7 +3797,7 @@ var T402WDK = class _T402WDK {
|
|
|
2102
3797
|
if (!includeNonEvm) {
|
|
2103
3798
|
return entries;
|
|
2104
3799
|
}
|
|
2105
|
-
if (
|
|
3800
|
+
if (this._walletModules.ton !== void 0) {
|
|
2106
3801
|
try {
|
|
2107
3802
|
const signer = await this.getTonSigner(accountIndex);
|
|
2108
3803
|
for (const scheme of schemes) {
|
|
@@ -2111,7 +3806,7 @@ var T402WDK = class _T402WDK {
|
|
|
2111
3806
|
} catch {
|
|
2112
3807
|
}
|
|
2113
3808
|
}
|
|
2114
|
-
if (
|
|
3809
|
+
if (this._walletModules.solana !== void 0) {
|
|
2115
3810
|
try {
|
|
2116
3811
|
const signer = await this.getSvmSigner(accountIndex);
|
|
2117
3812
|
for (const scheme of schemes) {
|
|
@@ -2125,7 +3820,7 @@ var T402WDK = class _T402WDK {
|
|
|
2125
3820
|
} catch {
|
|
2126
3821
|
}
|
|
2127
3822
|
}
|
|
2128
|
-
if (
|
|
3823
|
+
if (this._walletModules.tron !== void 0) {
|
|
2129
3824
|
try {
|
|
2130
3825
|
const signer = await this.getTronSigner(accountIndex);
|
|
2131
3826
|
for (const scheme of schemes) {
|
|
@@ -2134,6 +3829,29 @@ var T402WDK = class _T402WDK {
|
|
|
2134
3829
|
} catch {
|
|
2135
3830
|
}
|
|
2136
3831
|
}
|
|
3832
|
+
if (this._walletModules.spark !== void 0) {
|
|
3833
|
+
try {
|
|
3834
|
+
const signer = await this.getSparkSigner(accountIndex);
|
|
3835
|
+
for (const scheme of schemes) {
|
|
3836
|
+
entries.push({ scheme, network: "spark:mainnet", signer, family: "spark" });
|
|
3837
|
+
}
|
|
3838
|
+
} catch {
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
if (this._walletModules.btc !== void 0) {
|
|
3842
|
+
try {
|
|
3843
|
+
const signer = await this.getBtcSigner(accountIndex);
|
|
3844
|
+
for (const scheme of schemes) {
|
|
3845
|
+
entries.push({
|
|
3846
|
+
scheme,
|
|
3847
|
+
network: "bip122:000000000019d6689c085ae165831e93",
|
|
3848
|
+
signer,
|
|
3849
|
+
family: "btc"
|
|
3850
|
+
});
|
|
3851
|
+
}
|
|
3852
|
+
} catch {
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
2137
3855
|
return entries;
|
|
2138
3856
|
}
|
|
2139
3857
|
/**
|
|
@@ -2161,10 +3879,45 @@ var T402WDK = class _T402WDK {
|
|
|
2161
3879
|
}
|
|
2162
3880
|
this._seedPhrase = seedPhrase;
|
|
2163
3881
|
this._balanceCache = new BalanceCache(options.cache);
|
|
3882
|
+
this._wdkConstructor = options.wdk ?? _T402WDK._defaultModules.wdk ?? null;
|
|
3883
|
+
this._walletModules = options.wallets ?? _T402WDK._defaultModules.wallets ?? {};
|
|
3884
|
+
this._protocolModules = options.protocols ?? _T402WDK._defaultModules.protocols ?? {};
|
|
3885
|
+
this._walletManagerEvm = this._walletModules.evm ?? _T402WDK._defaultModules.walletManagerEvm ?? null;
|
|
3886
|
+
this._bridgeUsdt0Evm = this._protocolModules.bridgeUsdt0Evm ?? _T402WDK._defaultModules.bridgeUsdt0Evm ?? null;
|
|
3887
|
+
this._fiatOnRampProvider = options.fiatOnRampProvider ?? _T402WDK._defaultModules.fiatOnRampProvider ?? null;
|
|
3888
|
+
if (options.middlewares) {
|
|
3889
|
+
this._middlewares = new Map(options.middlewares);
|
|
3890
|
+
} else if (_T402WDK._defaultModules.middlewares) {
|
|
3891
|
+
this._middlewares = new Map(_T402WDK._defaultModules.middlewares);
|
|
3892
|
+
}
|
|
3893
|
+
this._retryConfig = options.retry;
|
|
2164
3894
|
for (const [chain, chainConfig] of Object.entries(config)) {
|
|
2165
3895
|
if (chainConfig) {
|
|
2166
3896
|
try {
|
|
2167
|
-
|
|
3897
|
+
if (typeof chainConfig === "object" && "provider" in chainConfig && Array.isArray(chainConfig.provider)) {
|
|
3898
|
+
const urls = chainConfig.provider;
|
|
3899
|
+
if (urls.length === 0) {
|
|
3900
|
+
throw new Error("Provider array must contain at least one URL");
|
|
3901
|
+
}
|
|
3902
|
+
const failoverConfig = {
|
|
3903
|
+
urls,
|
|
3904
|
+
...chainConfig.failover ?? {}
|
|
3905
|
+
};
|
|
3906
|
+
const failoverProvider = new FailoverProvider(failoverConfig);
|
|
3907
|
+
this._failoverProviders.set(chain, failoverProvider);
|
|
3908
|
+
const normalized = normalizeChainConfig(chain, failoverProvider.getCurrentUrl());
|
|
3909
|
+
if (chainConfig.chainId !== void 0) normalized.chainId = chainConfig.chainId;
|
|
3910
|
+
if (chainConfig.network !== void 0) normalized.network = chainConfig.network;
|
|
3911
|
+
this._normalizedChains.set(chain, normalized);
|
|
3912
|
+
} else {
|
|
3913
|
+
this._normalizedChains.set(
|
|
3914
|
+
chain,
|
|
3915
|
+
normalizeChainConfig(
|
|
3916
|
+
chain,
|
|
3917
|
+
chainConfig
|
|
3918
|
+
)
|
|
3919
|
+
);
|
|
3920
|
+
}
|
|
2168
3921
|
} catch (error) {
|
|
2169
3922
|
throw new ChainError(
|
|
2170
3923
|
2003 /* INVALID_CHAIN_CONFIG */,
|
|
@@ -2175,10 +3928,24 @@ var T402WDK = class _T402WDK {
|
|
|
2175
3928
|
}
|
|
2176
3929
|
}
|
|
2177
3930
|
this._addDefaultChainsIfNeeded();
|
|
2178
|
-
if (!isFromWDK &&
|
|
3931
|
+
if (!isFromWDK && this._wdkConstructor) {
|
|
2179
3932
|
this._initializeWDK();
|
|
2180
3933
|
}
|
|
2181
3934
|
}
|
|
3935
|
+
/**
|
|
3936
|
+
* Guard: throw if this instance has been disposed (#194)
|
|
3937
|
+
*/
|
|
3938
|
+
assertNotDisposed() {
|
|
3939
|
+
if (this._disposed) {
|
|
3940
|
+
throw new WDKError(1002 /* WDK_NOT_INITIALIZED */, "T402WDK has been disposed");
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
/**
|
|
3944
|
+
* Whether this instance has been disposed
|
|
3945
|
+
*/
|
|
3946
|
+
get isDisposed() {
|
|
3947
|
+
return this._disposed;
|
|
3948
|
+
}
|
|
2182
3949
|
/**
|
|
2183
3950
|
* Add default chain configurations for common chains
|
|
2184
3951
|
*/
|
|
@@ -2194,22 +3961,24 @@ var T402WDK = class _T402WDK {
|
|
|
2194
3961
|
* Initialize the underlying WDK instance
|
|
2195
3962
|
*/
|
|
2196
3963
|
_initializeWDK() {
|
|
2197
|
-
if (!
|
|
3964
|
+
if (!this._wdkConstructor) {
|
|
2198
3965
|
this._initializationError = new WDKInitializationError("WDK not registered");
|
|
2199
3966
|
return;
|
|
2200
3967
|
}
|
|
2201
|
-
if (!
|
|
3968
|
+
if (!this._walletManagerEvm) {
|
|
2202
3969
|
this._initializationError = new WDKInitializationError(
|
|
2203
3970
|
"WalletManagerEvm not registered. Call T402WDK.registerWDK(WDK, WalletManagerEvm) to enable wallet functionality."
|
|
2204
3971
|
);
|
|
2205
3972
|
return;
|
|
2206
3973
|
}
|
|
2207
3974
|
try {
|
|
2208
|
-
let wdk = new
|
|
3975
|
+
let wdk = new this._wdkConstructor(this._seedPhrase);
|
|
2209
3976
|
for (const [chain, config] of this._normalizedChains) {
|
|
2210
3977
|
try {
|
|
2211
|
-
|
|
2212
|
-
|
|
3978
|
+
const failover = this._failoverProviders.get(chain);
|
|
3979
|
+
const providerUrl = failover ? failover.getCurrentUrl() : config.provider;
|
|
3980
|
+
wdk = wdk.registerWallet(chain, this._walletManagerEvm, {
|
|
3981
|
+
provider: providerUrl,
|
|
2213
3982
|
chainId: config.chainId
|
|
2214
3983
|
});
|
|
2215
3984
|
} catch (error) {
|
|
@@ -2220,15 +3989,44 @@ var T402WDK = class _T402WDK {
|
|
|
2220
3989
|
);
|
|
2221
3990
|
}
|
|
2222
3991
|
}
|
|
2223
|
-
if (
|
|
3992
|
+
if (this._bridgeUsdt0Evm) {
|
|
2224
3993
|
try {
|
|
2225
|
-
wdk = wdk.registerProtocol("bridge-usdt0",
|
|
3994
|
+
wdk = wdk.registerProtocol("bridge-usdt0", this._bridgeUsdt0Evm);
|
|
2226
3995
|
} catch (error) {
|
|
2227
3996
|
console.warn(
|
|
2228
3997
|
`Failed to register USDT0 bridge protocol: ${error instanceof Error ? error.message : String(error)}`
|
|
2229
3998
|
);
|
|
2230
3999
|
}
|
|
2231
4000
|
}
|
|
4001
|
+
if (this._protocolModules.swapVeloraEvm) {
|
|
4002
|
+
try {
|
|
4003
|
+
wdk = wdk.registerProtocol("swap-velora", this._protocolModules.swapVeloraEvm);
|
|
4004
|
+
} catch (error) {
|
|
4005
|
+
console.warn(
|
|
4006
|
+
`Failed to register Velora swap protocol: ${error instanceof Error ? error.message : String(error)}`
|
|
4007
|
+
);
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
if (this._protocolModules.lendingAaveEvm) {
|
|
4011
|
+
try {
|
|
4012
|
+
wdk = wdk.registerProtocol("lending-aave", this._protocolModules.lendingAaveEvm);
|
|
4013
|
+
} catch (error) {
|
|
4014
|
+
console.warn(
|
|
4015
|
+
`Failed to register Aave lending protocol: ${error instanceof Error ? error.message : String(error)}`
|
|
4016
|
+
);
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
if (typeof wdk.registerMiddleware === "function") {
|
|
4020
|
+
for (const [chain, fns] of this._middlewares) {
|
|
4021
|
+
for (const fn of fns) {
|
|
4022
|
+
try {
|
|
4023
|
+
;
|
|
4024
|
+
wdk.registerMiddleware(chain, fn);
|
|
4025
|
+
} catch {
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
2232
4030
|
this._wdk = wdk;
|
|
2233
4031
|
this._initializationError = null;
|
|
2234
4032
|
} catch (error) {
|
|
@@ -2267,6 +4065,47 @@ var T402WDK = class _T402WDK {
|
|
|
2267
4065
|
get initializationError() {
|
|
2268
4066
|
return this._initializationError;
|
|
2269
4067
|
}
|
|
4068
|
+
// ========== Event Emitter ==========
|
|
4069
|
+
/**
|
|
4070
|
+
* Subscribe to a T402 event
|
|
4071
|
+
*/
|
|
4072
|
+
on(event, handler) {
|
|
4073
|
+
this._events.on(event, handler);
|
|
4074
|
+
return this;
|
|
4075
|
+
}
|
|
4076
|
+
/**
|
|
4077
|
+
* Unsubscribe from a T402 event
|
|
4078
|
+
*/
|
|
4079
|
+
off(event, handler) {
|
|
4080
|
+
this._events.off(event, handler);
|
|
4081
|
+
return this;
|
|
4082
|
+
}
|
|
4083
|
+
/**
|
|
4084
|
+
* Subscribe to a T402 event (fires once then auto-unsubscribes)
|
|
4085
|
+
*/
|
|
4086
|
+
once(event, handler) {
|
|
4087
|
+
this._events.once(event, handler);
|
|
4088
|
+
return this;
|
|
4089
|
+
}
|
|
4090
|
+
/**
|
|
4091
|
+
* Emit a T402 event
|
|
4092
|
+
*/
|
|
4093
|
+
emit(event, data) {
|
|
4094
|
+
return this._events.emit(event, data);
|
|
4095
|
+
}
|
|
4096
|
+
// ========== Receipt Store ==========
|
|
4097
|
+
/**
|
|
4098
|
+
* Get the payment receipt store
|
|
4099
|
+
*/
|
|
4100
|
+
getReceiptStore() {
|
|
4101
|
+
return this._receiptStore;
|
|
4102
|
+
}
|
|
4103
|
+
/**
|
|
4104
|
+
* Set a custom payment receipt store backend
|
|
4105
|
+
*/
|
|
4106
|
+
setReceiptStore(store) {
|
|
4107
|
+
this._receiptStore = store;
|
|
4108
|
+
}
|
|
2270
4109
|
/**
|
|
2271
4110
|
* Get all configured chains
|
|
2272
4111
|
*/
|
|
@@ -2295,6 +4134,7 @@ var T402WDK = class _T402WDK {
|
|
|
2295
4134
|
* @returns An initialized WDKSigner
|
|
2296
4135
|
*/
|
|
2297
4136
|
async getSigner(chain, accountIndex = 0) {
|
|
4137
|
+
this.assertNotDisposed();
|
|
2298
4138
|
if (!chain || typeof chain !== "string") {
|
|
2299
4139
|
throw new ChainError(
|
|
2300
4140
|
2001 /* CHAIN_NOT_CONFIGURED */,
|
|
@@ -2318,7 +4158,12 @@ var T402WDK = class _T402WDK {
|
|
|
2318
4158
|
try {
|
|
2319
4159
|
const signer = await createWDKSigner(this.wdk, chain, accountIndex);
|
|
2320
4160
|
this._signerCache.set(cacheKey, signer);
|
|
2321
|
-
|
|
4161
|
+
this._events.emit("signer:initialized", {
|
|
4162
|
+
chain,
|
|
4163
|
+
address: signer.address,
|
|
4164
|
+
family: "evm"
|
|
4165
|
+
});
|
|
4166
|
+
return signer;
|
|
2322
4167
|
} catch (error) {
|
|
2323
4168
|
if (isWDKError(error)) {
|
|
2324
4169
|
throw error;
|
|
@@ -2337,9 +4182,69 @@ var T402WDK = class _T402WDK {
|
|
|
2337
4182
|
*/
|
|
2338
4183
|
clearSignerCache() {
|
|
2339
4184
|
this._signerCache.clear();
|
|
4185
|
+
this._pathSignerCache.clear();
|
|
2340
4186
|
this._tonSignerCache.clear();
|
|
2341
4187
|
this._svmSignerCache.clear();
|
|
2342
4188
|
this._tronSignerCache.clear();
|
|
4189
|
+
this._sparkSignerCache.clear();
|
|
4190
|
+
this._btcSignerCache.clear();
|
|
4191
|
+
}
|
|
4192
|
+
// ========== Fee Rates & Cost Estimation ==========
|
|
4193
|
+
/**
|
|
4194
|
+
* Get current fee rates for a chain
|
|
4195
|
+
*/
|
|
4196
|
+
async getFeeRates(chain) {
|
|
4197
|
+
if (this._wdk && typeof this._wdk.getFeeRates === "function") {
|
|
4198
|
+
return this._wdk.getFeeRates(chain);
|
|
4199
|
+
}
|
|
4200
|
+
return { low: 1000000000n, medium: 2000000000n, high: 5000000000n };
|
|
4201
|
+
}
|
|
4202
|
+
/**
|
|
4203
|
+
* Estimate total cost of a payment on a chain
|
|
4204
|
+
*/
|
|
4205
|
+
async estimatePaymentCost(chain, amount) {
|
|
4206
|
+
const signer = await this.getSigner(chain);
|
|
4207
|
+
const nativeBalance = await signer.getBalance();
|
|
4208
|
+
let estimatedGasCost = 100000n * 2000000000n;
|
|
4209
|
+
try {
|
|
4210
|
+
const feeRates = await this.getFeeRates(chain);
|
|
4211
|
+
const mediumRate = feeRates.medium ?? 2000000000n;
|
|
4212
|
+
estimatedGasCost = 100000n * mediumRate;
|
|
4213
|
+
} catch {
|
|
4214
|
+
}
|
|
4215
|
+
const config = this.getChainConfig(chain);
|
|
4216
|
+
return {
|
|
4217
|
+
paymentAmount: amount,
|
|
4218
|
+
estimatedGasCost,
|
|
4219
|
+
nativeBalance,
|
|
4220
|
+
canAffordGas: nativeBalance >= estimatedGasCost,
|
|
4221
|
+
chain,
|
|
4222
|
+
network: config?.network ?? ""
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
// ========== HD Derivation Paths ==========
|
|
4226
|
+
/**
|
|
4227
|
+
* Get a signer using a custom BIP-44 derivation path
|
|
4228
|
+
*/
|
|
4229
|
+
async getSignerByPath(chain, path) {
|
|
4230
|
+
const cacheKey = `${chain}:path:${path}`;
|
|
4231
|
+
const cached = this._pathSignerCache.get(cacheKey);
|
|
4232
|
+
if (cached) return cached;
|
|
4233
|
+
if (!this._wdk) {
|
|
4234
|
+
throw new WDKInitializationError("WDK not initialized");
|
|
4235
|
+
}
|
|
4236
|
+
if (typeof this._wdk.getAccountByPath !== "function") {
|
|
4237
|
+
throw new Error(
|
|
4238
|
+
"getAccountByPath not available. Upgrade @tetherto/wdk to support custom derivation paths."
|
|
4239
|
+
);
|
|
4240
|
+
}
|
|
4241
|
+
const account = await this._wdk.getAccountByPath(chain, path);
|
|
4242
|
+
const address = await account.getAddress();
|
|
4243
|
+
const signer = new WDKSigner(this._wdk, chain, 0);
|
|
4244
|
+
signer._account = account;
|
|
4245
|
+
signer._address = address;
|
|
4246
|
+
this._pathSignerCache.set(cacheKey, signer);
|
|
4247
|
+
return signer;
|
|
2343
4248
|
}
|
|
2344
4249
|
// ========== Multi-Chain Signers ==========
|
|
2345
4250
|
/**
|
|
@@ -2359,11 +4264,12 @@ var T402WDK = class _T402WDK {
|
|
|
2359
4264
|
* ```
|
|
2360
4265
|
*/
|
|
2361
4266
|
async getTonSigner(accountIndex = 0) {
|
|
4267
|
+
this.assertNotDisposed();
|
|
2362
4268
|
const cached = this._tonSignerCache.get(accountIndex);
|
|
2363
4269
|
if (cached) {
|
|
2364
4270
|
return cached;
|
|
2365
4271
|
}
|
|
2366
|
-
if (!
|
|
4272
|
+
if (!this._walletModules.ton) {
|
|
2367
4273
|
throw new ChainError(
|
|
2368
4274
|
2002 /* CHAIN_NOT_SUPPORTED */,
|
|
2369
4275
|
"TON wallet manager not registered. Call T402WDK.registerWDK(WDK, { wallets: { ton: WalletManagerTon } }).",
|
|
@@ -2374,6 +4280,11 @@ var T402WDK = class _T402WDK {
|
|
|
2374
4280
|
const account = await this.wdk.getAccount("ton", accountIndex);
|
|
2375
4281
|
const signer = await createWDKTonSigner(account);
|
|
2376
4282
|
this._tonSignerCache.set(accountIndex, signer);
|
|
4283
|
+
this._events.emit("signer:initialized", {
|
|
4284
|
+
chain: "ton",
|
|
4285
|
+
address: signer.address.toString(),
|
|
4286
|
+
family: "ton"
|
|
4287
|
+
});
|
|
2377
4288
|
return signer;
|
|
2378
4289
|
} catch (error) {
|
|
2379
4290
|
if (isWDKError(error)) {
|
|
@@ -2402,11 +4313,12 @@ var T402WDK = class _T402WDK {
|
|
|
2402
4313
|
* ```
|
|
2403
4314
|
*/
|
|
2404
4315
|
async getSvmSigner(accountIndex = 0) {
|
|
4316
|
+
this.assertNotDisposed();
|
|
2405
4317
|
const cached = this._svmSignerCache.get(accountIndex);
|
|
2406
4318
|
if (cached) {
|
|
2407
4319
|
return cached;
|
|
2408
4320
|
}
|
|
2409
|
-
if (!
|
|
4321
|
+
if (!this._walletModules.solana) {
|
|
2410
4322
|
throw new ChainError(
|
|
2411
4323
|
2002 /* CHAIN_NOT_SUPPORTED */,
|
|
2412
4324
|
"Solana wallet manager not registered. Call T402WDK.registerWDK(WDK, { wallets: { solana: WalletManagerSolana } }).",
|
|
@@ -2420,6 +4332,11 @@ var T402WDK = class _T402WDK {
|
|
|
2420
4332
|
);
|
|
2421
4333
|
const signer = await createWDKSvmSigner(account);
|
|
2422
4334
|
this._svmSignerCache.set(accountIndex, signer);
|
|
4335
|
+
this._events.emit("signer:initialized", {
|
|
4336
|
+
chain: "solana",
|
|
4337
|
+
address: signer.address.toString(),
|
|
4338
|
+
family: "svm"
|
|
4339
|
+
});
|
|
2423
4340
|
return signer;
|
|
2424
4341
|
} catch (error) {
|
|
2425
4342
|
if (isWDKError(error)) {
|
|
@@ -2451,13 +4368,14 @@ var T402WDK = class _T402WDK {
|
|
|
2451
4368
|
* ```
|
|
2452
4369
|
*/
|
|
2453
4370
|
async getTronSigner(accountIndex = 0, rpcUrl) {
|
|
4371
|
+
this.assertNotDisposed();
|
|
2454
4372
|
if (!rpcUrl) {
|
|
2455
4373
|
const cached = this._tronSignerCache.get(accountIndex);
|
|
2456
4374
|
if (cached) {
|
|
2457
4375
|
return cached;
|
|
2458
4376
|
}
|
|
2459
4377
|
}
|
|
2460
|
-
if (!
|
|
4378
|
+
if (!this._walletModules.tron) {
|
|
2461
4379
|
throw new ChainError(
|
|
2462
4380
|
2002 /* CHAIN_NOT_SUPPORTED */,
|
|
2463
4381
|
"TRON wallet manager not registered. Call T402WDK.registerWDK(WDK, { wallets: { tron: WalletManagerTron } }).",
|
|
@@ -2470,6 +4388,11 @@ var T402WDK = class _T402WDK {
|
|
|
2470
4388
|
if (!rpcUrl) {
|
|
2471
4389
|
this._tronSignerCache.set(accountIndex, signer);
|
|
2472
4390
|
}
|
|
4391
|
+
this._events.emit("signer:initialized", {
|
|
4392
|
+
chain: "tron",
|
|
4393
|
+
address: signer.address,
|
|
4394
|
+
family: "tron"
|
|
4395
|
+
});
|
|
2473
4396
|
return signer;
|
|
2474
4397
|
} catch (error) {
|
|
2475
4398
|
if (isWDKError(error)) {
|
|
@@ -2481,6 +4404,109 @@ var T402WDK = class _T402WDK {
|
|
|
2481
4404
|
});
|
|
2482
4405
|
}
|
|
2483
4406
|
}
|
|
4407
|
+
/**
|
|
4408
|
+
* Get a Spark (Bitcoin L2) signer for T402 payments
|
|
4409
|
+
*
|
|
4410
|
+
* @param accountIndex - HD wallet account index (default: 0)
|
|
4411
|
+
* @throws {ChainError} If Spark wallet manager is not registered
|
|
4412
|
+
* @returns An initialized WDKSparkSignerAdapter
|
|
4413
|
+
*
|
|
4414
|
+
* @example
|
|
4415
|
+
* ```typescript
|
|
4416
|
+
* const sparkSigner = await wallet.getSparkSigner();
|
|
4417
|
+
*
|
|
4418
|
+
* const client = createT402HTTPClient({
|
|
4419
|
+
* signers: [{ scheme: 'exact', network: 'spark:mainnet', signer: sparkSigner }]
|
|
4420
|
+
* });
|
|
4421
|
+
* ```
|
|
4422
|
+
*/
|
|
4423
|
+
async getSparkSigner(accountIndex = 0) {
|
|
4424
|
+
this.assertNotDisposed();
|
|
4425
|
+
const cached = this._sparkSignerCache.get(accountIndex);
|
|
4426
|
+
if (cached) {
|
|
4427
|
+
return cached;
|
|
4428
|
+
}
|
|
4429
|
+
if (!this._walletModules.spark) {
|
|
4430
|
+
throw new ChainError(
|
|
4431
|
+
2002 /* CHAIN_NOT_SUPPORTED */,
|
|
4432
|
+
"Spark wallet manager not registered. Call T402WDK.registerWDK(WDK, { wallets: { spark: SparkWalletManager } }).",
|
|
4433
|
+
{ chain: "spark" }
|
|
4434
|
+
);
|
|
4435
|
+
}
|
|
4436
|
+
try {
|
|
4437
|
+
const account = await this.wdk.getAccount(
|
|
4438
|
+
"spark",
|
|
4439
|
+
accountIndex
|
|
4440
|
+
);
|
|
4441
|
+
const signer = await createWDKSparkSigner(account);
|
|
4442
|
+
this._sparkSignerCache.set(accountIndex, signer);
|
|
4443
|
+
this._events.emit("signer:initialized", {
|
|
4444
|
+
chain: "spark",
|
|
4445
|
+
address: signer.address,
|
|
4446
|
+
family: "spark"
|
|
4447
|
+
});
|
|
4448
|
+
return signer;
|
|
4449
|
+
} catch (error) {
|
|
4450
|
+
if (isWDKError(error)) {
|
|
4451
|
+
throw error;
|
|
4452
|
+
}
|
|
4453
|
+
throw wrapError(error, 3001 /* SIGNER_NOT_INITIALIZED */, "Failed to create Spark signer", {
|
|
4454
|
+
chain: "spark",
|
|
4455
|
+
accountIndex
|
|
4456
|
+
});
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
/**
|
|
4460
|
+
* Get a Bitcoin (BTC) on-chain signer for T402 payments
|
|
4461
|
+
*
|
|
4462
|
+
* @param accountIndex - HD wallet account index (default: 0)
|
|
4463
|
+
* @throws {ChainError} If Bitcoin wallet manager is not registered
|
|
4464
|
+
* @returns An initialized WDKBtcSignerAdapter
|
|
4465
|
+
*
|
|
4466
|
+
* @example
|
|
4467
|
+
* ```typescript
|
|
4468
|
+
* const btcSigner = await wallet.getBtcSigner();
|
|
4469
|
+
*
|
|
4470
|
+
* const client = createT402HTTPClient({
|
|
4471
|
+
* signers: [{ scheme: 'exact', network: 'bip122:000000000019d6689c085ae165831e93', signer: btcSigner }]
|
|
4472
|
+
* });
|
|
4473
|
+
* ```
|
|
4474
|
+
*/
|
|
4475
|
+
async getBtcSigner(accountIndex = 0) {
|
|
4476
|
+
this.assertNotDisposed();
|
|
4477
|
+
const cached = this._btcSignerCache.get(accountIndex);
|
|
4478
|
+
if (cached) {
|
|
4479
|
+
return cached;
|
|
4480
|
+
}
|
|
4481
|
+
if (!this._walletModules.btc) {
|
|
4482
|
+
throw new ChainError(
|
|
4483
|
+
2002 /* CHAIN_NOT_SUPPORTED */,
|
|
4484
|
+
"Bitcoin wallet manager not registered. Call T402WDK.registerWDK(WDK, { wallets: { btc: WalletManagerBtc } }).",
|
|
4485
|
+
{ chain: "btc" }
|
|
4486
|
+
);
|
|
4487
|
+
}
|
|
4488
|
+
try {
|
|
4489
|
+
const account = await this.wdk.getAccount("btc", accountIndex);
|
|
4490
|
+
const signer = await createWDKBtcSigner(account);
|
|
4491
|
+
this._btcSignerCache.set(accountIndex, signer);
|
|
4492
|
+
this._events.emit("signer:initialized", {
|
|
4493
|
+
chain: "btc",
|
|
4494
|
+
address: signer.address,
|
|
4495
|
+
family: "btc"
|
|
4496
|
+
});
|
|
4497
|
+
return signer;
|
|
4498
|
+
} catch (error) {
|
|
4499
|
+
if (isWDKError(error)) {
|
|
4500
|
+
throw error;
|
|
4501
|
+
}
|
|
4502
|
+
throw wrapError(
|
|
4503
|
+
error,
|
|
4504
|
+
3001 /* SIGNER_NOT_INITIALIZED */,
|
|
4505
|
+
"Failed to create Bitcoin signer",
|
|
4506
|
+
{ chain: "btc", accountIndex }
|
|
4507
|
+
);
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
2484
4510
|
/**
|
|
2485
4511
|
* Get a signer for a specific chain family
|
|
2486
4512
|
*
|
|
@@ -2519,10 +4545,14 @@ var T402WDK = class _T402WDK {
|
|
|
2519
4545
|
return this.getSvmSigner(typeof chainOrIndex === "number" ? chainOrIndex : accountIndex);
|
|
2520
4546
|
case "tron":
|
|
2521
4547
|
return this.getTronSigner(typeof chainOrIndex === "number" ? chainOrIndex : accountIndex);
|
|
4548
|
+
case "spark":
|
|
4549
|
+
return this.getSparkSigner(typeof chainOrIndex === "number" ? chainOrIndex : accountIndex);
|
|
4550
|
+
case "btc":
|
|
4551
|
+
return this.getBtcSigner(typeof chainOrIndex === "number" ? chainOrIndex : accountIndex);
|
|
2522
4552
|
default:
|
|
2523
4553
|
throw new ChainError(
|
|
2524
4554
|
2002 /* CHAIN_NOT_SUPPORTED */,
|
|
2525
|
-
`Chain family "${family}" is not supported. Available: evm, ton, svm, tron`,
|
|
4555
|
+
`Chain family "${family}" is not supported. Available: evm, ton, svm, tron, spark, btc`,
|
|
2526
4556
|
{ chain: family }
|
|
2527
4557
|
);
|
|
2528
4558
|
}
|
|
@@ -2536,6 +4566,7 @@ var T402WDK = class _T402WDK {
|
|
|
2536
4566
|
* @throws {SignerError} If address fetch fails
|
|
2537
4567
|
*/
|
|
2538
4568
|
async getAddress(chain, accountIndex = 0) {
|
|
4569
|
+
this.assertNotDisposed();
|
|
2539
4570
|
const signer = await this.getSigner(chain, accountIndex);
|
|
2540
4571
|
return signer.address;
|
|
2541
4572
|
}
|
|
@@ -2547,6 +4578,7 @@ var T402WDK = class _T402WDK {
|
|
|
2547
4578
|
* @throws {BalanceError} If balance fetch fails
|
|
2548
4579
|
*/
|
|
2549
4580
|
async getUsdt0Balance(chain, accountIndex = 0) {
|
|
4581
|
+
this.assertNotDisposed();
|
|
2550
4582
|
const usdt0Address = USDT0_ADDRESSES[chain];
|
|
2551
4583
|
if (!usdt0Address) {
|
|
2552
4584
|
return 0n;
|
|
@@ -2558,7 +4590,7 @@ var T402WDK = class _T402WDK {
|
|
|
2558
4590
|
chain,
|
|
2559
4591
|
usdt0Address,
|
|
2560
4592
|
address,
|
|
2561
|
-
|
|
4593
|
+
() => this._withRetry(() => signer.getTokenBalance(usdt0Address))
|
|
2562
4594
|
);
|
|
2563
4595
|
} catch (error) {
|
|
2564
4596
|
if (isWDKError(error) && error.code === 5002 /* TOKEN_BALANCE_FETCH_FAILED */) {
|
|
@@ -2575,6 +4607,7 @@ var T402WDK = class _T402WDK {
|
|
|
2575
4607
|
* @throws {BalanceError} If balance fetch fails
|
|
2576
4608
|
*/
|
|
2577
4609
|
async getUsdcBalance(chain, accountIndex = 0) {
|
|
4610
|
+
this.assertNotDisposed();
|
|
2578
4611
|
const usdcAddress = USDC_ADDRESSES[chain];
|
|
2579
4612
|
if (!usdcAddress) {
|
|
2580
4613
|
return 0n;
|
|
@@ -2586,7 +4619,7 @@ var T402WDK = class _T402WDK {
|
|
|
2586
4619
|
chain,
|
|
2587
4620
|
usdcAddress,
|
|
2588
4621
|
address,
|
|
2589
|
-
|
|
4622
|
+
() => this._withRetry(() => signer.getTokenBalance(usdcAddress))
|
|
2590
4623
|
);
|
|
2591
4624
|
} catch (error) {
|
|
2592
4625
|
if (isWDKError(error) && error.code === 5002 /* TOKEN_BALANCE_FETCH_FAILED */) {
|
|
@@ -2604,6 +4637,7 @@ var T402WDK = class _T402WDK {
|
|
|
2604
4637
|
* @throws {BalanceError} If balance fetch fails
|
|
2605
4638
|
*/
|
|
2606
4639
|
async getChainBalances(chain, accountIndex = 0) {
|
|
4640
|
+
this.assertNotDisposed();
|
|
2607
4641
|
const config = this._normalizedChains.get(chain);
|
|
2608
4642
|
if (!config) {
|
|
2609
4643
|
throw new ChainError(2001 /* CHAIN_NOT_CONFIGURED */, `Chain "${chain}" not configured`, {
|
|
@@ -2620,7 +4654,7 @@ var T402WDK = class _T402WDK {
|
|
|
2620
4654
|
chain,
|
|
2621
4655
|
token.address,
|
|
2622
4656
|
address,
|
|
2623
|
-
|
|
4657
|
+
() => this._withRetry(() => signer.getTokenBalance(token.address))
|
|
2624
4658
|
);
|
|
2625
4659
|
return {
|
|
2626
4660
|
token: token.address,
|
|
@@ -2649,7 +4683,7 @@ var T402WDK = class _T402WDK {
|
|
|
2649
4683
|
nativeBalance = await this._balanceCache.getOrFetchNativeBalance(
|
|
2650
4684
|
chain,
|
|
2651
4685
|
address,
|
|
2652
|
-
|
|
4686
|
+
() => this._withRetry(() => signer.getBalance())
|
|
2653
4687
|
);
|
|
2654
4688
|
} catch {
|
|
2655
4689
|
nativeBalance = 0n;
|
|
@@ -2742,6 +4776,16 @@ var T402WDK = class _T402WDK {
|
|
|
2742
4776
|
for (const chainBalance of balances.chains) {
|
|
2743
4777
|
const tokenBalance = chainBalance.tokens.find((t) => t.symbol === tokenSymbol);
|
|
2744
4778
|
if (tokenBalance && tokenBalance.balance >= amount) {
|
|
4779
|
+
try {
|
|
4780
|
+
const costEstimate = await this.estimatePaymentCost(
|
|
4781
|
+
chainBalance.chain,
|
|
4782
|
+
amount.toString()
|
|
4783
|
+
);
|
|
4784
|
+
if (!costEstimate.canAffordGas) {
|
|
4785
|
+
continue;
|
|
4786
|
+
}
|
|
4787
|
+
} catch {
|
|
4788
|
+
}
|
|
2745
4789
|
return {
|
|
2746
4790
|
chain: chainBalance.chain,
|
|
2747
4791
|
token: tokenSymbol,
|
|
@@ -2775,7 +4819,8 @@ var T402WDK = class _T402WDK {
|
|
|
2775
4819
|
* @returns Bridge result with transaction hash
|
|
2776
4820
|
*/
|
|
2777
4821
|
async bridgeUsdt0(params) {
|
|
2778
|
-
|
|
4822
|
+
this.assertNotDisposed();
|
|
4823
|
+
if (!this._bridgeUsdt0Evm) {
|
|
2779
4824
|
throw new BridgeError(
|
|
2780
4825
|
7001 /* BRIDGE_NOT_AVAILABLE */,
|
|
2781
4826
|
"USDT0 bridge not available. Register BridgeUsdt0Evm with T402WDK.registerWDK().",
|
|
@@ -2810,6 +4855,11 @@ var T402WDK = class _T402WDK {
|
|
|
2810
4855
|
}
|
|
2811
4856
|
try {
|
|
2812
4857
|
const recipient = params.recipient ?? await this.getAddress(params.toChain);
|
|
4858
|
+
this._events.emit("bridge:start", {
|
|
4859
|
+
fromChain: params.fromChain,
|
|
4860
|
+
toChain: params.toChain,
|
|
4861
|
+
amount: params.amount
|
|
4862
|
+
});
|
|
2813
4863
|
const result = await this.wdk.executeProtocol("bridge-usdt0", {
|
|
2814
4864
|
fromChain: params.fromChain,
|
|
2815
4865
|
toChain: params.toChain,
|
|
@@ -2823,6 +4873,11 @@ var T402WDK = class _T402WDK {
|
|
|
2823
4873
|
{ fromChain: params.fromChain, toChain: params.toChain }
|
|
2824
4874
|
);
|
|
2825
4875
|
}
|
|
4876
|
+
this._events.emit("bridge:confirmed", {
|
|
4877
|
+
txHash: result.txHash,
|
|
4878
|
+
fromChain: params.fromChain,
|
|
4879
|
+
toChain: params.toChain
|
|
4880
|
+
});
|
|
2826
4881
|
return {
|
|
2827
4882
|
txHash: result.txHash,
|
|
2828
4883
|
estimatedTime: 300
|
|
@@ -2878,7 +4933,7 @@ var T402WDK = class _T402WDK {
|
|
|
2878
4933
|
* Check if the Velora swap protocol is registered and available
|
|
2879
4934
|
*/
|
|
2880
4935
|
canSwap() {
|
|
2881
|
-
return
|
|
4936
|
+
return this._protocolModules.swapVeloraEvm !== void 0;
|
|
2882
4937
|
}
|
|
2883
4938
|
/**
|
|
2884
4939
|
* Get a swap quote for converting a token to USDT0
|
|
@@ -2985,6 +5040,131 @@ var T402WDK = class _T402WDK {
|
|
|
2985
5040
|
);
|
|
2986
5041
|
}
|
|
2987
5042
|
}
|
|
5043
|
+
// ========== Lending Protocol ==========
|
|
5044
|
+
/**
|
|
5045
|
+
* Check if the Aave lending protocol is registered and available
|
|
5046
|
+
*/
|
|
5047
|
+
canBorrow() {
|
|
5048
|
+
return this._protocolModules.lendingAaveEvm !== void 0;
|
|
5049
|
+
}
|
|
5050
|
+
/**
|
|
5051
|
+
* Borrow USDT0 against collateral and pay
|
|
5052
|
+
*
|
|
5053
|
+
* Uses the Aave protocol to deposit collateral, borrow USDT0, then the
|
|
5054
|
+
* borrowed USDT0 is available for T402 payments.
|
|
5055
|
+
*
|
|
5056
|
+
* @param params - Borrow parameters
|
|
5057
|
+
* @throws {WDKError} If lending protocol is not registered or borrow fails
|
|
5058
|
+
*
|
|
5059
|
+
* @example
|
|
5060
|
+
* ```typescript
|
|
5061
|
+
* // Borrow 100 USDT0 against 0.05 WETH on Arbitrum
|
|
5062
|
+
* const result = await wallet.borrowAndPay({
|
|
5063
|
+
* chain: 'arbitrum',
|
|
5064
|
+
* collateralToken: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', // WETH
|
|
5065
|
+
* collateralAmount: 50000000000000000n, // 0.05 WETH
|
|
5066
|
+
* borrowAmount: 100000000n, // 100 USDT0
|
|
5067
|
+
* });
|
|
5068
|
+
* ```
|
|
5069
|
+
*/
|
|
5070
|
+
async borrowAndPay(params) {
|
|
5071
|
+
if (!this.canBorrow()) {
|
|
5072
|
+
throw new WDKError(
|
|
5073
|
+
8101 /* PROTOCOL_NOT_REGISTERED */,
|
|
5074
|
+
"Aave lending protocol not registered. Call T402WDK.registerWDK(WDK, { protocols: { lendingAaveEvm: LendingAaveEvm } })."
|
|
5075
|
+
);
|
|
5076
|
+
}
|
|
5077
|
+
const usdt0Address = USDT0_ADDRESSES[params.chain];
|
|
5078
|
+
if (!usdt0Address) {
|
|
5079
|
+
throw new ChainError(
|
|
5080
|
+
2002 /* CHAIN_NOT_SUPPORTED */,
|
|
5081
|
+
`Chain "${params.chain}" does not have a known USDT0 address`,
|
|
5082
|
+
{ chain: params.chain }
|
|
5083
|
+
);
|
|
5084
|
+
}
|
|
5085
|
+
if (params.collateralAmount <= 0n) {
|
|
5086
|
+
throw new WDKError(8103 /* INVALID_PARAMETER */, "collateralAmount must be greater than 0");
|
|
5087
|
+
}
|
|
5088
|
+
if (params.borrowAmount <= 0n) {
|
|
5089
|
+
throw new WDKError(8103 /* INVALID_PARAMETER */, "borrowAmount must be greater than 0");
|
|
5090
|
+
}
|
|
5091
|
+
try {
|
|
5092
|
+
const result = await this.wdk.executeProtocol("lending-aave", {
|
|
5093
|
+
action: "borrow",
|
|
5094
|
+
chain: params.chain,
|
|
5095
|
+
collateralToken: params.collateralToken,
|
|
5096
|
+
collateralAmount: params.collateralAmount.toString(),
|
|
5097
|
+
borrowToken: usdt0Address,
|
|
5098
|
+
borrowAmount: params.borrowAmount.toString(),
|
|
5099
|
+
interestRateMode: params.interestRateMode ?? 2
|
|
5100
|
+
});
|
|
5101
|
+
this._balanceCache.invalidateChain(params.chain);
|
|
5102
|
+
const r = result;
|
|
5103
|
+
return {
|
|
5104
|
+
supplyTxHash: r.supplyTxHash,
|
|
5105
|
+
borrowTxHash: r.borrowTxHash,
|
|
5106
|
+
borrowedAmount: BigInt(r.borrowedAmount)
|
|
5107
|
+
};
|
|
5108
|
+
} catch (error) {
|
|
5109
|
+
throw wrapError(
|
|
5110
|
+
error,
|
|
5111
|
+
8102 /* PROTOCOL_EXECUTION_FAILED */,
|
|
5112
|
+
`Failed to execute borrow on ${params.chain}`,
|
|
5113
|
+
{
|
|
5114
|
+
chain: params.chain,
|
|
5115
|
+
collateralToken: params.collateralToken,
|
|
5116
|
+
borrowAmount: params.borrowAmount.toString()
|
|
5117
|
+
}
|
|
5118
|
+
);
|
|
5119
|
+
}
|
|
5120
|
+
}
|
|
5121
|
+
// ========== Fiat On-Ramp ==========
|
|
5122
|
+
/**
|
|
5123
|
+
* Get a fiat on-ramp quote
|
|
5124
|
+
*
|
|
5125
|
+
* @param params - Quote parameters (fiatAmount, fiatCurrency, network)
|
|
5126
|
+
* @throws {WDKError} If no fiat on-ramp provider is registered
|
|
5127
|
+
*/
|
|
5128
|
+
async getFiatOnRampQuote(params) {
|
|
5129
|
+
this.assertNotDisposed();
|
|
5130
|
+
if (!this._fiatOnRampProvider) {
|
|
5131
|
+
throw new WDKError(
|
|
5132
|
+
8101 /* PROTOCOL_NOT_REGISTERED */,
|
|
5133
|
+
"No fiat on-ramp provider registered. Call T402WDK.registerFiatOnRamp() first."
|
|
5134
|
+
);
|
|
5135
|
+
}
|
|
5136
|
+
return this._fiatOnRampProvider.getQuote(params);
|
|
5137
|
+
}
|
|
5138
|
+
/**
|
|
5139
|
+
* Generate a fiat on-ramp widget URL for the user
|
|
5140
|
+
*
|
|
5141
|
+
* Returns a widget URL that the application should open in a browser
|
|
5142
|
+
* or webview so the user can complete the fiat purchase.
|
|
5143
|
+
*
|
|
5144
|
+
* @param params - On-ramp parameters
|
|
5145
|
+
* @throws {WDKError} If no fiat on-ramp provider is registered
|
|
5146
|
+
*
|
|
5147
|
+
* @example
|
|
5148
|
+
* ```typescript
|
|
5149
|
+
* const result = await wallet.onRampAndPay({
|
|
5150
|
+
* fiatAmount: 100,
|
|
5151
|
+
* fiatCurrency: 'USD',
|
|
5152
|
+
* walletAddress: '0x...',
|
|
5153
|
+
* network: 'eip155:42161',
|
|
5154
|
+
* });
|
|
5155
|
+
* // Open result.widgetUrl in browser/webview
|
|
5156
|
+
* ```
|
|
5157
|
+
*/
|
|
5158
|
+
onRampAndPay(params) {
|
|
5159
|
+
this.assertNotDisposed();
|
|
5160
|
+
if (!this._fiatOnRampProvider) {
|
|
5161
|
+
throw new WDKError(
|
|
5162
|
+
8101 /* PROTOCOL_NOT_REGISTERED */,
|
|
5163
|
+
"No fiat on-ramp provider registered. Call T402WDK.registerFiatOnRamp() first."
|
|
5164
|
+
);
|
|
5165
|
+
}
|
|
5166
|
+
return this._fiatOnRampProvider.createWidget(params);
|
|
5167
|
+
}
|
|
2988
5168
|
// ========== Cache Management ==========
|
|
2989
5169
|
/**
|
|
2990
5170
|
* Check if balance caching is enabled
|
|
@@ -3011,6 +5191,14 @@ var T402WDK = class _T402WDK {
|
|
|
3011
5191
|
*/
|
|
3012
5192
|
invalidateBalanceCache() {
|
|
3013
5193
|
this._balanceCache.clear();
|
|
5194
|
+
for (const chain of this.getConfiguredChains()) {
|
|
5195
|
+
this._events.emit("balance:changed", {
|
|
5196
|
+
chain,
|
|
5197
|
+
token: "*",
|
|
5198
|
+
previousBalance: 0n,
|
|
5199
|
+
newBalance: 0n
|
|
5200
|
+
});
|
|
5201
|
+
}
|
|
3014
5202
|
}
|
|
3015
5203
|
/**
|
|
3016
5204
|
* Invalidate cached balances for a specific chain
|
|
@@ -3019,7 +5207,16 @@ var T402WDK = class _T402WDK {
|
|
|
3019
5207
|
* @returns Number of cache entries invalidated
|
|
3020
5208
|
*/
|
|
3021
5209
|
invalidateChainCache(chain) {
|
|
3022
|
-
|
|
5210
|
+
const count = this._balanceCache.invalidateChain(chain);
|
|
5211
|
+
if (count > 0) {
|
|
5212
|
+
this._events.emit("balance:changed", {
|
|
5213
|
+
chain,
|
|
5214
|
+
token: "*",
|
|
5215
|
+
previousBalance: 0n,
|
|
5216
|
+
newBalance: 0n
|
|
5217
|
+
});
|
|
5218
|
+
}
|
|
5219
|
+
return count;
|
|
3023
5220
|
}
|
|
3024
5221
|
/**
|
|
3025
5222
|
* Invalidate cached balances for a specific address
|
|
@@ -3031,16 +5228,92 @@ var T402WDK = class _T402WDK {
|
|
|
3031
5228
|
return this._balanceCache.invalidateAddress(address);
|
|
3032
5229
|
}
|
|
3033
5230
|
/**
|
|
3034
|
-
* Dispose of
|
|
5231
|
+
* Dispose of all resources held by this instance (#194).
|
|
3035
5232
|
*
|
|
3036
|
-
*
|
|
5233
|
+
* After disposal, any public method call will throw.
|
|
5234
|
+
* Safe to call multiple times.
|
|
3037
5235
|
*/
|
|
3038
5236
|
dispose() {
|
|
3039
|
-
this.
|
|
5237
|
+
if (this._disposed) return;
|
|
5238
|
+
this._disposed = true;
|
|
5239
|
+
if (this._wdk && typeof this._wdk.dispose === "function") {
|
|
5240
|
+
try {
|
|
5241
|
+
;
|
|
5242
|
+
this._wdk.dispose();
|
|
5243
|
+
} catch {
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
this._wdk = null;
|
|
3040
5247
|
this._signerCache.clear();
|
|
5248
|
+
this._pathSignerCache.clear();
|
|
3041
5249
|
this._tonSignerCache.clear();
|
|
3042
5250
|
this._svmSignerCache.clear();
|
|
3043
5251
|
this._tronSignerCache.clear();
|
|
5252
|
+
this._sparkSignerCache.clear();
|
|
5253
|
+
this._btcSignerCache.clear();
|
|
5254
|
+
this._seedPhrase = "";
|
|
5255
|
+
this._balanceCache.dispose();
|
|
5256
|
+
for (const provider of this._failoverProviders.values()) {
|
|
5257
|
+
provider.dispose();
|
|
5258
|
+
}
|
|
5259
|
+
this._failoverProviders.clear();
|
|
5260
|
+
}
|
|
5261
|
+
/**
|
|
5262
|
+
* Symbol.dispose support for `using` declarations (TC39 Explicit Resource Management)
|
|
5263
|
+
*/
|
|
5264
|
+
[Symbol.dispose]() {
|
|
5265
|
+
this.dispose();
|
|
5266
|
+
}
|
|
5267
|
+
// ========== Failover Provider Status (#195) ==========
|
|
5268
|
+
/**
|
|
5269
|
+
* Get the FailoverProvider status for a chain, if one exists.
|
|
5270
|
+
*
|
|
5271
|
+
* @param chain - Chain name
|
|
5272
|
+
* @returns Array of provider statuses, or null if no failover is configured for the chain
|
|
5273
|
+
*/
|
|
5274
|
+
getProviderStatus(chain) {
|
|
5275
|
+
const provider = this._failoverProviders.get(chain);
|
|
5276
|
+
if (!provider) return null;
|
|
5277
|
+
return provider.getStatus();
|
|
5278
|
+
}
|
|
5279
|
+
// ========== Network Resilience (#202) ==========
|
|
5280
|
+
/**
|
|
5281
|
+
* Simple online connectivity check.
|
|
5282
|
+
* Returns true if at least one configured chain's RPC responds.
|
|
5283
|
+
*/
|
|
5284
|
+
get isOnline() {
|
|
5285
|
+
return this._checkOnline();
|
|
5286
|
+
}
|
|
5287
|
+
async _checkOnline() {
|
|
5288
|
+
const chains = this.getConfiguredChains();
|
|
5289
|
+
if (chains.length === 0) return false;
|
|
5290
|
+
for (const chain of chains) {
|
|
5291
|
+
const config = this._normalizedChains.get(chain);
|
|
5292
|
+
if (!config) continue;
|
|
5293
|
+
try {
|
|
5294
|
+
const controller = new AbortController();
|
|
5295
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
5296
|
+
const response = await fetch(config.provider, {
|
|
5297
|
+
method: "POST",
|
|
5298
|
+
headers: { "Content-Type": "application/json" },
|
|
5299
|
+
body: JSON.stringify({ jsonrpc: "2.0", method: "eth_chainId", params: [], id: 1 }),
|
|
5300
|
+
signal: controller.signal
|
|
5301
|
+
});
|
|
5302
|
+
clearTimeout(timeout);
|
|
5303
|
+
if (response.ok) return true;
|
|
5304
|
+
} catch {
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5307
|
+
return false;
|
|
5308
|
+
}
|
|
5309
|
+
/**
|
|
5310
|
+
* Wrap an async operation with the instance retry config (#202)
|
|
5311
|
+
*/
|
|
5312
|
+
async _withRetry(fn) {
|
|
5313
|
+
if (this._retryConfig) {
|
|
5314
|
+
return withRetry(fn, this._retryConfig);
|
|
5315
|
+
}
|
|
5316
|
+
return fn();
|
|
3044
5317
|
}
|
|
3045
5318
|
};
|
|
3046
5319
|
function formatTokenAmount(amount, decimals) {
|
|
@@ -3058,158 +5331,1136 @@ function formatTokenAmount(amount, decimals) {
|
|
|
3058
5331
|
return `${whole}.${trimmed}`;
|
|
3059
5332
|
}
|
|
3060
5333
|
|
|
3061
|
-
// src/
|
|
3062
|
-
var
|
|
3063
|
-
var
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
5334
|
+
// src/validation.ts
|
|
5335
|
+
var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
5336
|
+
var TON_RAW_RE = /^0:[0-9a-fA-F]{64}$/;
|
|
5337
|
+
var TRON_ADDRESS_RE = /^T[1-9A-HJ-NP-Za-km-z]{33}$/;
|
|
5338
|
+
var BASE58_CHARS = /^[1-9A-HJ-NP-Za-km-z]+$/;
|
|
5339
|
+
var BECH32_BTC_RE = /^bc1[a-zA-HJ-NP-Z0-9]{25,90}$/;
|
|
5340
|
+
var COSMOS_BECH32_RE = /^[a-z]+1[a-z0-9]{38,58}$/;
|
|
5341
|
+
function isBase58(s) {
|
|
5342
|
+
return BASE58_CHARS.test(s);
|
|
5343
|
+
}
|
|
5344
|
+
function validateEvm(address) {
|
|
5345
|
+
if (!EVM_ADDRESS_RE.test(address)) {
|
|
5346
|
+
const detected = detectFamily(address, "evm");
|
|
5347
|
+
if (detected) {
|
|
5348
|
+
return {
|
|
5349
|
+
valid: false,
|
|
5350
|
+
error: `Address appears to be a ${detected} address, not an EVM address`,
|
|
5351
|
+
detectedFamily: detected
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
3069
5354
|
return {
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
throw new Error(
|
|
3073
|
-
"readContract not available on WDKSigner. Use T402WDK.bridgeUsdt0() instead."
|
|
3074
|
-
);
|
|
3075
|
-
},
|
|
3076
|
-
writeContract: async (_args) => {
|
|
3077
|
-
throw new Error(
|
|
3078
|
-
"writeContract not available on WDKSigner. Use T402WDK.bridgeUsdt0() instead."
|
|
3079
|
-
);
|
|
3080
|
-
},
|
|
3081
|
-
waitForTransactionReceipt: async (_args) => {
|
|
3082
|
-
throw new Error(
|
|
3083
|
-
"waitForTransactionReceipt not available on WDKSigner. Use T402WDK.bridgeUsdt0() instead."
|
|
3084
|
-
);
|
|
3085
|
-
}
|
|
5355
|
+
valid: false,
|
|
5356
|
+
error: "Invalid EVM address: must be 0x-prefixed, 40 hex characters"
|
|
3086
5357
|
};
|
|
3087
5358
|
}
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
5359
|
+
return {
|
|
5360
|
+
valid: true,
|
|
5361
|
+
normalized: address.toLowerCase()
|
|
5362
|
+
};
|
|
5363
|
+
}
|
|
5364
|
+
function validateTon(address) {
|
|
5365
|
+
if (TON_RAW_RE.test(address)) {
|
|
5366
|
+
return { valid: true, normalized: address.toLowerCase() };
|
|
5367
|
+
}
|
|
5368
|
+
const base64UrlRe = /^[A-Za-z0-9_\-+/=]{44,48}$/;
|
|
5369
|
+
if (base64UrlRe.test(address)) {
|
|
5370
|
+
return { valid: true, normalized: address };
|
|
5371
|
+
}
|
|
5372
|
+
const detected = detectFamily(address, "ton");
|
|
5373
|
+
if (detected) {
|
|
5374
|
+
return {
|
|
5375
|
+
valid: false,
|
|
5376
|
+
error: `Address appears to be a ${detected} address, not a TON address`,
|
|
5377
|
+
detectedFamily: detected
|
|
5378
|
+
};
|
|
5379
|
+
}
|
|
5380
|
+
return {
|
|
5381
|
+
valid: false,
|
|
5382
|
+
error: "Invalid TON address: must be raw format (0:<64 hex>) or user-friendly (48 chars base64)"
|
|
5383
|
+
};
|
|
5384
|
+
}
|
|
5385
|
+
function validateTron(address) {
|
|
5386
|
+
if (!TRON_ADDRESS_RE.test(address)) {
|
|
5387
|
+
const detected = detectFamily(address, "tron");
|
|
5388
|
+
if (detected) {
|
|
5389
|
+
return {
|
|
5390
|
+
valid: false,
|
|
5391
|
+
error: `Address appears to be a ${detected} address, not a TRON address`,
|
|
5392
|
+
detectedFamily: detected
|
|
5393
|
+
};
|
|
3095
5394
|
}
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
5395
|
+
return {
|
|
5396
|
+
valid: false,
|
|
5397
|
+
error: "Invalid TRON address: must be T-prefixed base58check, 34 characters"
|
|
5398
|
+
};
|
|
5399
|
+
}
|
|
5400
|
+
return { valid: true, normalized: address };
|
|
5401
|
+
}
|
|
5402
|
+
function validateSvm(address) {
|
|
5403
|
+
if (TRON_ADDRESS_RE.test(address)) {
|
|
5404
|
+
return {
|
|
5405
|
+
valid: false,
|
|
5406
|
+
error: "Address appears to be a tron address, not a Solana address",
|
|
5407
|
+
detectedFamily: "tron"
|
|
5408
|
+
};
|
|
5409
|
+
}
|
|
5410
|
+
if (!isBase58(address) || address.length < 32 || address.length > 44) {
|
|
5411
|
+
const detected = detectFamily(address, "svm");
|
|
5412
|
+
if (detected) {
|
|
5413
|
+
return {
|
|
5414
|
+
valid: false,
|
|
5415
|
+
error: `Address appears to be a ${detected} address, not a Solana address`,
|
|
5416
|
+
detectedFamily: detected
|
|
5417
|
+
};
|
|
5418
|
+
}
|
|
5419
|
+
return {
|
|
5420
|
+
valid: false,
|
|
5421
|
+
error: "Invalid Solana address: must be base58, 32-44 characters"
|
|
5422
|
+
};
|
|
5423
|
+
}
|
|
5424
|
+
return { valid: true, normalized: address };
|
|
5425
|
+
}
|
|
5426
|
+
function validateBtc(address) {
|
|
5427
|
+
if (BECH32_BTC_RE.test(address)) {
|
|
5428
|
+
return { valid: true, normalized: address.toLowerCase() };
|
|
5429
|
+
}
|
|
5430
|
+
if ((address.startsWith("1") || address.startsWith("3")) && isBase58(address)) {
|
|
5431
|
+
if (address.length >= 25 && address.length <= 34) {
|
|
5432
|
+
return { valid: true, normalized: address };
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
const detected = detectFamily(address, "btc");
|
|
5436
|
+
if (detected) {
|
|
5437
|
+
return {
|
|
5438
|
+
valid: false,
|
|
5439
|
+
error: `Address appears to be a ${detected} address, not a Bitcoin address`,
|
|
5440
|
+
detectedFamily: detected
|
|
5441
|
+
};
|
|
5442
|
+
}
|
|
5443
|
+
return {
|
|
5444
|
+
valid: false,
|
|
5445
|
+
error: "Invalid Bitcoin address: must be bech32 (bc1...) or base58 (1... or 3...), 25-90 characters"
|
|
5446
|
+
};
|
|
5447
|
+
}
|
|
5448
|
+
function validateCosmos(address) {
|
|
5449
|
+
if (!COSMOS_BECH32_RE.test(address)) {
|
|
5450
|
+
const detected = detectFamily(address, "cosmos");
|
|
5451
|
+
if (detected) {
|
|
5452
|
+
return {
|
|
5453
|
+
valid: false,
|
|
5454
|
+
error: `Address appears to be a ${detected} address, not a Cosmos address`,
|
|
5455
|
+
detectedFamily: detected
|
|
5456
|
+
};
|
|
5457
|
+
}
|
|
5458
|
+
return {
|
|
5459
|
+
valid: false,
|
|
5460
|
+
error: "Invalid Cosmos address: must be bech32 with lowercase prefix"
|
|
5461
|
+
};
|
|
5462
|
+
}
|
|
5463
|
+
return { valid: true, normalized: address };
|
|
5464
|
+
}
|
|
5465
|
+
function detectFamily(address, exclude) {
|
|
5466
|
+
if (exclude !== "evm" && EVM_ADDRESS_RE.test(address)) return "evm";
|
|
5467
|
+
if (exclude !== "tron" && TRON_ADDRESS_RE.test(address)) return "tron";
|
|
5468
|
+
if (exclude !== "ton" && (TON_RAW_RE.test(address) || /^[A-Za-z0-9_\-+/=]{44,48}$/.test(address)))
|
|
5469
|
+
return "ton";
|
|
5470
|
+
if (exclude !== "btc" && (BECH32_BTC_RE.test(address) || address.startsWith("1") && isBase58(address) && address.length >= 25 && address.length <= 34))
|
|
5471
|
+
return "btc";
|
|
5472
|
+
if (exclude !== "svm" && exclude !== "tron" && isBase58(address) && address.length >= 32 && address.length <= 44 && !address.startsWith("T"))
|
|
5473
|
+
return "svm";
|
|
5474
|
+
return void 0;
|
|
5475
|
+
}
|
|
5476
|
+
var VALIDATORS = {
|
|
5477
|
+
evm: validateEvm,
|
|
5478
|
+
ton: validateTon,
|
|
5479
|
+
tron: validateTron,
|
|
5480
|
+
svm: validateSvm,
|
|
5481
|
+
btc: validateBtc,
|
|
5482
|
+
spark: validateBtc,
|
|
5483
|
+
// Spark uses Bitcoin addresses
|
|
5484
|
+
cosmos: validateCosmos
|
|
5485
|
+
};
|
|
5486
|
+
function validatePaymentAddress(address, family) {
|
|
5487
|
+
if (!address || typeof address !== "string") {
|
|
5488
|
+
return { valid: false, error: "Address is required" };
|
|
5489
|
+
}
|
|
5490
|
+
const trimmed = address.trim();
|
|
5491
|
+
if (trimmed.length === 0) {
|
|
5492
|
+
return { valid: false, error: "Address is required" };
|
|
5493
|
+
}
|
|
5494
|
+
const validator = VALIDATORS[family];
|
|
5495
|
+
if (!validator) {
|
|
5496
|
+
return { valid: false, error: `Unsupported chain family: ${family}` };
|
|
5497
|
+
}
|
|
5498
|
+
return validator(trimmed);
|
|
5499
|
+
}
|
|
5500
|
+
|
|
5501
|
+
// src/bridge.ts
|
|
5502
|
+
var import_evm2 = require("@t402/evm");
|
|
5503
|
+
|
|
5504
|
+
// src/bridge-tracker.ts
|
|
5505
|
+
var LAYERZERO_SCAN_API = "https://scan.layerzero-api.com/v1";
|
|
5506
|
+
var BridgeTracker = class {
|
|
5507
|
+
apiBaseUrl;
|
|
5508
|
+
constructor(config) {
|
|
5509
|
+
this.apiBaseUrl = config?.apiBaseUrl ?? LAYERZERO_SCAN_API;
|
|
5510
|
+
}
|
|
5511
|
+
/** Get current status of a bridge message */
|
|
5512
|
+
async getStatus(txHash) {
|
|
5513
|
+
const response = await fetch(`${this.apiBaseUrl}/messages/tx/${txHash}`);
|
|
5514
|
+
if (!response.ok) {
|
|
5515
|
+
if (response.status === 404) return { status: "INFLIGHT" };
|
|
5516
|
+
throw new Error(`LayerZero API error: ${response.status}`);
|
|
5517
|
+
}
|
|
5518
|
+
const data = await response.json();
|
|
5519
|
+
return mapLayerZeroStatus(data);
|
|
5520
|
+
}
|
|
5521
|
+
/** Wait for delivery with polling */
|
|
5522
|
+
async waitForDelivery(txHash, options) {
|
|
5523
|
+
const timeout = options?.timeout ?? 6e5;
|
|
5524
|
+
const pollInterval = options?.pollInterval ?? 1e4;
|
|
5525
|
+
const startTime = Date.now();
|
|
5526
|
+
while (Date.now() - startTime < timeout) {
|
|
5527
|
+
const { status, dstTxHash } = await this.getStatus(txHash);
|
|
5528
|
+
options?.onStatusChange?.(status);
|
|
5529
|
+
if (status === "DELIVERED") {
|
|
5530
|
+
return {
|
|
5531
|
+
success: true,
|
|
5532
|
+
status,
|
|
5533
|
+
dstTxHash,
|
|
5534
|
+
srcTxHash: txHash,
|
|
5535
|
+
messageGuid: txHash
|
|
5536
|
+
};
|
|
5537
|
+
}
|
|
5538
|
+
if (status === "FAILED" || status === "BLOCKED") {
|
|
5539
|
+
return {
|
|
5540
|
+
success: false,
|
|
5541
|
+
status,
|
|
5542
|
+
srcTxHash: txHash,
|
|
5543
|
+
messageGuid: txHash,
|
|
5544
|
+
error: `Bridge ${status.toLowerCase()}`
|
|
5545
|
+
};
|
|
5546
|
+
}
|
|
5547
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
5548
|
+
}
|
|
5549
|
+
return {
|
|
5550
|
+
success: false,
|
|
5551
|
+
status: "INFLIGHT",
|
|
5552
|
+
srcTxHash: txHash,
|
|
5553
|
+
messageGuid: txHash,
|
|
5554
|
+
error: "Timeout waiting for delivery"
|
|
5555
|
+
};
|
|
5556
|
+
}
|
|
5557
|
+
};
|
|
5558
|
+
function mapLayerZeroStatus(data) {
|
|
5559
|
+
const obj = data;
|
|
5560
|
+
if (!obj) return { status: "INFLIGHT" };
|
|
5561
|
+
const messages = obj.messages ?? obj.data ?? [];
|
|
5562
|
+
const msg = Array.isArray(messages) ? messages[0] : messages;
|
|
5563
|
+
if (!msg) return { status: "INFLIGHT" };
|
|
5564
|
+
const lzStatus = (msg.status ?? msg.msgStatus ?? "").toUpperCase();
|
|
5565
|
+
if (lzStatus === "DELIVERED" || lzStatus === "DESTINATION_FINALIZED") {
|
|
5566
|
+
return { status: "DELIVERED", dstTxHash: msg.dstTxHash };
|
|
5567
|
+
}
|
|
5568
|
+
if (lzStatus === "FAILED") return { status: "FAILED" };
|
|
5569
|
+
if (lzStatus === "BLOCKED") return { status: "BLOCKED" };
|
|
5570
|
+
if (lzStatus.includes("CONFIRM")) return { status: "CONFIRMING" };
|
|
5571
|
+
return { status: "INFLIGHT" };
|
|
5572
|
+
}
|
|
5573
|
+
|
|
5574
|
+
// src/bridge.ts
|
|
5575
|
+
var jsonRpcId = 1;
|
|
5576
|
+
async function jsonRpcCall(rpcUrl, method, params) {
|
|
5577
|
+
const id = jsonRpcId++;
|
|
5578
|
+
const body = JSON.stringify({ jsonrpc: "2.0", id, method, params });
|
|
5579
|
+
const res = await fetch(rpcUrl, {
|
|
5580
|
+
method: "POST",
|
|
5581
|
+
headers: { "Content-Type": "application/json" },
|
|
5582
|
+
body
|
|
5583
|
+
});
|
|
5584
|
+
if (!res.ok) {
|
|
5585
|
+
throw new Error(`JSON-RPC request failed: ${res.status} ${res.statusText}`);
|
|
5586
|
+
}
|
|
5587
|
+
const json = await res.json();
|
|
5588
|
+
if (json.error) {
|
|
5589
|
+
throw new Error(`JSON-RPC error ${json.error.code}: ${json.error.message}`);
|
|
5590
|
+
}
|
|
5591
|
+
return json.result;
|
|
5592
|
+
}
|
|
5593
|
+
var KNOWN_SELECTORS = {
|
|
5594
|
+
// ERC-20
|
|
5595
|
+
balanceOf: "0x70a08231",
|
|
5596
|
+
allowance: "0xdd62ed3e",
|
|
5597
|
+
approve: "0x095ea7b3",
|
|
5598
|
+
transfer: "0xa9059cbb",
|
|
5599
|
+
// OFT / LayerZero
|
|
5600
|
+
quoteSend: "0x0d35b415",
|
|
5601
|
+
send: "0xc7c7f5b3"
|
|
5602
|
+
};
|
|
5603
|
+
function encodeUint256(value) {
|
|
5604
|
+
return value.toString(16).padStart(64, "0");
|
|
5605
|
+
}
|
|
5606
|
+
function encodeAddress(addr) {
|
|
5607
|
+
const clean = addr.startsWith("0x") ? addr.slice(2) : addr;
|
|
5608
|
+
return clean.toLowerCase().padStart(64, "0");
|
|
5609
|
+
}
|
|
5610
|
+
function encodeFunctionCall(args) {
|
|
5611
|
+
const selector = KNOWN_SELECTORS[args.functionName];
|
|
5612
|
+
if (!selector) {
|
|
5613
|
+
throw new Error(`Unknown function: ${args.functionName}. Cannot encode without full ABI codec.`);
|
|
5614
|
+
}
|
|
5615
|
+
const fnArgs = args.args ?? [];
|
|
5616
|
+
let encoded = selector;
|
|
5617
|
+
for (const arg of fnArgs) {
|
|
5618
|
+
if (typeof arg === "bigint") {
|
|
5619
|
+
encoded += encodeUint256(arg);
|
|
5620
|
+
} else if (typeof arg === "string" && arg.startsWith("0x") && arg.length === 42) {
|
|
5621
|
+
encoded += encodeAddress(arg);
|
|
5622
|
+
} else if (typeof arg === "string" && arg.startsWith("0x")) {
|
|
5623
|
+
const clean = arg.slice(2);
|
|
5624
|
+
encoded += clean.padStart(64, "0");
|
|
5625
|
+
} else if (typeof arg === "number") {
|
|
5626
|
+
encoded += BigInt(arg).toString(16).padStart(64, "0");
|
|
5627
|
+
} else if (typeof arg === "object" && arg !== null) {
|
|
5628
|
+
const obj = arg;
|
|
5629
|
+
for (const val of Object.values(obj)) {
|
|
5630
|
+
if (typeof val === "bigint") {
|
|
5631
|
+
encoded += encodeUint256(val);
|
|
5632
|
+
} else if (typeof val === "string" && val.startsWith("0x")) {
|
|
5633
|
+
const clean = val.slice(2);
|
|
5634
|
+
encoded += clean.padStart(64, "0");
|
|
5635
|
+
} else if (typeof val === "number") {
|
|
5636
|
+
encoded += BigInt(val).toString(16).padStart(64, "0");
|
|
5637
|
+
}
|
|
5638
|
+
}
|
|
5639
|
+
} else {
|
|
5640
|
+
throw new Error(`Cannot encode argument of type ${typeof arg}`);
|
|
5641
|
+
}
|
|
5642
|
+
}
|
|
5643
|
+
return encoded;
|
|
5644
|
+
}
|
|
5645
|
+
function decodeFunctionResult(args, hex) {
|
|
5646
|
+
if (typeof hex !== "string") return hex;
|
|
5647
|
+
const data = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
5648
|
+
if (data.length === 0) return void 0;
|
|
5649
|
+
const word = data.slice(0, 64);
|
|
5650
|
+
const fnName = args.functionName;
|
|
5651
|
+
if (["balanceOf", "allowance", "totalSupply", "decimals", "nonces"].includes(fnName)) {
|
|
5652
|
+
return BigInt("0x" + word);
|
|
5653
|
+
}
|
|
5654
|
+
if (["approve", "transfer", "transferFrom"].includes(fnName)) {
|
|
5655
|
+
return BigInt("0x" + word) !== 0n;
|
|
5656
|
+
}
|
|
5657
|
+
return hex;
|
|
5658
|
+
}
|
|
5659
|
+
async function pollForReceipt(rpcUrl, hash, timeout) {
|
|
5660
|
+
const pollInterval = 2e3;
|
|
5661
|
+
const deadline = Date.now() + timeout;
|
|
5662
|
+
while (Date.now() < deadline) {
|
|
5663
|
+
const result = await jsonRpcCall(rpcUrl, "eth_getTransactionReceipt", [hash]);
|
|
5664
|
+
if (result) {
|
|
5665
|
+
return {
|
|
5666
|
+
status: result.status === "0x1" ? "success" : "reverted",
|
|
5667
|
+
transactionHash: result.transactionHash,
|
|
5668
|
+
logs: (result.logs ?? []).map((log) => ({
|
|
5669
|
+
address: log.address,
|
|
5670
|
+
topics: log.topics,
|
|
5671
|
+
data: log.data
|
|
5672
|
+
}))
|
|
5673
|
+
};
|
|
5674
|
+
}
|
|
5675
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
5676
|
+
}
|
|
5677
|
+
throw new Error(`Transaction receipt not found after ${timeout}ms: ${hash}`);
|
|
5678
|
+
}
|
|
5679
|
+
var WdkBridge = class {
|
|
5680
|
+
bridges = /* @__PURE__ */ new Map();
|
|
5681
|
+
tracker;
|
|
5682
|
+
constructor(trackerConfig) {
|
|
5683
|
+
this.tracker = new BridgeTracker(trackerConfig);
|
|
5684
|
+
}
|
|
5685
|
+
/**
|
|
5686
|
+
* Create bridge signer adapter from WDK signer.
|
|
5687
|
+
*
|
|
5688
|
+
* Uses JSON-RPC calls for readContract / waitForTransactionReceipt,
|
|
5689
|
+
* and delegates writeContract to the WDK signer's sendTransaction.
|
|
5690
|
+
*/
|
|
5691
|
+
createBridgeSigner(signer, rpcUrl) {
|
|
5692
|
+
return {
|
|
5693
|
+
address: signer.address,
|
|
5694
|
+
readContract: async (args) => {
|
|
5695
|
+
const data = encodeFunctionCall(args);
|
|
5696
|
+
const result = await jsonRpcCall(rpcUrl, "eth_call", [{ to: args.address, data }, "latest"]);
|
|
5697
|
+
return decodeFunctionResult(args, result);
|
|
5698
|
+
},
|
|
5699
|
+
writeContract: async (args) => {
|
|
5700
|
+
const data = encodeFunctionCall(args);
|
|
5701
|
+
const { hash } = await signer.sendTransaction({
|
|
5702
|
+
to: args.address,
|
|
5703
|
+
data,
|
|
5704
|
+
value: args.value
|
|
5705
|
+
});
|
|
5706
|
+
return hash;
|
|
5707
|
+
},
|
|
5708
|
+
waitForTransactionReceipt: async (args) => {
|
|
5709
|
+
return pollForReceipt(rpcUrl, args.hash, 6e4);
|
|
5710
|
+
}
|
|
5711
|
+
};
|
|
5712
|
+
}
|
|
5713
|
+
/**
|
|
5714
|
+
* Get or create a bridge instance for a chain
|
|
5715
|
+
*
|
|
5716
|
+
* @param chain - Chain name (e.g., "arbitrum", "ethereum")
|
|
5717
|
+
* @param signer - WDK signer for the chain
|
|
5718
|
+
* @param rpcUrl - JSON-RPC endpoint URL for the chain
|
|
5719
|
+
*/
|
|
5720
|
+
getBridge(chain, signer, rpcUrl) {
|
|
5721
|
+
const cached = this.bridges.get(chain);
|
|
5722
|
+
if (cached) {
|
|
5723
|
+
return cached;
|
|
5724
|
+
}
|
|
5725
|
+
const bridgeSigner = this.createBridgeSigner(signer, rpcUrl);
|
|
5726
|
+
const bridge = new import_evm2.Usdt0Bridge(bridgeSigner, chain);
|
|
5727
|
+
this.bridges.set(chain, bridge);
|
|
5728
|
+
return bridge;
|
|
5729
|
+
}
|
|
5730
|
+
/**
|
|
5731
|
+
* Check if a chain supports USDT0 bridging
|
|
5732
|
+
*/
|
|
5733
|
+
static supportsBridging(chain) {
|
|
5734
|
+
return (0, import_evm2.supportsBridging)(chain);
|
|
5735
|
+
}
|
|
5736
|
+
/**
|
|
5737
|
+
* Get all chains that support USDT0 bridging
|
|
5738
|
+
*/
|
|
5739
|
+
static getBridgeableChains() {
|
|
5740
|
+
return (0, import_evm2.getBridgeableChains)();
|
|
5741
|
+
}
|
|
5742
|
+
/**
|
|
5743
|
+
* Get supported destinations from a source chain
|
|
5744
|
+
*/
|
|
5745
|
+
static getSupportedDestinations(fromChain) {
|
|
5746
|
+
return (0, import_evm2.getBridgeableChains)().filter((chain) => chain !== fromChain);
|
|
5747
|
+
}
|
|
5748
|
+
};
|
|
5749
|
+
function createDirectBridge(signer, chain) {
|
|
5750
|
+
return new import_evm2.Usdt0Bridge(signer, chain);
|
|
5751
|
+
}
|
|
5752
|
+
|
|
5753
|
+
// src/logger.ts
|
|
5754
|
+
var defaultLogger = {
|
|
5755
|
+
debug(msg, ctx) {
|
|
5756
|
+
if (ctx && Object.keys(ctx).length > 0) {
|
|
5757
|
+
console.debug(`[t402] ${msg}`, ctx);
|
|
5758
|
+
} else {
|
|
5759
|
+
console.debug(`[t402] ${msg}`);
|
|
5760
|
+
}
|
|
5761
|
+
},
|
|
5762
|
+
info(msg, ctx) {
|
|
5763
|
+
if (ctx && Object.keys(ctx).length > 0) {
|
|
5764
|
+
console.info(`[t402] ${msg}`, ctx);
|
|
5765
|
+
} else {
|
|
5766
|
+
console.info(`[t402] ${msg}`);
|
|
5767
|
+
}
|
|
5768
|
+
},
|
|
5769
|
+
warn(msg, ctx) {
|
|
5770
|
+
if (ctx && Object.keys(ctx).length > 0) {
|
|
5771
|
+
console.warn(`[t402] ${msg}`, ctx);
|
|
5772
|
+
} else {
|
|
5773
|
+
console.warn(`[t402] ${msg}`);
|
|
5774
|
+
}
|
|
5775
|
+
},
|
|
5776
|
+
error(msg, ctx) {
|
|
5777
|
+
if (ctx && Object.keys(ctx).length > 0) {
|
|
5778
|
+
console.error(`[t402] ${msg}`, ctx);
|
|
5779
|
+
} else {
|
|
5780
|
+
console.error(`[t402] ${msg}`);
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5784
|
+
var noopLogger = {
|
|
5785
|
+
debug() {
|
|
5786
|
+
},
|
|
5787
|
+
info() {
|
|
5788
|
+
},
|
|
5789
|
+
warn() {
|
|
5790
|
+
},
|
|
5791
|
+
error() {
|
|
5792
|
+
}
|
|
5793
|
+
};
|
|
5794
|
+
function createCorrelationId() {
|
|
5795
|
+
const bytes = new Uint8Array(8);
|
|
5796
|
+
if (typeof globalThis.crypto?.getRandomValues === "function") {
|
|
5797
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
5798
|
+
} else {
|
|
5799
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
5800
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
5801
|
+
}
|
|
5802
|
+
}
|
|
5803
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
5804
|
+
}
|
|
5805
|
+
|
|
5806
|
+
// src/index.ts
|
|
5807
|
+
var import_evm3 = require("@t402/evm");
|
|
5808
|
+
|
|
5809
|
+
// src/compatibility.ts
|
|
5810
|
+
var WDK_COMPATIBILITY = {
|
|
5811
|
+
/** Minimum supported @tetherto/wdk version */
|
|
5812
|
+
minVersion: "1.0.0-beta.0",
|
|
5813
|
+
/** Versions that have been tested */
|
|
5814
|
+
testedVersions: ["1.0.0-beta.3", "1.0.0-beta.4", "1.0.0-beta.5"],
|
|
5815
|
+
/** Tested wallet-evm module versions */
|
|
5816
|
+
walletEvmVersions: ["1.0.0-beta.5", "2.0.0-rc.1"],
|
|
5817
|
+
/** Feature availability by @tetherto/wdk core version */
|
|
5818
|
+
features: {
|
|
5819
|
+
signTypedData: "1.0.0-beta.0",
|
|
5820
|
+
estimateGas: "1.0.0-beta.3",
|
|
5821
|
+
multiChainWallets: "1.0.0-beta.0",
|
|
5822
|
+
bridgeProtocol: "1.0.0-beta.3",
|
|
5823
|
+
swapProtocol: "1.0.0-beta.4"
|
|
5824
|
+
},
|
|
5825
|
+
/** Known wallet module minimum versions */
|
|
5826
|
+
walletModuleVersions: {
|
|
5827
|
+
evm: "1.0.0-beta.5",
|
|
5828
|
+
ton: "1.0.0-beta.7",
|
|
5829
|
+
btc: "1.0.0-beta.5",
|
|
5830
|
+
tron: "1.0.0-beta.4",
|
|
5831
|
+
solana: "1.0.0-beta.5",
|
|
5832
|
+
spark: "1.0.0-beta.6"
|
|
5833
|
+
}
|
|
5834
|
+
};
|
|
5835
|
+
function parseVersion(version) {
|
|
5836
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
|
|
5837
|
+
if (!match) {
|
|
5838
|
+
return { major: 0, minor: 0, patch: 0, prerelease: "" };
|
|
5839
|
+
}
|
|
5840
|
+
return {
|
|
5841
|
+
major: parseInt(match[1], 10),
|
|
5842
|
+
minor: parseInt(match[2], 10),
|
|
5843
|
+
patch: parseInt(match[3], 10),
|
|
5844
|
+
prerelease: match[4] ?? ""
|
|
5845
|
+
};
|
|
5846
|
+
}
|
|
5847
|
+
function compareVersions(a, b) {
|
|
5848
|
+
const va = parseVersion(a);
|
|
5849
|
+
const vb = parseVersion(b);
|
|
5850
|
+
if (va.major !== vb.major) return va.major < vb.major ? -1 : 1;
|
|
5851
|
+
if (va.minor !== vb.minor) return va.minor < vb.minor ? -1 : 1;
|
|
5852
|
+
if (va.patch !== vb.patch) return va.patch < vb.patch ? -1 : 1;
|
|
5853
|
+
if (va.prerelease && !vb.prerelease) return -1;
|
|
5854
|
+
if (!va.prerelease && vb.prerelease) return 1;
|
|
5855
|
+
if (va.prerelease && vb.prerelease) {
|
|
5856
|
+
return va.prerelease < vb.prerelease ? -1 : va.prerelease > vb.prerelease ? 1 : 0;
|
|
5857
|
+
}
|
|
5858
|
+
return 0;
|
|
5859
|
+
}
|
|
5860
|
+
function checkWdkCompatibility(version) {
|
|
5861
|
+
const warnings = [];
|
|
5862
|
+
if (compareVersions(version, WDK_COMPATIBILITY.minVersion) < 0) {
|
|
5863
|
+
return {
|
|
5864
|
+
compatible: false,
|
|
5865
|
+
warnings: [
|
|
5866
|
+
`@tetherto/wdk ${version} is below minimum supported version ${WDK_COMPATIBILITY.minVersion}`
|
|
5867
|
+
]
|
|
5868
|
+
};
|
|
5869
|
+
}
|
|
5870
|
+
const isTested = WDK_COMPATIBILITY.testedVersions.includes(version);
|
|
5871
|
+
if (!isTested) {
|
|
5872
|
+
warnings.push(
|
|
5873
|
+
`@tetherto/wdk ${version} has not been explicitly tested. Tested versions: ${WDK_COMPATIBILITY.testedVersions.join(", ")}`
|
|
5874
|
+
);
|
|
5875
|
+
}
|
|
5876
|
+
for (const [feature, minFeatureVersion] of Object.entries(WDK_COMPATIBILITY.features)) {
|
|
5877
|
+
if (compareVersions(version, minFeatureVersion) < 0) {
|
|
5878
|
+
warnings.push(`Feature '${feature}' requires @tetherto/wdk >= ${minFeatureVersion}`);
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
return { compatible: true, warnings };
|
|
5882
|
+
}
|
|
5883
|
+
function checkWalletEvmCompatibility(version) {
|
|
5884
|
+
const warnings = [];
|
|
5885
|
+
const isTested = WDK_COMPATIBILITY.walletEvmVersions.includes(version);
|
|
5886
|
+
if (!isTested) {
|
|
5887
|
+
warnings.push(
|
|
5888
|
+
`@tetherto/wdk-wallet-evm ${version} has not been explicitly tested. Tested versions: ${WDK_COMPATIBILITY.walletEvmVersions.join(", ")}`
|
|
5889
|
+
);
|
|
5890
|
+
}
|
|
5891
|
+
return { compatible: true, warnings };
|
|
5892
|
+
}
|
|
5893
|
+
function getWalletModuleMinVersion(module2) {
|
|
5894
|
+
return WDK_COMPATIBILITY.walletModuleVersions[module2];
|
|
5895
|
+
}
|
|
5896
|
+
|
|
5897
|
+
// src/idempotency.ts
|
|
5898
|
+
var InMemoryIdempotencyManager = class {
|
|
5899
|
+
_payments = /* @__PURE__ */ new Map();
|
|
5900
|
+
_nonces = /* @__PURE__ */ new Map();
|
|
5901
|
+
_recentTxHashes;
|
|
5902
|
+
_maxRecentTxHashes;
|
|
5903
|
+
/**
|
|
5904
|
+
* @param maxRecentTxHashes - Maximum number of recent tx hashes to track for dedup (default: 1000)
|
|
5905
|
+
*/
|
|
5906
|
+
constructor(maxRecentTxHashes = 1e3) {
|
|
5907
|
+
this._recentTxHashes = /* @__PURE__ */ new Set();
|
|
5908
|
+
this._maxRecentTxHashes = maxRecentTxHashes;
|
|
5909
|
+
}
|
|
5910
|
+
async checkDuplicate(key) {
|
|
5911
|
+
return this._payments.has(key);
|
|
5912
|
+
}
|
|
5913
|
+
async recordPayment(key, receipt) {
|
|
5914
|
+
this._payments.set(key, receipt);
|
|
5915
|
+
if (receipt.txHash) {
|
|
5916
|
+
this._addTxHash(receipt.txHash);
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
async getNonce(address, chain) {
|
|
5920
|
+
const key = this._nonceKey(address, chain);
|
|
5921
|
+
return this._nonces.get(key) ?? 0n;
|
|
5922
|
+
}
|
|
5923
|
+
async incrementNonce(address, chain) {
|
|
5924
|
+
const key = this._nonceKey(address, chain);
|
|
5925
|
+
const current = this._nonces.get(key) ?? 0n;
|
|
5926
|
+
const next = current + 1n;
|
|
5927
|
+
this._nonces.set(key, next);
|
|
5928
|
+
return next;
|
|
5929
|
+
}
|
|
5930
|
+
/**
|
|
5931
|
+
* Check if a transaction hash has been seen recently
|
|
5932
|
+
*/
|
|
5933
|
+
hasTxHash(txHash) {
|
|
5934
|
+
return this._recentTxHashes.has(txHash.toLowerCase());
|
|
5935
|
+
}
|
|
5936
|
+
/**
|
|
5937
|
+
* Get a recorded payment by its idempotency key
|
|
5938
|
+
*/
|
|
5939
|
+
getPayment(key) {
|
|
5940
|
+
return this._payments.get(key);
|
|
5941
|
+
}
|
|
5942
|
+
/**
|
|
5943
|
+
* Get the number of recorded payments
|
|
5944
|
+
*/
|
|
5945
|
+
get size() {
|
|
5946
|
+
return this._payments.size;
|
|
5947
|
+
}
|
|
5948
|
+
/**
|
|
5949
|
+
* Clear all recorded payments and nonces
|
|
5950
|
+
*/
|
|
5951
|
+
clear() {
|
|
5952
|
+
this._payments.clear();
|
|
5953
|
+
this._nonces.clear();
|
|
5954
|
+
this._recentTxHashes.clear();
|
|
5955
|
+
}
|
|
5956
|
+
_nonceKey(address, chain) {
|
|
5957
|
+
return `${chain}:${address.toLowerCase()}`;
|
|
5958
|
+
}
|
|
5959
|
+
_addTxHash(txHash) {
|
|
5960
|
+
const normalized = txHash.toLowerCase();
|
|
5961
|
+
if (this._recentTxHashes.size >= this._maxRecentTxHashes) {
|
|
5962
|
+
const first = this._recentTxHashes.values().next().value;
|
|
5963
|
+
if (first !== void 0) {
|
|
5964
|
+
this._recentTxHashes.delete(first);
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
this._recentTxHashes.add(normalized);
|
|
5968
|
+
}
|
|
5969
|
+
};
|
|
5970
|
+
var NonceManager = class {
|
|
5971
|
+
_nonces = /* @__PURE__ */ new Map();
|
|
5972
|
+
/**
|
|
5973
|
+
* Get the current nonce for an address on a chain.
|
|
5974
|
+
* If no cached value exists, uses the provided fetcher to query on-chain.
|
|
5975
|
+
*
|
|
5976
|
+
* @param address - Wallet address
|
|
5977
|
+
* @param chain - Chain identifier
|
|
5978
|
+
* @param fetchOnChainNonce - Optional function to query the on-chain nonce
|
|
5979
|
+
*/
|
|
5980
|
+
async getNonce(address, chain, fetchOnChainNonce) {
|
|
5981
|
+
const key = this._key(address, chain);
|
|
5982
|
+
const cached = this._nonces.get(key);
|
|
5983
|
+
if (cached !== void 0) {
|
|
5984
|
+
return cached;
|
|
5985
|
+
}
|
|
5986
|
+
if (fetchOnChainNonce) {
|
|
5987
|
+
const onChainNonce = await fetchOnChainNonce();
|
|
5988
|
+
this._nonces.set(key, onChainNonce);
|
|
5989
|
+
return onChainNonce;
|
|
5990
|
+
}
|
|
5991
|
+
return 0n;
|
|
5992
|
+
}
|
|
5993
|
+
/**
|
|
5994
|
+
* Increment the nonce after a successful signature/transaction
|
|
5995
|
+
*/
|
|
5996
|
+
increment(address, chain) {
|
|
5997
|
+
const key = this._key(address, chain);
|
|
5998
|
+
const current = this._nonces.get(key) ?? 0n;
|
|
5999
|
+
const next = current + 1n;
|
|
6000
|
+
this._nonces.set(key, next);
|
|
6001
|
+
return next;
|
|
6002
|
+
}
|
|
6003
|
+
/**
|
|
6004
|
+
* Set the nonce to a specific value (e.g., after querying on-chain)
|
|
6005
|
+
*/
|
|
6006
|
+
set(address, chain, nonce) {
|
|
6007
|
+
const key = this._key(address, chain);
|
|
6008
|
+
this._nonces.set(key, nonce);
|
|
6009
|
+
}
|
|
6010
|
+
/**
|
|
6011
|
+
* Reset the nonce for an address on a chain (forces re-fetch on next use)
|
|
6012
|
+
*/
|
|
6013
|
+
reset(address, chain) {
|
|
6014
|
+
const key = this._key(address, chain);
|
|
6015
|
+
this._nonces.delete(key);
|
|
6016
|
+
}
|
|
6017
|
+
/**
|
|
6018
|
+
* Clear all cached nonces
|
|
6019
|
+
*/
|
|
6020
|
+
clear() {
|
|
6021
|
+
this._nonces.clear();
|
|
6022
|
+
}
|
|
6023
|
+
_key(address, chain) {
|
|
6024
|
+
return `${chain}:${address.toLowerCase()}`;
|
|
6025
|
+
}
|
|
6026
|
+
};
|
|
6027
|
+
function generateIdempotencyKey(params) {
|
|
6028
|
+
return `${params.from.toLowerCase()}:${params.payTo.toLowerCase()}:${params.network}:${params.amount}:${params.url}`;
|
|
6029
|
+
}
|
|
6030
|
+
|
|
6031
|
+
// src/compliance.ts
|
|
6032
|
+
var ComplianceManager = class {
|
|
6033
|
+
_providers = [];
|
|
6034
|
+
_auditTrail = [];
|
|
6035
|
+
/**
|
|
6036
|
+
* Register a compliance provider
|
|
6037
|
+
*/
|
|
6038
|
+
registerProvider(provider) {
|
|
6039
|
+
this._providers.push(provider);
|
|
6040
|
+
}
|
|
6041
|
+
/**
|
|
6042
|
+
* Run all registered providers against the given parameters.
|
|
6043
|
+
* Returns the first rejection, or `{ allowed: true }` if all pass.
|
|
6044
|
+
*
|
|
6045
|
+
* @param params - The transaction parameters to check
|
|
6046
|
+
* @param action - The type of action being performed (default: 'payment')
|
|
6047
|
+
*/
|
|
6048
|
+
async check(params, action = "payment") {
|
|
6049
|
+
if (this._providers.length === 0) {
|
|
6050
|
+
const result2 = { allowed: true };
|
|
6051
|
+
this._recordEvent(action, params, result2);
|
|
6052
|
+
return result2;
|
|
6053
|
+
}
|
|
6054
|
+
for (const provider of this._providers) {
|
|
6055
|
+
const result2 = await provider.check(params);
|
|
6056
|
+
if (!result2.allowed) {
|
|
6057
|
+
this._recordEvent(action, params, result2);
|
|
6058
|
+
return result2;
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
const result = { allowed: true };
|
|
6062
|
+
this._recordEvent(action, params, result);
|
|
6063
|
+
return result;
|
|
6064
|
+
}
|
|
6065
|
+
/**
|
|
6066
|
+
* Get the full audit trail of compliance checks
|
|
6067
|
+
*/
|
|
6068
|
+
getAuditTrail() {
|
|
6069
|
+
return [...this._auditTrail];
|
|
6070
|
+
}
|
|
6071
|
+
/**
|
|
6072
|
+
* Clear the audit trail
|
|
6073
|
+
*/
|
|
6074
|
+
clearAuditTrail() {
|
|
6075
|
+
this._auditTrail = [];
|
|
6076
|
+
}
|
|
6077
|
+
/**
|
|
6078
|
+
* Get the number of registered providers
|
|
6079
|
+
*/
|
|
6080
|
+
get providerCount() {
|
|
6081
|
+
return this._providers.length;
|
|
6082
|
+
}
|
|
6083
|
+
_recordEvent(action, params, result) {
|
|
6084
|
+
this._auditTrail.push({
|
|
6085
|
+
timestamp: Date.now(),
|
|
6086
|
+
action,
|
|
6087
|
+
params,
|
|
6088
|
+
result
|
|
6089
|
+
});
|
|
6090
|
+
}
|
|
6091
|
+
};
|
|
6092
|
+
var BlacklistProvider = class {
|
|
6093
|
+
_addresses;
|
|
6094
|
+
constructor(addresses) {
|
|
6095
|
+
this._addresses = /* @__PURE__ */ new Set();
|
|
6096
|
+
if (addresses) {
|
|
6097
|
+
for (const addr of addresses) {
|
|
6098
|
+
this._addresses.add(addr.toLowerCase());
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
async check(params) {
|
|
6103
|
+
const fromLower = params.from.toLowerCase();
|
|
6104
|
+
const toLower = params.to.toLowerCase();
|
|
6105
|
+
if (this._addresses.has(fromLower)) {
|
|
6106
|
+
return { allowed: false, reason: `Address ${params.from} is blacklisted (sender)` };
|
|
6107
|
+
}
|
|
6108
|
+
if (this._addresses.has(toLower)) {
|
|
6109
|
+
return { allowed: false, reason: `Address ${params.to} is blacklisted (recipient)` };
|
|
6110
|
+
}
|
|
6111
|
+
return { allowed: true };
|
|
6112
|
+
}
|
|
6113
|
+
/**
|
|
6114
|
+
* Add an address to the blacklist
|
|
6115
|
+
*/
|
|
6116
|
+
addAddress(address) {
|
|
6117
|
+
this._addresses.add(address.toLowerCase());
|
|
6118
|
+
}
|
|
6119
|
+
/**
|
|
6120
|
+
* Remove an address from the blacklist
|
|
6121
|
+
*/
|
|
6122
|
+
removeAddress(address) {
|
|
6123
|
+
this._addresses.delete(address.toLowerCase());
|
|
6124
|
+
}
|
|
6125
|
+
/**
|
|
6126
|
+
* Check if an address is blacklisted
|
|
6127
|
+
*/
|
|
6128
|
+
hasAddress(address) {
|
|
6129
|
+
return this._addresses.has(address.toLowerCase());
|
|
6130
|
+
}
|
|
6131
|
+
/**
|
|
6132
|
+
* Get the number of blacklisted addresses
|
|
6133
|
+
*/
|
|
6134
|
+
get size() {
|
|
6135
|
+
return this._addresses.size;
|
|
6136
|
+
}
|
|
6137
|
+
};
|
|
6138
|
+
var AmountLimitProvider = class {
|
|
6139
|
+
_maxPerTransaction;
|
|
6140
|
+
_cumulativeAmounts = /* @__PURE__ */ new Map();
|
|
6141
|
+
_maxCumulative;
|
|
6142
|
+
/**
|
|
6143
|
+
* @param maxPerTransaction - Maximum amount per single transaction
|
|
6144
|
+
* @param maxCumulative - Optional maximum cumulative amount per address
|
|
6145
|
+
*/
|
|
6146
|
+
constructor(maxPerTransaction, maxCumulative) {
|
|
6147
|
+
this._maxPerTransaction = maxPerTransaction;
|
|
6148
|
+
this._maxCumulative = maxCumulative;
|
|
6149
|
+
}
|
|
6150
|
+
async check(params) {
|
|
6151
|
+
if (params.amount > this._maxPerTransaction) {
|
|
6152
|
+
return {
|
|
6153
|
+
allowed: false,
|
|
6154
|
+
reason: `Amount ${params.amount} exceeds per-transaction limit of ${this._maxPerTransaction}`
|
|
6155
|
+
};
|
|
6156
|
+
}
|
|
6157
|
+
if (this._maxCumulative !== void 0) {
|
|
6158
|
+
const key = params.from.toLowerCase();
|
|
6159
|
+
const cumulative = (this._cumulativeAmounts.get(key) ?? 0n) + params.amount;
|
|
6160
|
+
if (cumulative > this._maxCumulative) {
|
|
6161
|
+
return {
|
|
6162
|
+
allowed: false,
|
|
6163
|
+
reason: `Cumulative amount ${cumulative} would exceed limit of ${this._maxCumulative}`
|
|
6164
|
+
};
|
|
6165
|
+
}
|
|
6166
|
+
this._cumulativeAmounts.set(key, cumulative);
|
|
6167
|
+
}
|
|
6168
|
+
return { allowed: true };
|
|
6169
|
+
}
|
|
6170
|
+
/**
|
|
6171
|
+
* Reset cumulative tracking for an address
|
|
6172
|
+
*/
|
|
6173
|
+
resetCumulative(address) {
|
|
6174
|
+
this._cumulativeAmounts.delete(address.toLowerCase());
|
|
6175
|
+
}
|
|
6176
|
+
/**
|
|
6177
|
+
* Reset all cumulative tracking
|
|
6178
|
+
*/
|
|
6179
|
+
resetAllCumulative() {
|
|
6180
|
+
this._cumulativeAmounts.clear();
|
|
6181
|
+
}
|
|
6182
|
+
};
|
|
6183
|
+
|
|
6184
|
+
// src/webhooks.ts
|
|
6185
|
+
var WebhookManager = class {
|
|
6186
|
+
_configs;
|
|
6187
|
+
_deliveryResults = [];
|
|
6188
|
+
_maxDeliveryHistory;
|
|
6189
|
+
/**
|
|
6190
|
+
* @param configs - Array of webhook endpoint configurations
|
|
6191
|
+
* @param maxDeliveryHistory - Maximum number of delivery results to retain (default: 100)
|
|
6192
|
+
*/
|
|
6193
|
+
constructor(configs, maxDeliveryHistory = 100) {
|
|
6194
|
+
this._configs = configs;
|
|
6195
|
+
this._maxDeliveryHistory = maxDeliveryHistory;
|
|
6196
|
+
}
|
|
6197
|
+
/**
|
|
6198
|
+
* Send a webhook event to all subscribed endpoints
|
|
6199
|
+
*
|
|
6200
|
+
* @param event - Event type (e.g., 'payment.completed')
|
|
6201
|
+
* @param payload - Event payload data
|
|
6202
|
+
* @returns Array of delivery results for each endpoint
|
|
6203
|
+
*/
|
|
6204
|
+
async send(event, payload) {
|
|
6205
|
+
const results = [];
|
|
6206
|
+
for (const config of this._configs) {
|
|
6207
|
+
if (config.events && config.events.length > 0 && !config.events.includes(event)) {
|
|
6208
|
+
continue;
|
|
6209
|
+
}
|
|
6210
|
+
const result = await this._deliver(config, event, payload);
|
|
6211
|
+
results.push(result);
|
|
6212
|
+
this._recordResult(result);
|
|
6213
|
+
}
|
|
6214
|
+
return results;
|
|
6215
|
+
}
|
|
6216
|
+
/**
|
|
6217
|
+
* Sign a payload with HMAC-SHA256
|
|
6218
|
+
*
|
|
6219
|
+
* @param payload - The payload to sign (will be JSON.stringify'd if not a string)
|
|
6220
|
+
* @param secret - The secret key
|
|
6221
|
+
* @returns Hex-encoded HMAC-SHA256 signature
|
|
6222
|
+
*/
|
|
6223
|
+
signPayload(payload, secret) {
|
|
6224
|
+
const crypto = require("crypto");
|
|
6225
|
+
const data = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
6226
|
+
return crypto.createHmac("sha256", secret).update(data).digest("hex");
|
|
6227
|
+
}
|
|
6228
|
+
/**
|
|
6229
|
+
* Verify an HMAC-SHA256 signature on a payload
|
|
6230
|
+
*
|
|
6231
|
+
* @param payload - The raw payload string
|
|
6232
|
+
* @param signature - The signature to verify
|
|
6233
|
+
* @param secret - The secret key
|
|
6234
|
+
* @returns True if the signature is valid
|
|
6235
|
+
*/
|
|
6236
|
+
verifySignature(payload, signature, secret) {
|
|
6237
|
+
const expected = this.signPayload(payload, secret);
|
|
6238
|
+
if (expected.length !== signature.length) {
|
|
6239
|
+
return false;
|
|
6240
|
+
}
|
|
6241
|
+
const crypto = require("crypto");
|
|
6242
|
+
return crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(signature, "hex"));
|
|
3100
6243
|
}
|
|
3101
6244
|
/**
|
|
3102
|
-
*
|
|
6245
|
+
* Get recent delivery results
|
|
3103
6246
|
*/
|
|
3104
|
-
|
|
3105
|
-
return
|
|
6247
|
+
getDeliveryResults() {
|
|
6248
|
+
return [...this._deliveryResults];
|
|
3106
6249
|
}
|
|
3107
6250
|
/**
|
|
3108
|
-
*
|
|
6251
|
+
* Clear delivery history
|
|
3109
6252
|
*/
|
|
3110
|
-
|
|
3111
|
-
|
|
6253
|
+
clearDeliveryResults() {
|
|
6254
|
+
this._deliveryResults = [];
|
|
3112
6255
|
}
|
|
3113
6256
|
/**
|
|
3114
|
-
* Get
|
|
6257
|
+
* Get the number of configured webhook endpoints
|
|
3115
6258
|
*/
|
|
3116
|
-
|
|
3117
|
-
return
|
|
6259
|
+
get endpointCount() {
|
|
6260
|
+
return this._configs.length;
|
|
3118
6261
|
}
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
6262
|
+
async _deliver(config, event, payload) {
|
|
6263
|
+
const maxRetries = config.retries ?? 3;
|
|
6264
|
+
const body = JSON.stringify({ event, timestamp: (/* @__PURE__ */ new Date()).toISOString(), data: payload });
|
|
6265
|
+
const signature = this.signPayload(body, config.secret);
|
|
6266
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
6267
|
+
try {
|
|
6268
|
+
const response = await fetch(config.url, {
|
|
6269
|
+
method: "POST",
|
|
6270
|
+
headers: {
|
|
6271
|
+
"Content-Type": "application/json",
|
|
6272
|
+
"X-Webhook-Signature": signature,
|
|
6273
|
+
"X-Webhook-Event": event
|
|
6274
|
+
},
|
|
6275
|
+
body
|
|
6276
|
+
});
|
|
6277
|
+
if (response.ok) {
|
|
6278
|
+
return {
|
|
6279
|
+
url: config.url,
|
|
6280
|
+
success: true,
|
|
6281
|
+
statusCode: response.status,
|
|
6282
|
+
attempts: attempt
|
|
6283
|
+
};
|
|
6284
|
+
}
|
|
6285
|
+
if (response.status >= 400 && response.status < 500) {
|
|
6286
|
+
return {
|
|
6287
|
+
url: config.url,
|
|
6288
|
+
success: false,
|
|
6289
|
+
statusCode: response.status,
|
|
6290
|
+
error: `HTTP ${response.status}`,
|
|
6291
|
+
attempts: attempt
|
|
6292
|
+
};
|
|
6293
|
+
}
|
|
6294
|
+
if (attempt === maxRetries) {
|
|
6295
|
+
return {
|
|
6296
|
+
url: config.url,
|
|
6297
|
+
success: false,
|
|
6298
|
+
statusCode: response.status,
|
|
6299
|
+
error: `HTTP ${response.status} after ${maxRetries} attempts`,
|
|
6300
|
+
attempts: attempt
|
|
6301
|
+
};
|
|
6302
|
+
}
|
|
6303
|
+
await this._sleep(Math.min(1e3 * Math.pow(2, attempt - 1), 1e4));
|
|
6304
|
+
} catch (error) {
|
|
6305
|
+
if (attempt === maxRetries) {
|
|
6306
|
+
return {
|
|
6307
|
+
url: config.url,
|
|
6308
|
+
success: false,
|
|
6309
|
+
error: error instanceof Error ? error.message : String(error),
|
|
6310
|
+
attempts: attempt
|
|
6311
|
+
};
|
|
6312
|
+
}
|
|
6313
|
+
await this._sleep(Math.min(1e3 * Math.pow(2, attempt - 1), 1e4));
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
6316
|
+
return {
|
|
6317
|
+
url: config.url,
|
|
6318
|
+
success: false,
|
|
6319
|
+
error: "Max retries exceeded",
|
|
6320
|
+
attempts: maxRetries
|
|
6321
|
+
};
|
|
3151
6322
|
}
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
6323
|
+
_recordResult(result) {
|
|
6324
|
+
this._deliveryResults.push(result);
|
|
6325
|
+
while (this._deliveryResults.length > this._maxDeliveryHistory) {
|
|
6326
|
+
this._deliveryResults.shift();
|
|
6327
|
+
}
|
|
3157
6328
|
}
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
minor: parseInt(match[2], 10),
|
|
3161
|
-
patch: parseInt(match[3], 10),
|
|
3162
|
-
prerelease: match[4] ?? ""
|
|
3163
|
-
};
|
|
3164
|
-
}
|
|
3165
|
-
function compareVersions(a, b) {
|
|
3166
|
-
const va = parseVersion(a);
|
|
3167
|
-
const vb = parseVersion(b);
|
|
3168
|
-
if (va.major !== vb.major) return va.major < vb.major ? -1 : 1;
|
|
3169
|
-
if (va.minor !== vb.minor) return va.minor < vb.minor ? -1 : 1;
|
|
3170
|
-
if (va.patch !== vb.patch) return va.patch < vb.patch ? -1 : 1;
|
|
3171
|
-
if (va.prerelease && !vb.prerelease) return -1;
|
|
3172
|
-
if (!va.prerelease && vb.prerelease) return 1;
|
|
3173
|
-
if (va.prerelease && vb.prerelease) {
|
|
3174
|
-
return va.prerelease < vb.prerelease ? -1 : va.prerelease > vb.prerelease ? 1 : 0;
|
|
6329
|
+
_sleep(ms) {
|
|
6330
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3175
6331
|
}
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
}
|
|
6332
|
+
};
|
|
6333
|
+
|
|
6334
|
+
// src/indexer.ts
|
|
6335
|
+
var WdkIndexerVerifier = class {
|
|
6336
|
+
endpoint;
|
|
6337
|
+
apiKey;
|
|
6338
|
+
timeout;
|
|
6339
|
+
constructor(config) {
|
|
6340
|
+
if (!config.endpoint) {
|
|
6341
|
+
throw new Error("Indexer endpoint is required");
|
|
6342
|
+
}
|
|
6343
|
+
this.endpoint = config.endpoint.replace(/\/$/, "");
|
|
6344
|
+
this.apiKey = config.apiKey;
|
|
6345
|
+
this.timeout = config.timeout ?? 1e4;
|
|
3187
6346
|
}
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
)
|
|
6347
|
+
/**
|
|
6348
|
+
* Query a transaction across any supported chain
|
|
6349
|
+
*/
|
|
6350
|
+
async queryTransaction(query) {
|
|
6351
|
+
if (!query.txHash) {
|
|
6352
|
+
throw new Error("Transaction hash is required");
|
|
6353
|
+
}
|
|
6354
|
+
if (!query.network) {
|
|
6355
|
+
throw new Error("Network is required");
|
|
6356
|
+
}
|
|
6357
|
+
const url = `${this.endpoint}/v1/transactions/${encodeURIComponent(query.network)}/${encodeURIComponent(query.txHash)}`;
|
|
6358
|
+
const headers = {
|
|
6359
|
+
"Content-Type": "application/json"
|
|
6360
|
+
};
|
|
6361
|
+
if (this.apiKey) {
|
|
6362
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
6363
|
+
}
|
|
6364
|
+
const controller = new AbortController();
|
|
6365
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
6366
|
+
try {
|
|
6367
|
+
const response = await fetch(url, {
|
|
6368
|
+
method: "GET",
|
|
6369
|
+
headers,
|
|
6370
|
+
signal: controller.signal
|
|
6371
|
+
});
|
|
6372
|
+
if (!response.ok) {
|
|
6373
|
+
if (response.status === 404) {
|
|
6374
|
+
return {
|
|
6375
|
+
found: false,
|
|
6376
|
+
confirmed: false,
|
|
6377
|
+
from: "",
|
|
6378
|
+
to: "",
|
|
6379
|
+
amount: "0",
|
|
6380
|
+
token: ""
|
|
6381
|
+
};
|
|
6382
|
+
}
|
|
6383
|
+
throw new Error(`Indexer request failed: ${response.status} ${response.statusText}`);
|
|
6384
|
+
}
|
|
6385
|
+
const data = await response.json();
|
|
6386
|
+
return {
|
|
6387
|
+
found: true,
|
|
6388
|
+
confirmed: data.confirmed ?? data.status === "confirmed",
|
|
6389
|
+
from: data.from ?? "",
|
|
6390
|
+
to: data.to ?? "",
|
|
6391
|
+
amount: String(data.amount ?? data.value ?? "0"),
|
|
6392
|
+
token: data.token ?? data.tokenAddress ?? "",
|
|
6393
|
+
blockNumber: data.blockNumber ?? data.block_number,
|
|
6394
|
+
timestamp: data.timestamp ?? data.block_timestamp
|
|
6395
|
+
};
|
|
6396
|
+
} catch (error) {
|
|
6397
|
+
if (error.name === "AbortError") {
|
|
6398
|
+
throw new Error(`Indexer request timed out after ${this.timeout}ms`);
|
|
6399
|
+
}
|
|
6400
|
+
throw error;
|
|
6401
|
+
} finally {
|
|
6402
|
+
clearTimeout(timeoutId);
|
|
6403
|
+
}
|
|
3193
6404
|
}
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
6405
|
+
/**
|
|
6406
|
+
* Verify a transaction matches expected payment parameters
|
|
6407
|
+
*/
|
|
6408
|
+
async verifyPayment(query) {
|
|
6409
|
+
const result = await this.queryTransaction(query);
|
|
6410
|
+
if (!result.found) {
|
|
6411
|
+
return { verified: false, reason: "Transaction not found" };
|
|
3197
6412
|
}
|
|
6413
|
+
if (!result.confirmed) {
|
|
6414
|
+
return { verified: false, reason: "Transaction not yet confirmed" };
|
|
6415
|
+
}
|
|
6416
|
+
if (query.expectedTo) {
|
|
6417
|
+
const normalizedExpected = query.expectedTo.toLowerCase();
|
|
6418
|
+
const normalizedActual = result.to.toLowerCase();
|
|
6419
|
+
if (normalizedActual !== normalizedExpected) {
|
|
6420
|
+
return {
|
|
6421
|
+
verified: false,
|
|
6422
|
+
reason: `Recipient mismatch: expected ${query.expectedTo}, got ${result.to}`
|
|
6423
|
+
};
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6426
|
+
if (query.expectedAmount) {
|
|
6427
|
+
const expected = BigInt(query.expectedAmount);
|
|
6428
|
+
const actual = BigInt(result.amount);
|
|
6429
|
+
if (actual < expected) {
|
|
6430
|
+
return {
|
|
6431
|
+
verified: false,
|
|
6432
|
+
reason: `Amount insufficient: expected ${query.expectedAmount}, got ${result.amount}`
|
|
6433
|
+
};
|
|
6434
|
+
}
|
|
6435
|
+
}
|
|
6436
|
+
return { verified: true };
|
|
3198
6437
|
}
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
6438
|
+
/**
|
|
6439
|
+
* Check indexer health
|
|
6440
|
+
*/
|
|
6441
|
+
async healthCheck() {
|
|
6442
|
+
const controller = new AbortController();
|
|
6443
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
6444
|
+
try {
|
|
6445
|
+
const headers = {};
|
|
6446
|
+
if (this.apiKey) {
|
|
6447
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
6448
|
+
}
|
|
6449
|
+
const response = await fetch(`${this.endpoint}/health`, {
|
|
6450
|
+
method: "GET",
|
|
6451
|
+
headers,
|
|
6452
|
+
signal: controller.signal
|
|
6453
|
+
});
|
|
6454
|
+
return response.ok;
|
|
6455
|
+
} catch {
|
|
6456
|
+
return false;
|
|
6457
|
+
} finally {
|
|
6458
|
+
clearTimeout(timeoutId);
|
|
6459
|
+
}
|
|
3208
6460
|
}
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
return WDK_COMPATIBILITY.walletModuleVersions[module2];
|
|
6461
|
+
};
|
|
6462
|
+
function createIndexerVerifier(config) {
|
|
6463
|
+
return new WdkIndexerVerifier(config);
|
|
3213
6464
|
}
|
|
3214
6465
|
|
|
3215
6466
|
// src/hardware/types.ts
|
|
@@ -3834,26 +7085,390 @@ function isHardwareWalletSupported() {
|
|
|
3834
7085
|
const support = detectHardwareWalletSupport();
|
|
3835
7086
|
return support.ledger.webusb || support.ledger.webhid || support.ledger.bluetooth || support.trezor;
|
|
3836
7087
|
}
|
|
7088
|
+
|
|
7089
|
+
// src/providers/moonpay.ts
|
|
7090
|
+
var NETWORK_TO_MOONPAY_CURRENCY = {
|
|
7091
|
+
"eip155:1": "usdt",
|
|
7092
|
+
"eip155:42161": "usdt_arbitrum",
|
|
7093
|
+
"eip155:137": "usdt_polygon",
|
|
7094
|
+
"eip155:8453": "usdt_base",
|
|
7095
|
+
"eip155:10": "usdt_optimism",
|
|
7096
|
+
"eip155:43114": "usdt_avalanche_c_chain",
|
|
7097
|
+
"eip155:56": "usdt_bsc"
|
|
7098
|
+
};
|
|
7099
|
+
var SUPPORTED_FIAT_CURRENCIES = ["USD", "EUR", "GBP"];
|
|
7100
|
+
var SUPPORTED_NETWORKS = Object.keys(NETWORK_TO_MOONPAY_CURRENCY);
|
|
7101
|
+
var BASE_URLS = {
|
|
7102
|
+
production: "https://buy.moonpay.com",
|
|
7103
|
+
sandbox: "https://buy-sandbox.moonpay.com"
|
|
7104
|
+
};
|
|
7105
|
+
var API_URLS = {
|
|
7106
|
+
production: "https://api.moonpay.com",
|
|
7107
|
+
sandbox: "https://api.moonpay.com"
|
|
7108
|
+
};
|
|
7109
|
+
var MoonpayOnRampProvider = class {
|
|
7110
|
+
name = "moonpay";
|
|
7111
|
+
_apiKey;
|
|
7112
|
+
_environment;
|
|
7113
|
+
constructor(config) {
|
|
7114
|
+
if (!config.apiKey) {
|
|
7115
|
+
throw new Error("Moonpay API key is required");
|
|
7116
|
+
}
|
|
7117
|
+
this._apiKey = config.apiKey;
|
|
7118
|
+
this._environment = config.environment ?? "production";
|
|
7119
|
+
}
|
|
7120
|
+
/**
|
|
7121
|
+
* Get the base widget URL for the current environment
|
|
7122
|
+
*/
|
|
7123
|
+
get baseUrl() {
|
|
7124
|
+
return BASE_URLS[this._environment];
|
|
7125
|
+
}
|
|
7126
|
+
/**
|
|
7127
|
+
* Get the API base URL for the current environment
|
|
7128
|
+
*/
|
|
7129
|
+
get apiUrl() {
|
|
7130
|
+
return API_URLS[this._environment];
|
|
7131
|
+
}
|
|
7132
|
+
/**
|
|
7133
|
+
* Get a quote for fiat-to-crypto conversion
|
|
7134
|
+
*
|
|
7135
|
+
* This method fetches a real-time quote from Moonpay.
|
|
7136
|
+
* Override `_fetchQuote` for testing.
|
|
7137
|
+
*/
|
|
7138
|
+
async getQuote(params) {
|
|
7139
|
+
const currencyCode = this._getCurrencyCode(params.network);
|
|
7140
|
+
if (!currencyCode) {
|
|
7141
|
+
throw new Error(`Network "${params.network}" is not supported by Moonpay`);
|
|
7142
|
+
}
|
|
7143
|
+
if (params.fiatAmount <= 0) {
|
|
7144
|
+
throw new Error("fiatAmount must be greater than 0");
|
|
7145
|
+
}
|
|
7146
|
+
if (!SUPPORTED_FIAT_CURRENCIES.includes(params.fiatCurrency.toUpperCase())) {
|
|
7147
|
+
throw new Error(
|
|
7148
|
+
`Currency "${params.fiatCurrency}" is not supported. Supported: ${SUPPORTED_FIAT_CURRENCIES.join(", ")}`
|
|
7149
|
+
);
|
|
7150
|
+
}
|
|
7151
|
+
const quoteUrl = `${this.apiUrl}/v3/currencies/${currencyCode}/buy_quote?apiKey=${this._apiKey}&baseCurrencyAmount=${params.fiatAmount}&baseCurrencyCode=${params.fiatCurrency.toLowerCase()}`;
|
|
7152
|
+
const data = await this._fetchQuote(quoteUrl);
|
|
7153
|
+
return {
|
|
7154
|
+
fiatAmount: params.fiatAmount,
|
|
7155
|
+
fiatCurrency: params.fiatCurrency.toUpperCase(),
|
|
7156
|
+
cryptoAmount: String(data.quoteCurrencyAmount ?? "0"),
|
|
7157
|
+
cryptoCurrency: "USDT",
|
|
7158
|
+
exchangeRate: Number(data.quoteCurrencyPrice ?? 1),
|
|
7159
|
+
fees: {
|
|
7160
|
+
network: String(data.networkFeeAmount ?? "0"),
|
|
7161
|
+
service: String(data.feeAmount ?? "0"),
|
|
7162
|
+
total: String(data.totalFeeAmount ?? "0")
|
|
7163
|
+
},
|
|
7164
|
+
estimatedTime: 600
|
|
7165
|
+
// ~10 minutes typical for card purchases
|
|
7166
|
+
};
|
|
7167
|
+
}
|
|
7168
|
+
/**
|
|
7169
|
+
* Fetch quote from Moonpay API (override in tests)
|
|
7170
|
+
*/
|
|
7171
|
+
async _fetchQuote(url) {
|
|
7172
|
+
const response = await fetch(url);
|
|
7173
|
+
if (!response.ok) {
|
|
7174
|
+
throw new Error(`Moonpay API error: ${response.status} ${response.statusText}`);
|
|
7175
|
+
}
|
|
7176
|
+
return response.json();
|
|
7177
|
+
}
|
|
7178
|
+
/**
|
|
7179
|
+
* Create a Moonpay widget URL for the user
|
|
7180
|
+
*/
|
|
7181
|
+
createWidget(params) {
|
|
7182
|
+
const currencyCode = this._getCurrencyCode(params.network);
|
|
7183
|
+
if (!currencyCode) {
|
|
7184
|
+
throw new Error(`Network "${params.network}" is not supported by Moonpay`);
|
|
7185
|
+
}
|
|
7186
|
+
if (!params.walletAddress) {
|
|
7187
|
+
throw new Error("walletAddress is required");
|
|
7188
|
+
}
|
|
7189
|
+
if (params.fiatAmount <= 0) {
|
|
7190
|
+
throw new Error("fiatAmount must be greater than 0");
|
|
7191
|
+
}
|
|
7192
|
+
const queryParams = new URLSearchParams({
|
|
7193
|
+
apiKey: this._apiKey,
|
|
7194
|
+
currencyCode,
|
|
7195
|
+
baseCurrencyCode: params.fiatCurrency.toLowerCase(),
|
|
7196
|
+
baseCurrencyAmount: String(params.fiatAmount),
|
|
7197
|
+
walletAddress: params.walletAddress
|
|
7198
|
+
});
|
|
7199
|
+
if (params.redirectUrl) {
|
|
7200
|
+
queryParams.set("redirectURL", params.redirectUrl);
|
|
7201
|
+
}
|
|
7202
|
+
const widgetUrl = `${this.baseUrl}?${queryParams.toString()}`;
|
|
7203
|
+
const orderId = `mp_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
7204
|
+
const expiresAt = new Date(Date.now() + 30 * 60 * 1e3).toISOString();
|
|
7205
|
+
return { widgetUrl, orderId, expiresAt };
|
|
7206
|
+
}
|
|
7207
|
+
/**
|
|
7208
|
+
* Get supported fiat currencies
|
|
7209
|
+
*/
|
|
7210
|
+
getSupportedCurrencies() {
|
|
7211
|
+
return [...SUPPORTED_FIAT_CURRENCIES];
|
|
7212
|
+
}
|
|
7213
|
+
/**
|
|
7214
|
+
* Get supported CAIP-2 networks
|
|
7215
|
+
*/
|
|
7216
|
+
getSupportedNetworks() {
|
|
7217
|
+
return [...SUPPORTED_NETWORKS];
|
|
7218
|
+
}
|
|
7219
|
+
/**
|
|
7220
|
+
* Map CAIP-2 network to Moonpay currency code
|
|
7221
|
+
*/
|
|
7222
|
+
_getCurrencyCode(network) {
|
|
7223
|
+
return NETWORK_TO_MOONPAY_CURRENCY[network];
|
|
7224
|
+
}
|
|
7225
|
+
};
|
|
7226
|
+
function getMoonpayCurrencyCode(network) {
|
|
7227
|
+
return NETWORK_TO_MOONPAY_CURRENCY[network];
|
|
7228
|
+
}
|
|
7229
|
+
|
|
7230
|
+
// src/integrations/a2a-adapter.ts
|
|
7231
|
+
function findBestOption(accepts, signers, preferredScheme) {
|
|
7232
|
+
const signerNetworks = new Set(signers.map((s) => s.network));
|
|
7233
|
+
const exactMatch = accepts.find(
|
|
7234
|
+
(a) => a.scheme === preferredScheme && signerNetworks.has(a.network)
|
|
7235
|
+
);
|
|
7236
|
+
if (exactMatch) return exactMatch;
|
|
7237
|
+
const networkMatch = accepts.find((a) => signerNetworks.has(a.network));
|
|
7238
|
+
if (networkMatch) return networkMatch;
|
|
7239
|
+
const schemeMatch = accepts.find((a) => a.scheme === preferredScheme);
|
|
7240
|
+
if (schemeMatch) return schemeMatch;
|
|
7241
|
+
return accepts[0];
|
|
7242
|
+
}
|
|
7243
|
+
async function createWdkA2APaymentClient(wdk, options) {
|
|
7244
|
+
const preferredScheme = options?.preferredScheme ?? "exact";
|
|
7245
|
+
const signers = await wdk.getAllSigners({ schemes: [preferredScheme] });
|
|
7246
|
+
const paymentHandler = async (req) => {
|
|
7247
|
+
if (!req.accepts || req.accepts.length === 0) {
|
|
7248
|
+
throw new Error("No payment options in requirements");
|
|
7249
|
+
}
|
|
7250
|
+
const selected = findBestOption(req.accepts, signers, preferredScheme);
|
|
7251
|
+
if (!selected) {
|
|
7252
|
+
throw new Error("No compatible payment option found for available signers");
|
|
7253
|
+
}
|
|
7254
|
+
if (options?.spendingLimit !== void 0) {
|
|
7255
|
+
const amount = BigInt(selected.amount);
|
|
7256
|
+
if (amount > options.spendingLimit) {
|
|
7257
|
+
throw new Error(`Payment amount ${amount} exceeds spending limit ${options.spendingLimit}`);
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
if (options?.onApprovalRequired) {
|
|
7261
|
+
const approved = await options.onApprovalRequired({
|
|
7262
|
+
amount: BigInt(selected.amount),
|
|
7263
|
+
network: selected.network
|
|
7264
|
+
});
|
|
7265
|
+
if (!approved) {
|
|
7266
|
+
throw new Error("Payment rejected by approval callback");
|
|
7267
|
+
}
|
|
7268
|
+
}
|
|
7269
|
+
const signerEntry = signers.find((s) => s.network === selected.network && s.scheme === selected.scheme) ?? signers.find((s) => s.network === selected.network);
|
|
7270
|
+
if (!signerEntry) {
|
|
7271
|
+
throw new Error(`No signer available for network ${selected.network}`);
|
|
7272
|
+
}
|
|
7273
|
+
if (options?.autoBalance) {
|
|
7274
|
+
const chainName = getChainNameFromNetwork(wdk, selected.network);
|
|
7275
|
+
if (chainName) {
|
|
7276
|
+
const balance = await wdk.getUsdt0Balance(chainName);
|
|
7277
|
+
const requiredAmount = BigInt(selected.amount);
|
|
7278
|
+
if (balance < requiredAmount && options.autoBridge) {
|
|
7279
|
+
const best = await wdk.findBestChainForPayment(requiredAmount);
|
|
7280
|
+
if (best && best.chain !== chainName) {
|
|
7281
|
+
await wdk.bridgeUsdt0({
|
|
7282
|
+
fromChain: best.chain,
|
|
7283
|
+
toChain: chainName,
|
|
7284
|
+
amount: requiredAmount
|
|
7285
|
+
});
|
|
7286
|
+
}
|
|
7287
|
+
}
|
|
7288
|
+
}
|
|
7289
|
+
}
|
|
7290
|
+
const signer = signerEntry.signer;
|
|
7291
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
7292
|
+
const deadline = now + selected.maxTimeoutSeconds;
|
|
7293
|
+
const nonce = "0x" + Array.from(globalThis.crypto.getRandomValues(new Uint8Array(32))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
7294
|
+
const chainId = parseInt(selected.network.split(":")[1] || "0");
|
|
7295
|
+
const signature = await signer.signTypedData({
|
|
7296
|
+
domain: {
|
|
7297
|
+
name: "USD\u20AE0",
|
|
7298
|
+
version: "1",
|
|
7299
|
+
chainId,
|
|
7300
|
+
verifyingContract: selected.asset
|
|
7301
|
+
},
|
|
7302
|
+
types: {
|
|
7303
|
+
TransferWithAuthorization: [
|
|
7304
|
+
{ name: "from", type: "address" },
|
|
7305
|
+
{ name: "to", type: "address" },
|
|
7306
|
+
{ name: "value", type: "uint256" },
|
|
7307
|
+
{ name: "validAfter", type: "uint256" },
|
|
7308
|
+
{ name: "validBefore", type: "uint256" },
|
|
7309
|
+
{ name: "nonce", type: "bytes32" }
|
|
7310
|
+
]
|
|
7311
|
+
},
|
|
7312
|
+
primaryType: "TransferWithAuthorization",
|
|
7313
|
+
message: {
|
|
7314
|
+
from: signer.address,
|
|
7315
|
+
to: selected.payTo,
|
|
7316
|
+
value: BigInt(selected.amount),
|
|
7317
|
+
validAfter: 0n,
|
|
7318
|
+
validBefore: BigInt(deadline),
|
|
7319
|
+
nonce
|
|
7320
|
+
}
|
|
7321
|
+
});
|
|
7322
|
+
return {
|
|
7323
|
+
t402Version: req.t402Version,
|
|
7324
|
+
resource: req.resource,
|
|
7325
|
+
accepted: selected,
|
|
7326
|
+
payload: {
|
|
7327
|
+
signature,
|
|
7328
|
+
from: signer.address,
|
|
7329
|
+
validAfter: "0",
|
|
7330
|
+
validBefore: deadline.toString(),
|
|
7331
|
+
nonce
|
|
7332
|
+
}
|
|
7333
|
+
};
|
|
7334
|
+
};
|
|
7335
|
+
return { signers, paymentHandler };
|
|
7336
|
+
}
|
|
7337
|
+
function getChainNameFromNetwork(wdk, network) {
|
|
7338
|
+
for (const chain of wdk.getConfiguredChains()) {
|
|
7339
|
+
const config = wdk.getChainConfig(chain);
|
|
7340
|
+
if (config && config.network === network) {
|
|
7341
|
+
return chain;
|
|
7342
|
+
}
|
|
7343
|
+
}
|
|
7344
|
+
return void 0;
|
|
7345
|
+
}
|
|
7346
|
+
|
|
7347
|
+
// src/integrations/facilitator-adapter.ts
|
|
7348
|
+
async function toFacilitatorWdkSigner(wdk, chain, options) {
|
|
7349
|
+
const wdkSigner = await wdk.getSigner(chain);
|
|
7350
|
+
const address = wdkSigner.address;
|
|
7351
|
+
return {
|
|
7352
|
+
address,
|
|
7353
|
+
async signTransaction(tx) {
|
|
7354
|
+
if (tx && typeof tx === "object" && "domain" in tx) {
|
|
7355
|
+
const typedData = tx;
|
|
7356
|
+
return wdkSigner.signTypedData(typedData);
|
|
7357
|
+
}
|
|
7358
|
+
if (typeof tx === "string") {
|
|
7359
|
+
return wdkSigner.signMessage(tx);
|
|
7360
|
+
}
|
|
7361
|
+
throw new Error("Unsupported transaction format for WDK facilitator signer");
|
|
7362
|
+
},
|
|
7363
|
+
async signTypedData(data) {
|
|
7364
|
+
return wdkSigner.signTypedData(data);
|
|
7365
|
+
},
|
|
7366
|
+
async sendTransaction(params) {
|
|
7367
|
+
const result = await wdkSigner.sendTransaction({
|
|
7368
|
+
to: params.to,
|
|
7369
|
+
value: params.value,
|
|
7370
|
+
data: params.data
|
|
7371
|
+
});
|
|
7372
|
+
if (options?.bridgeToMainChain && options.bridgeToMainChain !== chain) {
|
|
7373
|
+
const balance = await wdk.getUsdt0Balance(chain);
|
|
7374
|
+
if (balance > 0n) {
|
|
7375
|
+
try {
|
|
7376
|
+
await wdk.bridgeUsdt0({
|
|
7377
|
+
fromChain: chain,
|
|
7378
|
+
toChain: options.bridgeToMainChain,
|
|
7379
|
+
amount: balance
|
|
7380
|
+
});
|
|
7381
|
+
} catch {
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
return result.hash;
|
|
7386
|
+
}
|
|
7387
|
+
};
|
|
7388
|
+
}
|
|
7389
|
+
async function createFacilitatorSigners(wdk, options) {
|
|
7390
|
+
const chains = wdk.getConfiguredChains();
|
|
7391
|
+
const signers = /* @__PURE__ */ new Map();
|
|
7392
|
+
const results = await Promise.allSettled(
|
|
7393
|
+
chains.map(async (chain) => {
|
|
7394
|
+
const signer = await toFacilitatorWdkSigner(wdk, chain, options);
|
|
7395
|
+
return { chain, signer };
|
|
7396
|
+
})
|
|
7397
|
+
);
|
|
7398
|
+
for (const result of results) {
|
|
7399
|
+
if (result.status === "fulfilled") {
|
|
7400
|
+
signers.set(result.value.chain, result.value.signer);
|
|
7401
|
+
}
|
|
7402
|
+
}
|
|
7403
|
+
return signers;
|
|
7404
|
+
}
|
|
7405
|
+
|
|
7406
|
+
// src/integrations/siwx-adapter.ts
|
|
7407
|
+
async function toSIWxSigner(wdk, chain) {
|
|
7408
|
+
const wdkSigner = await wdk.getSigner(chain);
|
|
7409
|
+
return {
|
|
7410
|
+
address: wdkSigner.address,
|
|
7411
|
+
async signMessage(message) {
|
|
7412
|
+
return wdkSigner.signMessage(message);
|
|
7413
|
+
},
|
|
7414
|
+
async signTypedData(data) {
|
|
7415
|
+
return wdkSigner.signTypedData({
|
|
7416
|
+
domain: data.domain,
|
|
7417
|
+
types: data.types,
|
|
7418
|
+
primaryType: data.primaryType,
|
|
7419
|
+
message: data.message
|
|
7420
|
+
});
|
|
7421
|
+
}
|
|
7422
|
+
};
|
|
7423
|
+
}
|
|
7424
|
+
async function createSIWxSigners(wdk) {
|
|
7425
|
+
const chains = wdk.getConfiguredChains();
|
|
7426
|
+
const signers = /* @__PURE__ */ new Map();
|
|
7427
|
+
const results = await Promise.allSettled(
|
|
7428
|
+
chains.map(async (chain) => {
|
|
7429
|
+
const signer = await toSIWxSigner(wdk, chain);
|
|
7430
|
+
return { chain, signer };
|
|
7431
|
+
})
|
|
7432
|
+
);
|
|
7433
|
+
for (const result of results) {
|
|
7434
|
+
if (result.status === "fulfilled") {
|
|
7435
|
+
signers.set(result.value.chain, result.value.signer);
|
|
7436
|
+
}
|
|
7437
|
+
}
|
|
7438
|
+
return signers;
|
|
7439
|
+
}
|
|
3837
7440
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3838
7441
|
0 && (module.exports = {
|
|
7442
|
+
AmountLimitProvider,
|
|
3839
7443
|
BalanceCache,
|
|
3840
7444
|
BalanceError,
|
|
7445
|
+
BlacklistProvider,
|
|
3841
7446
|
BridgeError,
|
|
7447
|
+
BridgeTracker,
|
|
7448
|
+
CHAIN_REGISTRY,
|
|
3842
7449
|
CHAIN_TOKENS,
|
|
3843
7450
|
ChainError,
|
|
7451
|
+
ComplianceManager,
|
|
3844
7452
|
DEFAULT_BALANCE_CACHE_CONFIG,
|
|
3845
7453
|
DEFAULT_CACHE_CONFIG,
|
|
3846
7454
|
DEFAULT_CHAINS,
|
|
3847
7455
|
DEFAULT_RETRY_CONFIG,
|
|
3848
7456
|
DEFAULT_RPC_ENDPOINTS,
|
|
7457
|
+
FailoverProvider,
|
|
3849
7458
|
HardwareWalletError,
|
|
3850
7459
|
HardwareWalletErrorCode,
|
|
7460
|
+
InMemoryIdempotencyManager,
|
|
7461
|
+
InMemoryReceiptStore,
|
|
3851
7462
|
LAYERZERO_ENDPOINT_IDS,
|
|
3852
7463
|
LedgerSigner,
|
|
3853
7464
|
MockWDKSigner,
|
|
7465
|
+
MoonpayOnRampProvider,
|
|
7466
|
+
NonceManager,
|
|
3854
7467
|
RPCError,
|
|
7468
|
+
SUPPORTED_WDK_RANGE,
|
|
3855
7469
|
SignerError,
|
|
3856
7470
|
SigningError,
|
|
7471
|
+
T402EventEmitter,
|
|
3857
7472
|
T402WDK,
|
|
3858
7473
|
TTLCache,
|
|
3859
7474
|
TransactionError,
|
|
@@ -3872,28 +7487,72 @@ function isHardwareWalletSupported() {
|
|
|
3872
7487
|
WDKTronSignerAdapter,
|
|
3873
7488
|
WDK_COMPATIBILITY,
|
|
3874
7489
|
WdkBridge,
|
|
7490
|
+
WdkIndexerVerifier,
|
|
7491
|
+
WebhookManager,
|
|
7492
|
+
buildVersionedTransaction,
|
|
3875
7493
|
checkWalletEvmCompatibility,
|
|
3876
7494
|
checkWdkCompatibility,
|
|
7495
|
+
compareSemver,
|
|
7496
|
+
createBackup,
|
|
7497
|
+
createCorrelationId,
|
|
3877
7498
|
createDirectBridge,
|
|
7499
|
+
createFacilitatorSigners,
|
|
7500
|
+
createFailoverProvider,
|
|
7501
|
+
createIndexerVerifier,
|
|
3878
7502
|
createLedgerSigner,
|
|
7503
|
+
createSIWxSigners,
|
|
3879
7504
|
createTrezorSigner,
|
|
3880
7505
|
createWDKSigner,
|
|
3881
7506
|
createWDKSvmSigner,
|
|
3882
7507
|
createWDKTonSigner,
|
|
3883
7508
|
createWDKTronSigner,
|
|
7509
|
+
createWdkA2APaymentClient,
|
|
7510
|
+
createWdkMoneyParser,
|
|
7511
|
+
decryptSeed,
|
|
7512
|
+
defaultLogger,
|
|
7513
|
+
deriveATAAddress,
|
|
3884
7514
|
detectHardwareWalletSupport,
|
|
7515
|
+
encryptSeed,
|
|
7516
|
+
generateIdempotencyKey,
|
|
3885
7517
|
getBridgeableChains,
|
|
3886
7518
|
getChainFromNetwork,
|
|
3887
7519
|
getChainId,
|
|
7520
|
+
getChainsByFamily,
|
|
7521
|
+
getJettonWalletAddress,
|
|
7522
|
+
getMoonpayCurrencyCode,
|
|
3888
7523
|
getNetworkFromChain,
|
|
3889
7524
|
getPreferredToken,
|
|
7525
|
+
getPricingProvider,
|
|
7526
|
+
getRecentPriorityFees,
|
|
7527
|
+
getRegistryByCaip2,
|
|
7528
|
+
getSecretManager,
|
|
7529
|
+
getTokenProgram,
|
|
7530
|
+
getTransferFee,
|
|
3890
7531
|
getUsdt0Chains,
|
|
3891
7532
|
getWalletModuleMinVersion,
|
|
3892
7533
|
hasErrorCode,
|
|
3893
7534
|
isHardwareWalletSupported,
|
|
7535
|
+
isPricingProviderRegistered,
|
|
3894
7536
|
isWDKError,
|
|
7537
|
+
mapLayerZeroStatus,
|
|
7538
|
+
noopLogger,
|
|
3895
7539
|
normalizeChainConfig,
|
|
7540
|
+
parseSemver,
|
|
7541
|
+
registerPricingProvider,
|
|
7542
|
+
registerSecretManager,
|
|
7543
|
+
resolveATA,
|
|
7544
|
+
resolveAssetForNetwork,
|
|
7545
|
+
resolveRpcUrl,
|
|
7546
|
+
rotateSeedPassword,
|
|
7547
|
+
satisfiesSemverRange,
|
|
3896
7548
|
supportsBridging,
|
|
7549
|
+
toAtomicUnits,
|
|
7550
|
+
toFacilitatorWdkSigner,
|
|
7551
|
+
toSIWxSigner,
|
|
7552
|
+
transferWithPriorityFee,
|
|
7553
|
+
validatePaymentAddress,
|
|
7554
|
+
verifyBackup,
|
|
7555
|
+
waitForJettonTransfer,
|
|
3897
7556
|
withRetry,
|
|
3898
7557
|
withTimeout,
|
|
3899
7558
|
wrapError
|