@trustwallet/cli 0.14.0 → 0.15.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/commands/erc20.d.ts.map +1 -1
- package/dist/commands/onramp.d.ts.map +1 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/swap.d.ts.map +1 -1
- package/dist/commands/transfer.d.ts.map +1 -1
- package/dist/index.js +1664 -1254
- package/dist/setup/phase-credentials.d.ts +1 -0
- package/dist/setup/phase-credentials.d.ts.map +1 -1
- package/package.json +13 -13
package/dist/index.js
CHANGED
|
@@ -3123,6 +3123,8 @@ __export(dist_exports, {
|
|
|
3123
3123
|
COMMON_TOKENS: () => COMMON_TOKENS,
|
|
3124
3124
|
CommandError: () => CommandError,
|
|
3125
3125
|
ERC20_ERROR_CODES: () => ERC20_ERROR_CODES,
|
|
3126
|
+
ERC20_TRANSFER_SIG: () => ERC20_TRANSFER_SIG,
|
|
3127
|
+
EVM_ADDRESS_RE: () => EVM_ADDRESS_RE,
|
|
3126
3128
|
Erc20Error: () => Erc20Error,
|
|
3127
3129
|
McpResponse: () => McpResponse,
|
|
3128
3130
|
NATIVE_TOKEN_ADDRESS: () => NATIVE_TOKEN_ADDRESS,
|
|
@@ -3144,6 +3146,7 @@ __export(dist_exports, {
|
|
|
3144
3146
|
WatchError: () => WatchError,
|
|
3145
3147
|
X402Error: () => X402Error,
|
|
3146
3148
|
X402_ERROR_CODES: () => X402_ERROR_CODES,
|
|
3149
|
+
assertEvmAddress: () => assertEvmAddress,
|
|
3147
3150
|
assertStrictPositiveAmount: () => assertStrictPositiveAmount,
|
|
3148
3151
|
chainKeyToCoinType: () => chainKeyToCoinType,
|
|
3149
3152
|
classifyApiError: () => classifyApiError,
|
|
@@ -3151,6 +3154,7 @@ __export(dist_exports, {
|
|
|
3151
3154
|
classifyWalletLoadError: () => classifyWalletLoadError,
|
|
3152
3155
|
coinTypeToChainKey: () => coinTypeToChainKey,
|
|
3153
3156
|
createTwakClient: () => createTwakClient,
|
|
3157
|
+
encodeErc20Transfer: () => encodeErc20Transfer,
|
|
3154
3158
|
fetchBalance: () => fetchBalance,
|
|
3155
3159
|
fetchBalances: () => fetchBalances,
|
|
3156
3160
|
fiatToTokenAmount: () => fiatToTokenAmount,
|
|
@@ -3169,13 +3173,18 @@ __export(dist_exports, {
|
|
|
3169
3173
|
getSupportedCoinTypes: () => getSupportedCoinTypes,
|
|
3170
3174
|
isChainSupported: () => isChainSupported,
|
|
3171
3175
|
isCoinTypeSupported: () => isCoinTypeSupported,
|
|
3176
|
+
isEvmAddress: () => isEvmAddress,
|
|
3172
3177
|
isNativeTokenId: () => isNativeTokenId,
|
|
3173
3178
|
isRateLimited: () => isRateLimited,
|
|
3179
|
+
lookupKnownTokenDecimals: () => lookupKnownTokenDecimals,
|
|
3174
3180
|
parseAmount: () => parseAmount,
|
|
3175
3181
|
parseHumanAmount: () => parseHumanAmount,
|
|
3176
3182
|
parseTokenId: () => parseTokenId,
|
|
3177
3183
|
rankQuotesByBest: () => rankQuotesByBest,
|
|
3184
|
+
requireChain: () => requireChain,
|
|
3178
3185
|
resolveAddressOrName: () => resolveAddressOrName,
|
|
3186
|
+
resolveAsset: () => resolveAsset,
|
|
3187
|
+
resolveChain: () => resolveChain,
|
|
3179
3188
|
resolveName: () => resolveName,
|
|
3180
3189
|
resolveToken: () => resolveToken,
|
|
3181
3190
|
signRequest: () => signRequest,
|
|
@@ -3234,20 +3243,30 @@ function getNativeAddressPlaceholder(chainKey) {
|
|
|
3234
3243
|
if (chain2.id === "celo") return CELO_NATIVE_ADDRESS;
|
|
3235
3244
|
return NATIVE_TOKEN_ADDRESS;
|
|
3236
3245
|
}
|
|
3246
|
+
function lookupKnownTokenDecimals(chainKey, tokenAddress) {
|
|
3247
|
+
const chain2 = getChainById(chainKey);
|
|
3248
|
+
if (!chain2) return void 0;
|
|
3249
|
+
if (!tokenAddress) return chain2.decimals;
|
|
3250
|
+
const mapKey = getChainMapKey(chainKey) ?? chainKey.toLowerCase();
|
|
3251
|
+
const registry = COMMON_TOKENS[mapKey] ?? {};
|
|
3252
|
+
const entry = Object.values(registry).find(
|
|
3253
|
+
(e2) => e2.address.toLowerCase() === tokenAddress.toLowerCase()
|
|
3254
|
+
);
|
|
3255
|
+
return entry?.decimals;
|
|
3256
|
+
}
|
|
3237
3257
|
function resolveToken(token, chainKey) {
|
|
3238
3258
|
const assetIdMatch = token.match(/^c(\d+)(?:_t(.+))?$/);
|
|
3239
3259
|
if (assetIdMatch) {
|
|
3240
3260
|
const coinId = parseInt(assetIdMatch[1]);
|
|
3241
3261
|
const contractAddress = assetIdMatch[2];
|
|
3242
|
-
const
|
|
3243
|
-
if (
|
|
3262
|
+
const found = Object.entries(CHAINS).find(([, c3]) => c3.coinId === coinId);
|
|
3263
|
+
if (found) {
|
|
3264
|
+
const [mapKey2, chain2] = found;
|
|
3244
3265
|
if (contractAddress) {
|
|
3245
|
-
const
|
|
3246
|
-
(
|
|
3247
|
-
)
|
|
3248
|
-
|
|
3249
|
-
const decimals = isBscChain && sym && BSC_18_DECIMAL_TOKENS.has(sym) ? 18 : TOKEN_DECIMALS[sym ?? ""] ?? 18;
|
|
3250
|
-
return { address: contractAddress, decimals };
|
|
3266
|
+
const entry = Object.values(COMMON_TOKENS[mapKey2] || {}).find(
|
|
3267
|
+
(e2) => e2.address.toLowerCase() === contractAddress.toLowerCase()
|
|
3268
|
+
);
|
|
3269
|
+
return { address: contractAddress, decimals: entry?.decimals ?? 18 };
|
|
3251
3270
|
}
|
|
3252
3271
|
return {
|
|
3253
3272
|
address: getNativeAddressPlaceholder(chain2.id),
|
|
@@ -3258,11 +3277,9 @@ function resolveToken(token, chainKey) {
|
|
|
3258
3277
|
const upperToken = token.toUpperCase();
|
|
3259
3278
|
const chainConfig = getChainById(chainKey);
|
|
3260
3279
|
const mapKey = getChainMapKey(chainKey) ?? chainKey.toLowerCase();
|
|
3261
|
-
const isBsc = mapKey === "bsc";
|
|
3262
3280
|
const chainTokens = COMMON_TOKENS[mapKey];
|
|
3263
3281
|
if (chainTokens?.[upperToken]) {
|
|
3264
|
-
|
|
3265
|
-
return { address: chainTokens[upperToken], decimals };
|
|
3282
|
+
return { address: chainTokens[upperToken].address, decimals: chainTokens[upperToken].decimals };
|
|
3266
3283
|
}
|
|
3267
3284
|
if (NATIVE_SYMBOLS.has(upperToken) || chainConfig && upperToken === chainConfig.symbol.toUpperCase()) {
|
|
3268
3285
|
return {
|
|
@@ -3271,23 +3288,20 @@ function resolveToken(token, chainKey) {
|
|
|
3271
3288
|
};
|
|
3272
3289
|
}
|
|
3273
3290
|
if (upperToken.startsWith("USDC")) {
|
|
3274
|
-
const
|
|
3275
|
-
if (!
|
|
3276
|
-
return { address:
|
|
3291
|
+
const entry = chainTokens?.["USDC.e"] || chainTokens?.["USDC"];
|
|
3292
|
+
if (!entry) return null;
|
|
3293
|
+
return { address: entry.address, decimals: entry.decimals };
|
|
3277
3294
|
}
|
|
3278
3295
|
if (upperToken.startsWith("USDT")) {
|
|
3279
|
-
const
|
|
3280
|
-
if (!
|
|
3281
|
-
return { address:
|
|
3296
|
+
const entry = chainTokens?.["USDT"];
|
|
3297
|
+
if (!entry) return null;
|
|
3298
|
+
return { address: entry.address, decimals: entry.decimals };
|
|
3282
3299
|
}
|
|
3283
3300
|
if (token.startsWith("0x") || token.length > 20) {
|
|
3284
3301
|
for (const tokens of Object.values(COMMON_TOKENS)) {
|
|
3285
|
-
for (const
|
|
3286
|
-
if (
|
|
3287
|
-
return {
|
|
3288
|
-
address: token,
|
|
3289
|
-
decimals: TOKEN_DECIMALS[symbol.replace(".e", "")] ?? 18
|
|
3290
|
-
};
|
|
3302
|
+
for (const entry of Object.values(tokens)) {
|
|
3303
|
+
if (entry.address.toLowerCase() === token.toLowerCase()) {
|
|
3304
|
+
return { address: token, decimals: entry.decimals };
|
|
3291
3305
|
}
|
|
3292
3306
|
}
|
|
3293
3307
|
}
|
|
@@ -3378,6 +3392,95 @@ function tryClassifyError(err) {
|
|
|
3378
3392
|
}
|
|
3379
3393
|
return classifyApiError(err) ?? classifyStorageError(err) ?? classifyWalletLoadError(err);
|
|
3380
3394
|
}
|
|
3395
|
+
function formatTokenId(coinId, contractAddress) {
|
|
3396
|
+
if (contractAddress) {
|
|
3397
|
+
return `c${coinId}_t${contractAddress}`;
|
|
3398
|
+
}
|
|
3399
|
+
return `c${coinId}`;
|
|
3400
|
+
}
|
|
3401
|
+
function parseTokenId(id) {
|
|
3402
|
+
const match = id.match(/^c(\d+)(?:_t(.+))?$/);
|
|
3403
|
+
if (!match) {
|
|
3404
|
+
throw new Error(`Invalid token ID format: ${id}`);
|
|
3405
|
+
}
|
|
3406
|
+
const coinId = Number(match[1]);
|
|
3407
|
+
const contractAddress = match[2] || void 0;
|
|
3408
|
+
return { coinId, contractAddress };
|
|
3409
|
+
}
|
|
3410
|
+
function isNativeTokenId(id) {
|
|
3411
|
+
return /^c\d+$/.test(id);
|
|
3412
|
+
}
|
|
3413
|
+
function tokenIdToChain(id) {
|
|
3414
|
+
const { coinId } = parseTokenId(id);
|
|
3415
|
+
const chain2 = getChainByCoinId(coinId);
|
|
3416
|
+
if (!chain2) return void 0;
|
|
3417
|
+
return Object.entries(CHAINS).find(([, c3]) => c3.coinId === coinId)?.[0];
|
|
3418
|
+
}
|
|
3419
|
+
function resolveChain(input2) {
|
|
3420
|
+
if (typeof input2 !== "string" || input2.trim().length === 0) {
|
|
3421
|
+
throw new WalletError(
|
|
3422
|
+
"CHAIN_UNSUPPORTED",
|
|
3423
|
+
'Chain identifier is required. Examples: "ethereum", "bsc", "solana", "56", "eip155:8453".'
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3426
|
+
const lower = input2.trim().toLowerCase();
|
|
3427
|
+
const colonIdx = lower.indexOf(":");
|
|
3428
|
+
if (colonIdx > 0 && colonIdx < lower.length - 1) {
|
|
3429
|
+
const namespace = lower.slice(0, colonIdx);
|
|
3430
|
+
const reference = lower.slice(colonIdx + 1);
|
|
3431
|
+
for (const [key, chain2] of Object.entries(CHAINS)) {
|
|
3432
|
+
if (chain2.namespace === namespace && chain2.chainId.toLowerCase() === reference) {
|
|
3433
|
+
return key;
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
throw chainUnsupported(input2);
|
|
3437
|
+
}
|
|
3438
|
+
if (lower.startsWith("c") && lower.length > 1) {
|
|
3439
|
+
const rest = lower.slice(1);
|
|
3440
|
+
const underscoreIdx = rest.indexOf("_");
|
|
3441
|
+
const coinStr = underscoreIdx === -1 ? rest : rest.slice(0, underscoreIdx);
|
|
3442
|
+
if (/^\d+$/.test(coinStr)) {
|
|
3443
|
+
const key = coinTypeToChainKey(Number(coinStr));
|
|
3444
|
+
if (key) return key;
|
|
3445
|
+
throw chainUnsupported(input2);
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
if (CHAINS[lower]) return lower;
|
|
3449
|
+
const aliased = ALIASES[lower];
|
|
3450
|
+
if (aliased) return aliased;
|
|
3451
|
+
for (const [key, chain2] of Object.entries(CHAINS)) {
|
|
3452
|
+
if (chain2.id === lower) return key;
|
|
3453
|
+
}
|
|
3454
|
+
if (/^0x[0-9a-f]+$/.test(lower)) {
|
|
3455
|
+
const key = lookupEvm(parseInt(lower, 16).toString());
|
|
3456
|
+
if (key) return key;
|
|
3457
|
+
} else if (/^\d+$/.test(lower)) {
|
|
3458
|
+
const key = lookupEvm(lower);
|
|
3459
|
+
if (key) return key;
|
|
3460
|
+
}
|
|
3461
|
+
throw chainUnsupported(input2);
|
|
3462
|
+
}
|
|
3463
|
+
function lookupEvm(chainId) {
|
|
3464
|
+
for (const [key, chain2] of Object.entries(CHAINS)) {
|
|
3465
|
+
if (chain2.namespace === "eip155" && chain2.chainId === chainId) return key;
|
|
3466
|
+
}
|
|
3467
|
+
return void 0;
|
|
3468
|
+
}
|
|
3469
|
+
function resolveAsset(chain2, tokenAddress) {
|
|
3470
|
+
const chainKey = resolveChain(chain2);
|
|
3471
|
+
const coinId = CHAINS[chainKey].coinId;
|
|
3472
|
+
return { chainKey, coinId, assetId: formatTokenId(coinId, tokenAddress) };
|
|
3473
|
+
}
|
|
3474
|
+
function chainUnsupported(input2) {
|
|
3475
|
+
const suggestions = suggest(input2);
|
|
3476
|
+
const hint = suggestions.length > 0 ? ` Did you mean: ${suggestions.map((s2) => `"${s2}"`).join(", ")}?` : "";
|
|
3477
|
+
return new WalletError("CHAIN_UNSUPPORTED", `Unknown chain: "${input2}".${hint}`);
|
|
3478
|
+
}
|
|
3479
|
+
function suggest(input2) {
|
|
3480
|
+
const lower = input2.trim().toLowerCase();
|
|
3481
|
+
const candidates = [...Object.keys(CHAINS), ...Object.keys(ALIASES)];
|
|
3482
|
+
return candidates.filter((c3) => c3.includes(lower) || lower.includes(c3)).slice(0, 3);
|
|
3483
|
+
}
|
|
3381
3484
|
function sortQueryParams(query) {
|
|
3382
3485
|
if (!query) return query;
|
|
3383
3486
|
const queryParams = new URLSearchParams(query);
|
|
@@ -3490,29 +3593,29 @@ async function resolveAddressOrName(value) {
|
|
|
3490
3593
|
if (!resolved) throw new Error(`Could not resolve name: ${value}`);
|
|
3491
3594
|
return resolved;
|
|
3492
3595
|
}
|
|
3493
|
-
function
|
|
3494
|
-
|
|
3495
|
-
return `c${coinId}_t${contractAddress}`;
|
|
3496
|
-
}
|
|
3497
|
-
return `c${coinId}`;
|
|
3596
|
+
function isEvmAddress(value) {
|
|
3597
|
+
return EVM_ADDRESS_RE.test(value);
|
|
3498
3598
|
}
|
|
3499
|
-
function
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3599
|
+
function assertEvmAddress(value, label) {
|
|
3600
|
+
if (!EVM_ADDRESS_RE.test(value)) {
|
|
3601
|
+
throw new WalletError(
|
|
3602
|
+
"VALIDATION_ERROR",
|
|
3603
|
+
`${label} is not a valid EVM address: ${value}. Expected 0x-prefixed 40-hex.`
|
|
3604
|
+
);
|
|
3503
3605
|
}
|
|
3504
|
-
const coinId = Number(match[1]);
|
|
3505
|
-
const contractAddress = match[2] || void 0;
|
|
3506
|
-
return { coinId, contractAddress };
|
|
3507
3606
|
}
|
|
3508
|
-
function
|
|
3509
|
-
|
|
3607
|
+
function encodeErc20Transfer(to3, amount) {
|
|
3608
|
+
assertEvmAddress(to3, "to");
|
|
3609
|
+
const paddedTo = to3.slice(2).toLowerCase().padStart(64, "0");
|
|
3610
|
+
const paddedAmount = amount.toString(16).padStart(64, "0");
|
|
3611
|
+
return `${ERC20_TRANSFER_SIG}${paddedTo}${paddedAmount}`;
|
|
3510
3612
|
}
|
|
3511
|
-
function
|
|
3512
|
-
const
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3613
|
+
function requireChain(chainKey) {
|
|
3614
|
+
const chain2 = getChainById(chainKey);
|
|
3615
|
+
if (!chain2) {
|
|
3616
|
+
throw new TwakValidationError(`Unknown chain: ${chainKey}`, { chainKey });
|
|
3617
|
+
}
|
|
3618
|
+
return chain2;
|
|
3516
3619
|
}
|
|
3517
3620
|
function sortQueryRaw(query) {
|
|
3518
3621
|
if (!query) return query;
|
|
@@ -3614,7 +3717,7 @@ async function fetchBalance(httpClient, assetId, address, currency = "USD") {
|
|
|
3614
3717
|
throw new BalanceError("NETWORK_ERROR", "Could not fetch balance. Please try again later.");
|
|
3615
3718
|
}
|
|
3616
3719
|
}
|
|
3617
|
-
var import_crypto, NATIVE_TOKEN_ADDRESS, SOLANA_NATIVE_ADDRESS, CHAINS, COMMON_TOKENS,
|
|
3720
|
+
var import_crypto, NATIVE_TOKEN_ADDRESS, SOLANA_NATIVE_ADDRESS, CHAINS, COMMON_TOKENS, NATIVE_SYMBOLS, CELO_NATIVE_ADDRESS, TwakError, TwakApiError, TwakNetworkError, TwakValidationError, CommandError, COMMON_ERROR_CODES, FS_ERRNO_CODES, SWAP_ERROR_CODES, SwapError, TRANSFER_ERROR_CODES, TransferError, WALLET_ERROR_CODES, WalletError, QUERY_ERROR_CODES, QueryError, BALANCE_ERROR_CODES, BalanceError, ALERT_ERROR_CODES, AlertError, ERC20_ERROR_CODES, Erc20Error, WATCH_ERROR_CODES, WatchError, AUTOMATION_ERROR_CODES, AutomationError, X402_ERROR_CODES, X402Error, ALIASES, DECIMAL_AMOUNT_RE, UNSAFE_KEYS, NAMING_API, EVM_ADDRESS_RE, ERC20_TRANSFER_SIG, DEFAULT_GATEWAY_URL, DEFAULT_MARKET_URL, DEFAULT_TIMEOUT, DEFAULT_USER_AGENT, DEFAULT_X_ORIGIN, TwakHttpClient, ALLOWED_OFFRAMP_PROVIDERS, McpResponse;
|
|
3618
3721
|
var init_dist = __esm({
|
|
3619
3722
|
"../core/dist/index.mjs"() {
|
|
3620
3723
|
"use strict";
|
|
@@ -3646,7 +3749,10 @@ var init_dist = __esm({
|
|
|
3646
3749
|
explorerUrl: "https://arbiscan.io",
|
|
3647
3750
|
rpcUrl: "https://arbitrum.rpc.trustwallet.com",
|
|
3648
3751
|
isEVM: true,
|
|
3649
|
-
sampleAccount: "0xecf9ffa7f51e1194f89c25ad8c484f6bfd04e1ac"
|
|
3752
|
+
sampleAccount: "0xecf9ffa7f51e1194f89c25ad8c484f6bfd04e1ac",
|
|
3753
|
+
// Sub-second block times — poll fast, fail fast.
|
|
3754
|
+
receiptPollMs: 500,
|
|
3755
|
+
receiptTimeoutMs: 3e4
|
|
3650
3756
|
},
|
|
3651
3757
|
optimism: {
|
|
3652
3758
|
id: "optimism",
|
|
@@ -3659,7 +3765,8 @@ var init_dist = __esm({
|
|
|
3659
3765
|
explorerUrl: "https://optimistic.etherscan.io",
|
|
3660
3766
|
rpcUrl: "https://optimism.rpc.trustwallet.com",
|
|
3661
3767
|
isEVM: true,
|
|
3662
|
-
sampleAccount: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
|
|
3768
|
+
sampleAccount: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
|
|
3769
|
+
receiptPollMs: 1e3
|
|
3663
3770
|
},
|
|
3664
3771
|
polygon: {
|
|
3665
3772
|
id: "polygon",
|
|
@@ -3672,7 +3779,8 @@ var init_dist = __esm({
|
|
|
3672
3779
|
explorerUrl: "https://polygonscan.com",
|
|
3673
3780
|
rpcUrl: "https://polygon.rpc.trustwallet.com",
|
|
3674
3781
|
isEVM: true,
|
|
3675
|
-
sampleAccount: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
|
|
3782
|
+
sampleAccount: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
|
|
3783
|
+
receiptPollMs: 1e3
|
|
3676
3784
|
},
|
|
3677
3785
|
bsc: {
|
|
3678
3786
|
id: "smartchain",
|
|
@@ -3698,7 +3806,8 @@ var init_dist = __esm({
|
|
|
3698
3806
|
explorerUrl: "https://snowtrace.io",
|
|
3699
3807
|
rpcUrl: "https://avalanche.rpc.trustwallet.com",
|
|
3700
3808
|
isEVM: true,
|
|
3701
|
-
sampleAccount: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
|
|
3809
|
+
sampleAccount: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
|
|
3810
|
+
receiptPollMs: 1e3
|
|
3702
3811
|
},
|
|
3703
3812
|
base: {
|
|
3704
3813
|
id: "base",
|
|
@@ -3711,7 +3820,8 @@ var init_dist = __esm({
|
|
|
3711
3820
|
explorerUrl: "https://basescan.org",
|
|
3712
3821
|
rpcUrl: "https://base.rpc.trustwallet.com",
|
|
3713
3822
|
isEVM: true,
|
|
3714
|
-
sampleAccount: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
|
|
3823
|
+
sampleAccount: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
|
|
3824
|
+
receiptPollMs: 1e3
|
|
3715
3825
|
},
|
|
3716
3826
|
fantom: {
|
|
3717
3827
|
id: "fantom",
|
|
@@ -3950,72 +4060,72 @@ var init_dist = __esm({
|
|
|
3950
4060
|
};
|
|
3951
4061
|
COMMON_TOKENS = {
|
|
3952
4062
|
ethereum: {
|
|
3953
|
-
USDT: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
3954
|
-
USDC: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
3955
|
-
DAI: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
|
3956
|
-
WETH: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
3957
|
-
WBTC: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
4063
|
+
USDT: { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6 },
|
|
4064
|
+
USDC: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", decimals: 6 },
|
|
4065
|
+
DAI: { address: "0x6B175474E89094C44Da98b954EedeAC495271d0F", decimals: 18 },
|
|
4066
|
+
WETH: { address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decimals: 18 },
|
|
4067
|
+
WBTC: { address: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", decimals: 8 }
|
|
3958
4068
|
},
|
|
3959
4069
|
arbitrum: {
|
|
3960
|
-
USDT: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
|
3961
|
-
USDC: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
3962
|
-
"USDC.e": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8",
|
|
3963
|
-
DAI: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
|
|
3964
|
-
WETH: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
3965
|
-
WBTC: "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f",
|
|
3966
|
-
ARB: "0x912CE59144191C1204E64559FE8253a0e49E6548"
|
|
4070
|
+
USDT: { address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", decimals: 6 },
|
|
4071
|
+
USDC: { address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", decimals: 6 },
|
|
4072
|
+
"USDC.e": { address: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", decimals: 6 },
|
|
4073
|
+
DAI: { address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", decimals: 18 },
|
|
4074
|
+
WETH: { address: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", decimals: 18 },
|
|
4075
|
+
WBTC: { address: "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", decimals: 8 },
|
|
4076
|
+
ARB: { address: "0x912CE59144191C1204E64559FE8253a0e49E6548", decimals: 18 }
|
|
3967
4077
|
},
|
|
3968
4078
|
polygon: {
|
|
3969
|
-
USDT: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
|
3970
|
-
USDC: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
3971
|
-
"USDC.e": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
|
|
3972
|
-
DAI: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
|
|
3973
|
-
WMATIC: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
|
|
3974
|
-
WETH: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619"
|
|
4079
|
+
USDT: { address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", decimals: 6 },
|
|
4080
|
+
USDC: { address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", decimals: 6 },
|
|
4081
|
+
"USDC.e": { address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", decimals: 6 },
|
|
4082
|
+
DAI: { address: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", decimals: 18 },
|
|
4083
|
+
WMATIC: { address: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", decimals: 18 },
|
|
4084
|
+
WETH: { address: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", decimals: 18 }
|
|
3975
4085
|
},
|
|
3976
4086
|
bsc: {
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
4087
|
+
// BEP-20 stables on BSC use 18 decimals (vs the canonical 6 on Ethereum).
|
|
4088
|
+
USDT: { address: "0x55d398326f99059fF775485246999027B3197955", decimals: 18 },
|
|
4089
|
+
USDC: { address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", decimals: 18 },
|
|
4090
|
+
BUSD: { address: "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", decimals: 18 },
|
|
4091
|
+
DAI: { address: "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3", decimals: 18 },
|
|
4092
|
+
WBNB: { address: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", decimals: 18 },
|
|
4093
|
+
ETH: { address: "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", decimals: 18 },
|
|
4094
|
+
TWT: { address: "0x4B0F1812e5Df2A09796481Ff14017e6005508003", decimals: 18 }
|
|
3984
4095
|
},
|
|
3985
4096
|
base: {
|
|
3986
|
-
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
3987
|
-
USDbC: "0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA",
|
|
3988
|
-
DAI: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
|
|
3989
|
-
WETH: "0x4200000000000000000000000000000000000006"
|
|
4097
|
+
USDC: { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", decimals: 6 },
|
|
4098
|
+
USDbC: { address: "0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA", decimals: 6 },
|
|
4099
|
+
DAI: { address: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb", decimals: 18 },
|
|
4100
|
+
WETH: { address: "0x4200000000000000000000000000000000000006", decimals: 18 }
|
|
3990
4101
|
},
|
|
3991
4102
|
avalanche: {
|
|
3992
|
-
USDT: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
|
|
3993
|
-
USDC: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
3994
|
-
"USDC.e": "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664",
|
|
3995
|
-
DAI: "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70",
|
|
3996
|
-
WAVAX: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7"
|
|
4103
|
+
USDT: { address: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", decimals: 6 },
|
|
4104
|
+
USDC: { address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", decimals: 6 },
|
|
4105
|
+
"USDC.e": { address: "0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664", decimals: 6 },
|
|
4106
|
+
DAI: { address: "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70", decimals: 18 },
|
|
4107
|
+
WAVAX: { address: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", decimals: 18 }
|
|
3997
4108
|
},
|
|
3998
4109
|
optimism: {
|
|
3999
|
-
USDT: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58",
|
|
4000
|
-
USDC: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
4001
|
-
"USDC.e": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607",
|
|
4002
|
-
DAI: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
|
|
4003
|
-
WETH: "0x4200000000000000000000000000000000000006",
|
|
4004
|
-
OP: "0x4200000000000000000000000000000000000042"
|
|
4110
|
+
USDT: { address: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", decimals: 6 },
|
|
4111
|
+
USDC: { address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", decimals: 6 },
|
|
4112
|
+
"USDC.e": { address: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", decimals: 6 },
|
|
4113
|
+
DAI: { address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", decimals: 18 },
|
|
4114
|
+
WETH: { address: "0x4200000000000000000000000000000000000006", decimals: 18 },
|
|
4115
|
+
OP: { address: "0x4200000000000000000000000000000000000042", decimals: 18 }
|
|
4005
4116
|
},
|
|
4006
4117
|
tron: {
|
|
4007
|
-
USDT: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
|
|
4008
|
-
USDC: "TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8"
|
|
4118
|
+
USDT: { address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", decimals: 6 },
|
|
4119
|
+
USDC: { address: "TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8", decimals: 6 }
|
|
4009
4120
|
},
|
|
4010
4121
|
solana: {
|
|
4011
|
-
USDC: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
4012
|
-
USDT: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
|
|
4013
|
-
BONK: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
|
|
4014
|
-
JUP: "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN",
|
|
4015
|
-
WSOL: "So11111111111111111111111111111111111111112"
|
|
4122
|
+
USDC: { address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", decimals: 6 },
|
|
4123
|
+
USDT: { address: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", decimals: 6 },
|
|
4124
|
+
BONK: { address: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263", decimals: 5 },
|
|
4125
|
+
JUP: { address: "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN", decimals: 6 },
|
|
4126
|
+
WSOL: { address: "So11111111111111111111111111111111111111112", decimals: 9 }
|
|
4016
4127
|
}
|
|
4017
4128
|
};
|
|
4018
|
-
BSC_18_DECIMAL_TOKENS = /* @__PURE__ */ new Set(["USDT", "USDC", "BUSD"]);
|
|
4019
4129
|
NATIVE_SYMBOLS = /* @__PURE__ */ new Set([
|
|
4020
4130
|
"NATIVE",
|
|
4021
4131
|
"ETH",
|
|
@@ -4045,41 +4155,6 @@ var init_dist = __esm({
|
|
|
4045
4155
|
"DOGE"
|
|
4046
4156
|
// Bitcoin family
|
|
4047
4157
|
]);
|
|
4048
|
-
TOKEN_DECIMALS = {
|
|
4049
|
-
USDC: 6,
|
|
4050
|
-
USDT: 6,
|
|
4051
|
-
BUSD: 6,
|
|
4052
|
-
DAI: 18,
|
|
4053
|
-
ETH: 18,
|
|
4054
|
-
MATIC: 18,
|
|
4055
|
-
BNB: 18,
|
|
4056
|
-
AVAX: 18,
|
|
4057
|
-
FTM: 18,
|
|
4058
|
-
SOL: 9,
|
|
4059
|
-
S: 18,
|
|
4060
|
-
CELO: 18,
|
|
4061
|
-
WETH: 18,
|
|
4062
|
-
WMATIC: 18,
|
|
4063
|
-
WBNB: 18,
|
|
4064
|
-
WAVAX: 18,
|
|
4065
|
-
WBTC: 8,
|
|
4066
|
-
ARB: 18,
|
|
4067
|
-
OP: 18,
|
|
4068
|
-
// Non-EVM natives
|
|
4069
|
-
TRX: 6,
|
|
4070
|
-
APT: 8,
|
|
4071
|
-
TON: 9,
|
|
4072
|
-
SUI: 9,
|
|
4073
|
-
NEAR: 24,
|
|
4074
|
-
ATOM: 6,
|
|
4075
|
-
BTC: 8,
|
|
4076
|
-
LTC: 8,
|
|
4077
|
-
DOGE: 8,
|
|
4078
|
-
// Solana SPL tokens
|
|
4079
|
-
BONK: 5,
|
|
4080
|
-
JUP: 6,
|
|
4081
|
-
WSOL: 9
|
|
4082
|
-
};
|
|
4083
4158
|
CELO_NATIVE_ADDRESS = "0x471EcE3750Da237f93B8E339c536989b8978a438";
|
|
4084
4159
|
TwakError = class extends Error {
|
|
4085
4160
|
constructor(message, code2, details) {
|
|
@@ -4266,9 +4341,17 @@ var init_dist = __esm({
|
|
|
4266
4341
|
this.name = "X402Error";
|
|
4267
4342
|
}
|
|
4268
4343
|
};
|
|
4344
|
+
ALIASES = {
|
|
4345
|
+
"binance-smart-chain": "bsc",
|
|
4346
|
+
matic: "polygon",
|
|
4347
|
+
"arbitrum-one": "arbitrum",
|
|
4348
|
+
op: "optimism"
|
|
4349
|
+
};
|
|
4269
4350
|
DECIMAL_AMOUNT_RE = /^(?:0|[1-9]\d*)(?:\.\d+)?$/;
|
|
4270
4351
|
UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
4271
4352
|
NAMING_API = "https://naming.trustwallet.com/v1/lookup";
|
|
4353
|
+
EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
|
|
4354
|
+
ERC20_TRANSFER_SIG = "0xa9059cbb";
|
|
4272
4355
|
DEFAULT_GATEWAY_URL = "https://tws.trustwallet.com";
|
|
4273
4356
|
DEFAULT_MARKET_URL = "https://market.trustwallet.com";
|
|
4274
4357
|
DEFAULT_TIMEOUT = 3e4;
|
|
@@ -4430,7 +4513,7 @@ function getPassword(service, account) {
|
|
|
4430
4513
|
throw err;
|
|
4431
4514
|
}
|
|
4432
4515
|
}
|
|
4433
|
-
var fs, path, os, crypto2, crypto22, import_keyring, crypto3, crypto4, FileStore, ALGORITHM, KEY_LENGTH, IV_LENGTH, SALT_LENGTH, ITERATIONS, SERVICE, WALLET_ACCOUNT, API_SECRET_ACCOUNT, PROBE_ACCOUNT, KeychainManager, CREDENTIALS_FILE, CredentialManager, WALLET_FILE, WalletConfigStore, ALERTS_FILE, AlertStore, AUTOMATIONS_FILE, AutomationStore,
|
|
4516
|
+
var fs, path, os, crypto2, crypto22, import_keyring, crypto3, crypto4, FileStore, ALGORITHM, KEY_LENGTH, IV_LENGTH, SALT_LENGTH, ITERATIONS, SERVICE, WALLET_ACCOUNT, API_SECRET_ACCOUNT, PROBE_ACCOUNT, KeychainManager, CREDENTIALS_FILE, CredentialManager, WALLET_FILE, WalletConfigStore, ALERTS_FILE, AlertStore, AUTOMATIONS_FILE, AutomationStore, ASSET_CACHE_FILE, AssetCache;
|
|
4434
4517
|
var init_dist2 = __esm({
|
|
4435
4518
|
"../storage/dist/index.mjs"() {
|
|
4436
4519
|
"use strict";
|
|
@@ -4731,34 +4814,6 @@ var init_dist2 = __esm({
|
|
|
4731
4814
|
return automations.filter((a3) => a3.active);
|
|
4732
4815
|
}
|
|
4733
4816
|
};
|
|
4734
|
-
SEEN_HASHES_FILE = "seen-hashes.json";
|
|
4735
|
-
SeenHashStore = class {
|
|
4736
|
-
store;
|
|
4737
|
-
// In-memory cache prevents duplicate acceptance within the same process
|
|
4738
|
-
memCache = /* @__PURE__ */ new Set();
|
|
4739
|
-
constructor(config) {
|
|
4740
|
-
this.store = new FileStore(config);
|
|
4741
|
-
}
|
|
4742
|
-
async hasSeen(txHash) {
|
|
4743
|
-
const normalized = txHash.toLowerCase();
|
|
4744
|
-
if (this.memCache.has(normalized)) return true;
|
|
4745
|
-
const records = await this.list();
|
|
4746
|
-
return records.some((r3) => r3.txHash === normalized);
|
|
4747
|
-
}
|
|
4748
|
-
async markSeen(txHash, chainId) {
|
|
4749
|
-
const normalized = txHash.toLowerCase();
|
|
4750
|
-
this.memCache.add(normalized);
|
|
4751
|
-
const records = await this.list();
|
|
4752
|
-
records.push({ txHash: normalized, chainId, usedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4753
|
-
await this.store.write(SEEN_HASHES_FILE, records);
|
|
4754
|
-
}
|
|
4755
|
-
async list() {
|
|
4756
|
-
return await this.store.read(SEEN_HASHES_FILE) || [];
|
|
4757
|
-
}
|
|
4758
|
-
async clear() {
|
|
4759
|
-
await this.store.remove(SEEN_HASHES_FILE);
|
|
4760
|
-
}
|
|
4761
|
-
};
|
|
4762
4817
|
ASSET_CACHE_FILE = "asset-cache.json";
|
|
4763
4818
|
AssetCache = class {
|
|
4764
4819
|
store;
|
|
@@ -6352,6 +6407,34 @@ var require_source = __commonJS({
|
|
|
6352
6407
|
});
|
|
6353
6408
|
|
|
6354
6409
|
// ../wallet-core/dist/index.mjs
|
|
6410
|
+
var dist_exports2 = {};
|
|
6411
|
+
__export(dist_exports2, {
|
|
6412
|
+
AgentWalletProvider: () => AgentWalletProvider,
|
|
6413
|
+
BlockchainClient: () => BlockchainClient,
|
|
6414
|
+
TeeWalletProvider: () => TeeWalletProvider,
|
|
6415
|
+
VerifyCredentialsError: () => VerifyCredentialsError,
|
|
6416
|
+
WalletContext: () => WalletContext,
|
|
6417
|
+
WalletCoreSigner: () => WalletCoreSigner,
|
|
6418
|
+
WalletProvider: () => WalletProvider,
|
|
6419
|
+
_setWalletCore: () => _setWalletCore,
|
|
6420
|
+
applyPolyfills: () => applyPolyfills,
|
|
6421
|
+
buildCoinTypeMap: () => buildCoinTypeMap,
|
|
6422
|
+
chainKeyToDefinedChain: () => chainKeyToDefinedChain,
|
|
6423
|
+
createPendingTransaction: () => createPendingTransaction,
|
|
6424
|
+
getAmberDomainsByCoinId: () => getAmberDomainsByCoinId,
|
|
6425
|
+
getCoinType: () => getCoinType,
|
|
6426
|
+
getWalletCore: () => getWalletCore,
|
|
6427
|
+
humanAmountToWei: () => humanAmountToWei,
|
|
6428
|
+
initTrustSDK: () => initTrustSDK,
|
|
6429
|
+
isInitialized: () => isInitialized,
|
|
6430
|
+
patchNaasSession: () => patchNaasSession,
|
|
6431
|
+
patchNodeStubs: () => patchNodeStubs,
|
|
6432
|
+
registerDeviceAndWallet: () => registerDeviceAndWallet,
|
|
6433
|
+
registerShutdownHandlers: () => registerShutdownHandlers,
|
|
6434
|
+
setPolyfillCredentials: () => setPolyfillCredentials,
|
|
6435
|
+
setPolyfillNaasSessionId: () => setPolyfillNaasSessionId,
|
|
6436
|
+
verifyCredentials: () => verifyCredentials
|
|
6437
|
+
});
|
|
6355
6438
|
function setPolyfillCredentials(creds) {
|
|
6356
6439
|
_credentials = creds;
|
|
6357
6440
|
}
|
|
@@ -6590,41 +6673,60 @@ function patchNodeStubs() {
|
|
|
6590
6673
|
} catch {
|
|
6591
6674
|
}
|
|
6592
6675
|
if (parsedUrl?.hostname === "tws.trustwallet.com" && parsedUrl.pathname === "/v2/devices/fingerprint") {
|
|
6593
|
-
return Promise.resolve(
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6676
|
+
return Promise.resolve(
|
|
6677
|
+
new Response(
|
|
6678
|
+
JSON.stringify({
|
|
6679
|
+
device_id: _credentials?.accessId || "twak-cli",
|
|
6680
|
+
hash_key: _credentials?.hmacSecret || ""
|
|
6681
|
+
}),
|
|
6682
|
+
{
|
|
6683
|
+
status: 200,
|
|
6684
|
+
headers: { "content-type": "application/json" }
|
|
6685
|
+
}
|
|
6686
|
+
)
|
|
6687
|
+
);
|
|
6600
6688
|
}
|
|
6601
6689
|
if (url.includes("/v2/projects/") && url.includes("/nodes")) {
|
|
6602
|
-
return Promise.resolve(
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6690
|
+
return Promise.resolve(
|
|
6691
|
+
new Response(
|
|
6692
|
+
JSON.stringify({
|
|
6693
|
+
message: "ok",
|
|
6694
|
+
code: 200,
|
|
6695
|
+
data: [
|
|
6696
|
+
{
|
|
6697
|
+
network: "solana",
|
|
6698
|
+
name: "solana",
|
|
6699
|
+
nodes: [{ url: "https://solana.twnodes.com", weight: "high" }]
|
|
6700
|
+
}
|
|
6701
|
+
]
|
|
6702
|
+
}),
|
|
6703
|
+
{
|
|
6704
|
+
status: 200,
|
|
6705
|
+
headers: { "content-type": "application/json" }
|
|
6706
|
+
}
|
|
6707
|
+
)
|
|
6708
|
+
);
|
|
6612
6709
|
}
|
|
6613
6710
|
if (url.includes("/btc-indexer/inscribed/") && url.includes("/available-balance")) {
|
|
6614
6711
|
const addrMatch = url.match(/\/address\/([^/]+)\//);
|
|
6615
6712
|
const addr = addrMatch?.[1] ?? "";
|
|
6616
|
-
return Promise.resolve(
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6713
|
+
return Promise.resolve(
|
|
6714
|
+
new Response(
|
|
6715
|
+
JSON.stringify({
|
|
6716
|
+
address: addr,
|
|
6717
|
+
balance: "0",
|
|
6718
|
+
totalReceived: "0",
|
|
6719
|
+
totalSent: "0",
|
|
6720
|
+
unconfirmedBalance: "0",
|
|
6721
|
+
unconfirmedTxs: 0,
|
|
6722
|
+
txs: 0
|
|
6723
|
+
}),
|
|
6724
|
+
{
|
|
6725
|
+
status: 200,
|
|
6726
|
+
headers: { "content-type": "application/json" }
|
|
6727
|
+
}
|
|
6728
|
+
)
|
|
6729
|
+
);
|
|
6628
6730
|
}
|
|
6629
6731
|
let resolvedUrl = url;
|
|
6630
6732
|
if (_naasSessionId && url.includes(".twnodes.com") && !url.includes("/naas/session/")) {
|
|
@@ -6659,11 +6761,11 @@ function patchNaasSession(sessionId) {
|
|
|
6659
6761
|
};
|
|
6660
6762
|
}
|
|
6661
6763
|
function buildCoinTypeMap() {
|
|
6662
|
-
const
|
|
6764
|
+
const wc3 = getWalletCore();
|
|
6663
6765
|
coinTypeByValue = /* @__PURE__ */ new Map();
|
|
6664
|
-
for (const key of Object.keys(
|
|
6766
|
+
for (const key of Object.keys(wc3.CoinType)) {
|
|
6665
6767
|
if (key === "values") continue;
|
|
6666
|
-
const ct5 =
|
|
6768
|
+
const ct5 = wc3.CoinType[key];
|
|
6667
6769
|
if (ct5 && typeof ct5 === "object" && typeof ct5.value === "number") {
|
|
6668
6770
|
coinTypeByValue.set(ct5.value, ct5);
|
|
6669
6771
|
}
|
|
@@ -6722,9 +6824,26 @@ function getWalletCore() {
|
|
|
6722
6824
|
function isInitialized() {
|
|
6723
6825
|
return initialized;
|
|
6724
6826
|
}
|
|
6725
|
-
function _setWalletCore(
|
|
6726
|
-
walletCore =
|
|
6727
|
-
initialized =
|
|
6827
|
+
function _setWalletCore(wc3) {
|
|
6828
|
+
walletCore = wc3;
|
|
6829
|
+
initialized = wc3 !== null;
|
|
6830
|
+
}
|
|
6831
|
+
function chainKeyToDefinedChain(chainKey) {
|
|
6832
|
+
let coinId;
|
|
6833
|
+
let canonicalKey;
|
|
6834
|
+
try {
|
|
6835
|
+
({ coinId, chainKey: canonicalKey } = resolveAsset(chainKey));
|
|
6836
|
+
} catch (err) {
|
|
6837
|
+
if (err instanceof WalletError) {
|
|
6838
|
+
throw new TwakValidationError(err.message, { chainKey });
|
|
6839
|
+
}
|
|
6840
|
+
throw err;
|
|
6841
|
+
}
|
|
6842
|
+
const ct5 = getCoinType(coinId);
|
|
6843
|
+
if (!ct5) {
|
|
6844
|
+
throw new TwakValidationError(`No CoinType mapping for coinId ${coinId}`, { chainKey, coinId });
|
|
6845
|
+
}
|
|
6846
|
+
return { chain: new import_trust_sdk3.DefinedChain(ct5), chainConfig: CHAINS[canonicalKey] };
|
|
6728
6847
|
}
|
|
6729
6848
|
function humanAmountToWei(amount, decimals = 18) {
|
|
6730
6849
|
const parts = amount.split(".");
|
|
@@ -6733,6 +6852,13 @@ function humanAmountToWei(amount, decimals = 18) {
|
|
|
6733
6852
|
const frac = parts[1].padEnd(decimals, "0").slice(0, decimals);
|
|
6734
6853
|
return whole * 10n ** BigInt(decimals) + BigInt(frac);
|
|
6735
6854
|
}
|
|
6855
|
+
function createPendingTransaction(client, chainKey, txHash) {
|
|
6856
|
+
return {
|
|
6857
|
+
txHash,
|
|
6858
|
+
chainKey,
|
|
6859
|
+
wait: (opts) => client.waitForReceipt(chainKey, txHash, opts)
|
|
6860
|
+
};
|
|
6861
|
+
}
|
|
6736
6862
|
function requireCoinType(coinId) {
|
|
6737
6863
|
const ct5 = getCoinType(coinId);
|
|
6738
6864
|
if (!ct5) {
|
|
@@ -6742,9 +6868,9 @@ function requireCoinType(coinId) {
|
|
|
6742
6868
|
}
|
|
6743
6869
|
function getCoinDecimals(coinId) {
|
|
6744
6870
|
try {
|
|
6745
|
-
const
|
|
6871
|
+
const wc3 = getWalletCore();
|
|
6746
6872
|
const ct5 = requireCoinType(coinId);
|
|
6747
|
-
return
|
|
6873
|
+
return wc3.CoinTypeConfiguration.getDecimals(ct5);
|
|
6748
6874
|
} catch {
|
|
6749
6875
|
const chain2 = getChainByCoinId(coinId);
|
|
6750
6876
|
return chain2?.decimals ?? 18;
|
|
@@ -6787,10 +6913,39 @@ function tokenTypeForCoinId(coinId) {
|
|
|
6787
6913
|
return import_trust_sdk2.TokenType.Erc20;
|
|
6788
6914
|
}
|
|
6789
6915
|
}
|
|
6916
|
+
async function signTransactionBytes(transaction, fee, nonce, hdWallet, ct5) {
|
|
6917
|
+
try {
|
|
6918
|
+
const signingResult = await import_trust_sdk2.BlockchainService.signWithHDWallet(
|
|
6919
|
+
transaction,
|
|
6920
|
+
fee,
|
|
6921
|
+
nonce,
|
|
6922
|
+
hdWallet
|
|
6923
|
+
);
|
|
6924
|
+
return signingResult.output[0];
|
|
6925
|
+
} catch (err) {
|
|
6926
|
+
if (!(err instanceof Error) || !err.message.includes("An operation is not implemented"))
|
|
6927
|
+
throw err;
|
|
6928
|
+
const privateKey = hdWallet.getKeyForCoin(ct5);
|
|
6929
|
+
try {
|
|
6930
|
+
const signingResult = await import_trust_sdk2.BlockchainService.signWithNonce(
|
|
6931
|
+
transaction,
|
|
6932
|
+
fee,
|
|
6933
|
+
nonce,
|
|
6934
|
+
privateKey
|
|
6935
|
+
);
|
|
6936
|
+
return signingResult.output[0];
|
|
6937
|
+
} finally {
|
|
6938
|
+
try {
|
|
6939
|
+
privateKey.data().fill(0);
|
|
6940
|
+
} catch {
|
|
6941
|
+
}
|
|
6942
|
+
}
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6790
6945
|
async function getAmberDomainsByCoinId() {
|
|
6791
6946
|
if (domainsByCoinId) return domainsByCoinId;
|
|
6792
6947
|
try {
|
|
6793
|
-
const domains = await
|
|
6948
|
+
const domains = await import_trust_sdk5.AmberService.getDomains();
|
|
6794
6949
|
domainsByCoinId = new Map(
|
|
6795
6950
|
domains.map((d3) => [d3.coin.value, d3.id])
|
|
6796
6951
|
);
|
|
@@ -6890,7 +7045,7 @@ function registerShutdownHandlers(providers, onBeforeExit) {
|
|
|
6890
7045
|
process.removeListener("SIGTERM", handler);
|
|
6891
7046
|
};
|
|
6892
7047
|
}
|
|
6893
|
-
var fs2, path2, import_trust_sdk, import_trust_sdk2, import_trust_sdk3, import_trust_sdk4, import_crypto2, __require, polyfillsApplied, _credentials, _naasSessionId, nodeStubsPatched, NAAS_SESSION_RE, naasSessionPatched, coinTypeByValue, walletCore, initialized, SPONSORED_RPC, BlockchainClient, WalletCoreSigner, domainsByCoinId, GATEWAY_URL, DEVICE_INFO_BODY, VerifyCredentialsError, WALLET_ID_SUFFIX, WALLET_ID_LENGTH, EVM_COIN_IDS, MAINTAINED_COIN_IDS, WalletProvider, AgentWalletProvider, WalletContext, SHUTDOWN_TIMEOUT_MS;
|
|
7048
|
+
var fs2, path2, import_trust_sdk, import_trust_sdk2, import_trust_sdk3, import_trust_sdk4, import_trust_sdk5, import_crypto2, __require, polyfillsApplied, _credentials, _naasSessionId, nodeStubsPatched, NAAS_SESSION_RE, naasSessionPatched, coinTypeByValue, walletCore, initialized, SPONSORED_RPC, RPC_TIMEOUT_MS, RECEIPT_POLL_INTERVAL_MS, RECEIPT_DEFAULT_TIMEOUT_MS, BlockchainClient, WalletCoreSigner, domainsByCoinId, GATEWAY_URL, DEVICE_INFO_BODY, VerifyCredentialsError, WALLET_ID_SUFFIX, WALLET_ID_LENGTH, EVM_COIN_IDS, MAINTAINED_COIN_IDS, WalletProvider, AgentWalletProvider, TeeWalletProvider, WalletContext, SHUTDOWN_TIMEOUT_MS;
|
|
6894
7049
|
var init_dist3 = __esm({
|
|
6895
7050
|
"../wallet-core/dist/index.mjs"() {
|
|
6896
7051
|
"use strict";
|
|
@@ -6899,9 +7054,11 @@ var init_dist3 = __esm({
|
|
|
6899
7054
|
import_trust_sdk = require("@trustwallet-trust-sdk/trust-sdk");
|
|
6900
7055
|
import_trust_sdk2 = require("@trustwallet-trust-sdk/trust-sdk");
|
|
6901
7056
|
init_dist();
|
|
6902
|
-
init_dist();
|
|
6903
7057
|
import_trust_sdk3 = require("@trustwallet-trust-sdk/trust-sdk");
|
|
7058
|
+
init_dist();
|
|
7059
|
+
init_dist();
|
|
6904
7060
|
import_trust_sdk4 = require("@trustwallet-trust-sdk/trust-sdk");
|
|
7061
|
+
import_trust_sdk5 = require("@trustwallet-trust-sdk/trust-sdk");
|
|
6905
7062
|
init_dist();
|
|
6906
7063
|
import_crypto2 = require("crypto");
|
|
6907
7064
|
init_dist();
|
|
@@ -6918,6 +7075,7 @@ var init_dist3 = __esm({
|
|
|
6918
7075
|
nodeStubsPatched = false;
|
|
6919
7076
|
NAAS_SESSION_RE = /\/naas\/session\/[A-Za-z0-9+/_-]+=*\//;
|
|
6920
7077
|
naasSessionPatched = false;
|
|
7078
|
+
applyPolyfills();
|
|
6921
7079
|
coinTypeByValue = null;
|
|
6922
7080
|
walletCore = null;
|
|
6923
7081
|
initialized = false;
|
|
@@ -6925,6 +7083,9 @@ var init_dist3 = __esm({
|
|
|
6925
7083
|
20000714: "https://bsc-sponsored.twnodes.com",
|
|
6926
7084
|
60: "https://ethereum-sponsored.twnodes.com"
|
|
6927
7085
|
};
|
|
7086
|
+
RPC_TIMEOUT_MS = 15e3;
|
|
7087
|
+
RECEIPT_POLL_INTERVAL_MS = 1500;
|
|
7088
|
+
RECEIPT_DEFAULT_TIMEOUT_MS = 6e4;
|
|
6928
7089
|
BlockchainClient = class _BlockchainClient {
|
|
6929
7090
|
signer;
|
|
6930
7091
|
constructor(signer) {
|
|
@@ -7065,97 +7226,9 @@ var init_dist3 = __esm({
|
|
|
7065
7226
|
);
|
|
7066
7227
|
if (transaction7702) transaction = transaction7702;
|
|
7067
7228
|
const nonce = await import_trust_sdk2.BlockchainService.fetchNonce(account);
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
const signingResult = await import_trust_sdk2.BlockchainService.signWithHDWallet(
|
|
7071
|
-
transaction,
|
|
7072
|
-
fee,
|
|
7073
|
-
nonce,
|
|
7074
|
-
params.hdWallet
|
|
7075
|
-
);
|
|
7076
|
-
encodedTx = signingResult.output[0];
|
|
7077
|
-
} catch (err) {
|
|
7078
|
-
if (!(err instanceof Error) || !err.message.includes("An operation is not implemented"))
|
|
7079
|
-
throw err;
|
|
7080
|
-
const privateKey = params.hdWallet.getKeyForCoin(ct5);
|
|
7081
|
-
try {
|
|
7082
|
-
const signingResult = await import_trust_sdk2.BlockchainService.signWithNonce(
|
|
7083
|
-
transaction,
|
|
7084
|
-
fee,
|
|
7085
|
-
nonce,
|
|
7086
|
-
privateKey
|
|
7087
|
-
);
|
|
7088
|
-
encodedTx = signingResult.output[0];
|
|
7089
|
-
} finally {
|
|
7090
|
-
try {
|
|
7091
|
-
privateKey.data().fill(0);
|
|
7092
|
-
} catch {
|
|
7093
|
-
}
|
|
7094
|
-
}
|
|
7095
|
-
}
|
|
7096
|
-
const hash = await this.sendTransactionWithSponsorship(chain2, encodedTx, coinId);
|
|
7097
|
-
return { hash, chainKey };
|
|
7098
|
-
}
|
|
7099
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7100
|
-
async approve(params) {
|
|
7101
|
-
const { coinId, contractAddress } = parseTokenId(params.assetId);
|
|
7102
|
-
const chainKey = coinTypeToChainKey(coinId);
|
|
7103
|
-
if (!chainKey) {
|
|
7104
|
-
throw new Error(`Unsupported coin type: ${coinId}`);
|
|
7105
|
-
}
|
|
7106
|
-
if (!contractAddress) {
|
|
7107
|
-
throw new Error("Cannot approve native token");
|
|
7108
|
-
}
|
|
7109
|
-
const ct5 = requireCoinType(coinId);
|
|
7110
|
-
const chain2 = new import_trust_sdk2.DefinedChain(ct5);
|
|
7111
|
-
const account = this.getAccountFromSigner(chainKey);
|
|
7112
|
-
const asset = new import_trust_sdk2.Asset.Token(account, contractAddress, tokenTypeForCoinId(coinId));
|
|
7113
|
-
const approveData = this.encodeApproveCalldata(params.spender, params.amount);
|
|
7114
|
-
let transaction = new import_trust_sdk2.TransactionSmartContract(
|
|
7115
|
-
asset,
|
|
7116
|
-
contractAddress,
|
|
7117
|
-
"0",
|
|
7118
|
-
approveData
|
|
7119
|
-
);
|
|
7120
|
-
const { fee, transaction7702 } = await this.calculateFeeWithSponsorship(
|
|
7121
|
-
transaction,
|
|
7122
|
-
account,
|
|
7123
|
-
coinId
|
|
7124
|
-
);
|
|
7125
|
-
if (transaction7702) transaction = transaction7702;
|
|
7126
|
-
const nonce = await import_trust_sdk2.BlockchainService.fetchNonce(account);
|
|
7127
|
-
let encodedTx;
|
|
7128
|
-
try {
|
|
7129
|
-
const signingResult = await import_trust_sdk2.BlockchainService.signWithHDWallet(
|
|
7130
|
-
transaction,
|
|
7131
|
-
fee,
|
|
7132
|
-
nonce,
|
|
7133
|
-
params.hdWallet
|
|
7134
|
-
);
|
|
7135
|
-
encodedTx = signingResult.output[0];
|
|
7136
|
-
} catch (err) {
|
|
7137
|
-
if (!(err instanceof Error) || !err.message.includes("An operation is not implemented"))
|
|
7138
|
-
throw err;
|
|
7139
|
-
const privateKey = params.hdWallet.getKeyForCoin(ct5);
|
|
7140
|
-
try {
|
|
7141
|
-
const signingResult = await import_trust_sdk2.BlockchainService.signWithNonce(
|
|
7142
|
-
transaction,
|
|
7143
|
-
fee,
|
|
7144
|
-
nonce,
|
|
7145
|
-
privateKey
|
|
7146
|
-
);
|
|
7147
|
-
encodedTx = signingResult.output[0];
|
|
7148
|
-
} finally {
|
|
7149
|
-
try {
|
|
7150
|
-
privateKey.data().fill(0);
|
|
7151
|
-
} catch {
|
|
7152
|
-
}
|
|
7153
|
-
}
|
|
7154
|
-
}
|
|
7155
|
-
const hash = await this.sendTransactionWithSponsorship(chain2, encodedTx, coinId);
|
|
7156
|
-
return { hash, chainKey };
|
|
7229
|
+
const encodedTx = await signTransactionBytes(transaction, fee, nonce, params.hdWallet, ct5);
|
|
7230
|
+
return this.sendTransactionWithSponsorship(chain2, encodedTx, coinId, chainKey);
|
|
7157
7231
|
}
|
|
7158
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7159
7232
|
async executeContract(params) {
|
|
7160
7233
|
const { coinId } = parseTokenId(params.assetId);
|
|
7161
7234
|
const chainKey = coinTypeToChainKey(coinId);
|
|
@@ -7180,74 +7253,67 @@ var init_dist3 = __esm({
|
|
|
7180
7253
|
);
|
|
7181
7254
|
if (transaction7702) transaction = transaction7702;
|
|
7182
7255
|
const nonce = await import_trust_sdk2.BlockchainService.fetchNonce(account);
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7256
|
+
const encodedTx = await signTransactionBytes(transaction, fee, nonce, params.hdWallet, ct5);
|
|
7257
|
+
return this.sendTransactionWithSponsorship(chain2, encodedTx, coinId, chainKey);
|
|
7258
|
+
}
|
|
7259
|
+
/**
|
|
7260
|
+
* Execute a read-only EVM `eth_call` against the chain's default RPC node.
|
|
7261
|
+
* Returns the raw hex result string from the JSON-RPC envelope, or `'0x'`
|
|
7262
|
+
* when the node omits a result. Errors surface as TwakNetworkError /
|
|
7263
|
+
* TwakValidationError.
|
|
7264
|
+
*/
|
|
7265
|
+
async ethCall(chainKey, to3, data) {
|
|
7266
|
+
const rpcUrl = await this.resolveRpcUrl(chainKey);
|
|
7267
|
+
const result = await this.postJsonRpc(rpcUrl, "eth_call", [{ to: to3, data }, "latest"]);
|
|
7268
|
+
return result ?? "0x";
|
|
7269
|
+
}
|
|
7270
|
+
/**
|
|
7271
|
+
* Poll `eth_getTransactionReceipt` until the transaction is mined or the
|
|
7272
|
+
* timeout elapses. Defaults come from the chain's `receiptPollMs` /
|
|
7273
|
+
* `receiptTimeoutMs` config when set, otherwise the global constants
|
|
7274
|
+
* (1.5s poll, 60s timeout). Caller-supplied opts override both.
|
|
7275
|
+
*/
|
|
7276
|
+
async waitForReceipt(chainKey, txHash, opts) {
|
|
7277
|
+
const { chain: chain2, chainConfig } = chainKeyToDefinedChain(chainKey);
|
|
7278
|
+
if (chainConfig?.namespace && chainConfig.namespace !== "eip155") {
|
|
7279
|
+
throw new TwakValidationError(
|
|
7280
|
+
`waitForReceipt is EVM-only; ${chainKey} is namespace ${chainConfig.namespace}`,
|
|
7281
|
+
{ chainKey, namespace: chainConfig.namespace }
|
|
7190
7282
|
);
|
|
7191
|
-
encodedTx = signingResult.output[0];
|
|
7192
|
-
} catch (err) {
|
|
7193
|
-
if (!(err instanceof Error) || !err.message.includes("An operation is not implemented"))
|
|
7194
|
-
throw err;
|
|
7195
|
-
const privateKey = params.hdWallet.getKeyForCoin(ct5);
|
|
7196
|
-
try {
|
|
7197
|
-
const signingResult = await import_trust_sdk2.BlockchainService.signWithNonce(
|
|
7198
|
-
transaction,
|
|
7199
|
-
fee,
|
|
7200
|
-
nonce,
|
|
7201
|
-
privateKey
|
|
7202
|
-
);
|
|
7203
|
-
encodedTx = signingResult.output[0];
|
|
7204
|
-
} finally {
|
|
7205
|
-
try {
|
|
7206
|
-
privateKey.data().fill(0);
|
|
7207
|
-
} catch {
|
|
7208
|
-
}
|
|
7209
|
-
}
|
|
7210
7283
|
}
|
|
7211
|
-
const
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
if (!chainKey) throw new Error(`Unsupported coin type: ${coinId}`);
|
|
7219
|
-
const chainDef = getChainByCoinId(coinId);
|
|
7220
|
-
if (!chainDef?.isEVM) throw new Error(`Allowance is only supported on EVM chains`);
|
|
7221
|
-
const ct5 = requireCoinType(coinId);
|
|
7222
|
-
const chain2 = new import_trust_sdk2.DefinedChain(ct5);
|
|
7223
|
-
const fnSelector = "dd62ed3e";
|
|
7224
|
-
const paddedOwner = params.owner.replace("0x", "").padStart(64, "0");
|
|
7225
|
-
const paddedSpender = params.spender.replace("0x", "").padStart(64, "0");
|
|
7226
|
-
const calldata = "0x" + fnSelector + paddedOwner + paddedSpender;
|
|
7227
|
-
const nodes = await import_trust_sdk2.BlockchainService.getNodes(chain2);
|
|
7228
|
-
const rpcUrl = nodes[0]?.url;
|
|
7229
|
-
if (!rpcUrl) throw new Error(`No RPC node available for ${chainKey}`);
|
|
7230
|
-
const response = await fetch(rpcUrl, {
|
|
7231
|
-
method: "POST",
|
|
7232
|
-
headers: { "Content-Type": "application/json" },
|
|
7233
|
-
body: JSON.stringify({
|
|
7234
|
-
jsonrpc: "2.0",
|
|
7235
|
-
id: 1,
|
|
7236
|
-
method: "eth_call",
|
|
7237
|
-
params: [{ to: contractAddress, data: calldata }, "latest"]
|
|
7238
|
-
}),
|
|
7239
|
-
signal: AbortSignal.timeout(15e3)
|
|
7240
|
-
});
|
|
7241
|
-
if (!response.ok) {
|
|
7242
|
-
const body = await response.text().catch(() => "");
|
|
7243
|
-
throw new Error(`RPC HTTP error ${response.status}: ${body.slice(0, 500)}`);
|
|
7284
|
+
const timeoutMs = opts?.timeoutMs ?? chainConfig?.receiptTimeoutMs ?? RECEIPT_DEFAULT_TIMEOUT_MS;
|
|
7285
|
+
const pollMs = opts?.pollMs ?? chainConfig?.receiptPollMs ?? RECEIPT_POLL_INTERVAL_MS;
|
|
7286
|
+
if (timeoutMs <= 0) {
|
|
7287
|
+
throw new TwakValidationError(`waitForReceipt: timeoutMs must be > 0 (got ${timeoutMs})`, {
|
|
7288
|
+
chainKey,
|
|
7289
|
+
timeoutMs
|
|
7290
|
+
});
|
|
7244
7291
|
}
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7292
|
+
if (pollMs <= 0) {
|
|
7293
|
+
throw new TwakValidationError(`waitForReceipt: pollMs must be > 0 (got ${pollMs})`, {
|
|
7294
|
+
chainKey,
|
|
7295
|
+
pollMs
|
|
7296
|
+
});
|
|
7297
|
+
}
|
|
7298
|
+
const rpcUrl = await this.rpcUrlForChain(chain2, chainKey);
|
|
7299
|
+
const deadline = Date.now() + timeoutMs;
|
|
7300
|
+
while (Date.now() < deadline) {
|
|
7301
|
+
const remainingMs = Math.max(1, deadline - Date.now());
|
|
7302
|
+
const result = await this.postJsonRpc(rpcUrl, "eth_getTransactionReceipt", [txHash], {
|
|
7303
|
+
timeoutMs: Math.min(RPC_TIMEOUT_MS, remainingMs)
|
|
7304
|
+
});
|
|
7305
|
+
const receipt = result ?? null;
|
|
7306
|
+
if (receipt) return receipt;
|
|
7307
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollMs));
|
|
7308
|
+
}
|
|
7309
|
+
throw new TwakNetworkError(`Timed out waiting for receipt ${txHash} on ${chainKey}`, {
|
|
7310
|
+
txHash,
|
|
7311
|
+
chainKey
|
|
7312
|
+
});
|
|
7313
|
+
}
|
|
7314
|
+
contract(def) {
|
|
7315
|
+
return def.using(this);
|
|
7249
7316
|
}
|
|
7250
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7251
7317
|
async transferRawSolana(params) {
|
|
7252
7318
|
const ct5 = requireCoinType(501);
|
|
7253
7319
|
const chain2 = new import_trust_sdk2.DefinedChain(ct5);
|
|
@@ -7268,31 +7334,9 @@ var init_dist3 = __esm({
|
|
|
7268
7334
|
);
|
|
7269
7335
|
const fee = await import_trust_sdk2.BlockchainService.calculateFee(trade);
|
|
7270
7336
|
const nonce = await import_trust_sdk2.BlockchainService.fetchNonce(account);
|
|
7271
|
-
|
|
7272
|
-
try {
|
|
7273
|
-
const signingResult = await import_trust_sdk2.BlockchainService.signWithHDWallet(
|
|
7274
|
-
trade,
|
|
7275
|
-
fee,
|
|
7276
|
-
nonce,
|
|
7277
|
-
params.hdWallet
|
|
7278
|
-
);
|
|
7279
|
-
encodedTx = signingResult.output[0];
|
|
7280
|
-
} catch (err) {
|
|
7281
|
-
if (!(err instanceof Error) || !err.message.includes("An operation is not implemented"))
|
|
7282
|
-
throw err;
|
|
7283
|
-
const privateKey = params.hdWallet.getKeyForCoin(ct5);
|
|
7284
|
-
try {
|
|
7285
|
-
const signingResult = await import_trust_sdk2.BlockchainService.signWithNonce(trade, fee, nonce, privateKey);
|
|
7286
|
-
encodedTx = signingResult.output[0];
|
|
7287
|
-
} finally {
|
|
7288
|
-
try {
|
|
7289
|
-
privateKey.data().fill(0);
|
|
7290
|
-
} catch {
|
|
7291
|
-
}
|
|
7292
|
-
}
|
|
7293
|
-
}
|
|
7337
|
+
const encodedTx = await signTransactionBytes(trade, fee, nonce, params.hdWallet, ct5);
|
|
7294
7338
|
const hash = await import_trust_sdk2.BlockchainService.sendTransactionV2(chain2, encodedTx);
|
|
7295
|
-
return
|
|
7339
|
+
return createPendingTransaction(this, "solana", hash);
|
|
7296
7340
|
}
|
|
7297
7341
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7298
7342
|
async signMessage(chainKey, message, privateKey) {
|
|
@@ -7410,7 +7454,7 @@ var init_dist3 = __esm({
|
|
|
7410
7454
|
* Try sending via sponsored RPC first (checks pm_isSponsorable, then
|
|
7411
7455
|
* eth_sendRawTransaction). Falls back to regular BlockchainService.
|
|
7412
7456
|
*/
|
|
7413
|
-
async sendTransactionWithSponsorship(chain2, encodedTx, coinId) {
|
|
7457
|
+
async sendTransactionWithSponsorship(chain2, encodedTx, coinId, chainKey) {
|
|
7414
7458
|
const sponsoredUrl = this.getSponsoredRpcUrl(coinId);
|
|
7415
7459
|
if (sponsoredUrl) {
|
|
7416
7460
|
try {
|
|
@@ -7422,23 +7466,17 @@ var init_dist3 = __esm({
|
|
|
7422
7466
|
rlpStart = offset;
|
|
7423
7467
|
}
|
|
7424
7468
|
const rawRlp = "0x" + Array.from(encodedTx.slice(rlpStart)).map((b3) => b3.toString(16).padStart(2, "0")).join("");
|
|
7425
|
-
const
|
|
7426
|
-
|
|
7427
|
-
headers: { "Content-Type": "application/json" },
|
|
7428
|
-
body: JSON.stringify({
|
|
7429
|
-
id: 0,
|
|
7430
|
-
jsonrpc: "2.0",
|
|
7431
|
-
method: "eth_sendRawTransaction",
|
|
7432
|
-
params: [rawRlp]
|
|
7433
|
-
}),
|
|
7434
|
-
signal: AbortSignal.timeout(3e4)
|
|
7469
|
+
const result = await this.postJsonRpc(sponsoredUrl, "eth_sendRawTransaction", [rawRlp], {
|
|
7470
|
+
timeoutMs: 3e4
|
|
7435
7471
|
});
|
|
7436
|
-
|
|
7437
|
-
|
|
7472
|
+
if (typeof result === "string") {
|
|
7473
|
+
return createPendingTransaction(this, chainKey, result);
|
|
7474
|
+
}
|
|
7438
7475
|
} catch {
|
|
7439
7476
|
}
|
|
7440
7477
|
}
|
|
7441
|
-
|
|
7478
|
+
const hash = await import_trust_sdk2.BlockchainService.sendTransactionV2(chain2, encodedTx);
|
|
7479
|
+
return createPendingTransaction(this, chainKey, hash);
|
|
7442
7480
|
}
|
|
7443
7481
|
// Known stablecoins to try as gas payment tokens when getSupportedTokens returns empty
|
|
7444
7482
|
static FALLBACK_GAS_TOKENS = {
|
|
@@ -7651,8 +7689,8 @@ var init_dist3 = __esm({
|
|
|
7651
7689
|
}
|
|
7652
7690
|
const ct5 = requireCoinType(coinId);
|
|
7653
7691
|
const chain2 = new import_trust_sdk2.DefinedChain(ct5);
|
|
7654
|
-
const
|
|
7655
|
-
const derivation = coinId === 0 && address.startsWith("bc1p") ?
|
|
7692
|
+
const wc3 = getWalletCore();
|
|
7693
|
+
const derivation = coinId === 0 && address.startsWith("bc1p") ? wc3.Derivation.bitcoinTaproot : coinId === 0 && address.startsWith("bc1") ? wc3.Derivation.bitcoinSegwit : wc3.Derivation.default;
|
|
7656
7694
|
return new import_trust_sdk2.Account(address, derivation, "", "", "", chain2);
|
|
7657
7695
|
}
|
|
7658
7696
|
async fetchBitcoinBalanceFromBlockbook(address) {
|
|
@@ -7666,11 +7704,61 @@ var init_dist3 = __esm({
|
|
|
7666
7704
|
const total = BigInt(confirmed) + BigInt(unconfirmed);
|
|
7667
7705
|
return { available: confirmed, total: total.toString() };
|
|
7668
7706
|
}
|
|
7669
|
-
|
|
7670
|
-
const
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7707
|
+
async resolveRpcUrl(chainKey) {
|
|
7708
|
+
const { chain: chain2 } = chainKeyToDefinedChain(chainKey);
|
|
7709
|
+
return this.rpcUrlForChain(chain2, chainKey);
|
|
7710
|
+
}
|
|
7711
|
+
async rpcUrlForChain(chain2, chainKey) {
|
|
7712
|
+
const nodes = await import_trust_sdk2.BlockchainService.getNodes(chain2);
|
|
7713
|
+
const rpcUrl = nodes[0]?.url;
|
|
7714
|
+
if (!rpcUrl) {
|
|
7715
|
+
throw new TwakNetworkError(`No RPC node available for ${chainKey}`, { chainKey });
|
|
7716
|
+
}
|
|
7717
|
+
return rpcUrl;
|
|
7718
|
+
}
|
|
7719
|
+
async postJsonRpc(rpcUrl, method, params, opts) {
|
|
7720
|
+
const timeoutMs = opts?.timeoutMs ?? RPC_TIMEOUT_MS;
|
|
7721
|
+
let response;
|
|
7722
|
+
try {
|
|
7723
|
+
response = await fetch(rpcUrl, {
|
|
7724
|
+
method: "POST",
|
|
7725
|
+
headers: { "Content-Type": "application/json" },
|
|
7726
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
7727
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
7728
|
+
});
|
|
7729
|
+
} catch (err) {
|
|
7730
|
+
throw new TwakNetworkError(
|
|
7731
|
+
`RPC fetch failed on ${method}: ${err instanceof Error ? err.message : String(err)}`,
|
|
7732
|
+
{ method, rpcUrl, timeoutMs }
|
|
7733
|
+
);
|
|
7734
|
+
}
|
|
7735
|
+
if (!response.ok) {
|
|
7736
|
+
const body = await response.text().catch(() => "");
|
|
7737
|
+
const truncated = body.slice(0, 500);
|
|
7738
|
+
throw new TwakNetworkError(`RPC HTTP error ${response.status} on ${method}: ${truncated}`, {
|
|
7739
|
+
status: response.status,
|
|
7740
|
+
method,
|
|
7741
|
+
rpcUrl,
|
|
7742
|
+
body: truncated
|
|
7743
|
+
});
|
|
7744
|
+
}
|
|
7745
|
+
let json;
|
|
7746
|
+
try {
|
|
7747
|
+
json = await response.json();
|
|
7748
|
+
} catch (err) {
|
|
7749
|
+
throw new TwakNetworkError(
|
|
7750
|
+
`RPC response JSON parse failed on ${method}: ${err instanceof Error ? err.message : String(err)}`,
|
|
7751
|
+
{ method, rpcUrl }
|
|
7752
|
+
);
|
|
7753
|
+
}
|
|
7754
|
+
if (json.error) {
|
|
7755
|
+
throw new TwakNetworkError(`RPC error on ${method}: ${json.error.message}`, {
|
|
7756
|
+
method,
|
|
7757
|
+
rpcUrl,
|
|
7758
|
+
rpcError: json.error
|
|
7759
|
+
});
|
|
7760
|
+
}
|
|
7761
|
+
return json.result;
|
|
7674
7762
|
}
|
|
7675
7763
|
};
|
|
7676
7764
|
WalletCoreSigner = class _WalletCoreSigner {
|
|
@@ -7705,8 +7793,8 @@ var init_dist3 = __esm({
|
|
|
7705
7793
|
if (!ct5) {
|
|
7706
7794
|
throw new Error(`No CoinType mapping for chain: ${chainKey} (coinId: ${coinId})`);
|
|
7707
7795
|
}
|
|
7708
|
-
const chain2 = new
|
|
7709
|
-
const accounts =
|
|
7796
|
+
const chain2 = new import_trust_sdk4.DefinedChain(ct5);
|
|
7797
|
+
const accounts = import_trust_sdk4.AccountManagementService.createDefaultAccountsHdWallet(chain2, this.hdWallet);
|
|
7710
7798
|
if (accounts.length === 0) {
|
|
7711
7799
|
throw new Error(`No accounts for chain: ${chainKey}`);
|
|
7712
7800
|
}
|
|
@@ -7725,8 +7813,8 @@ var init_dist3 = __esm({
|
|
|
7725
7813
|
if (!ct5) {
|
|
7726
7814
|
throw new Error(`No CoinType mapping for chain: ${chainKey} (coinId: ${coinId})`);
|
|
7727
7815
|
}
|
|
7728
|
-
const chain2 = new
|
|
7729
|
-
const accounts =
|
|
7816
|
+
const chain2 = new import_trust_sdk4.DefinedChain(ct5);
|
|
7817
|
+
const accounts = import_trust_sdk4.AccountManagementService.createDefaultAccountsHdWallet(chain2, this.hdWallet);
|
|
7730
7818
|
if (accounts.length === 0) {
|
|
7731
7819
|
throw new Error(`No accounts for chain: ${chainKey}`);
|
|
7732
7820
|
}
|
|
@@ -7937,23 +8025,10 @@ var init_dist3 = __esm({
|
|
|
7937
8025
|
hdWallet: this.signer.getHDWallet()
|
|
7938
8026
|
});
|
|
7939
8027
|
}
|
|
7940
|
-
async approveErc20(chainKey, token, spender, amount) {
|
|
7941
|
-
await this.ensureConnected();
|
|
7942
|
-
this.resetAutoLockTimer();
|
|
7943
|
-
const coinId = chainKeyToCoinType(chainKey);
|
|
7944
|
-
if (coinId === void 0) throw new Error(`Unsupported chain: ${chainKey}`);
|
|
7945
|
-
const result = await this.blockchainClient.approve({
|
|
7946
|
-
assetId: `c${coinId}_t${token}`,
|
|
7947
|
-
spender,
|
|
7948
|
-
amount,
|
|
7949
|
-
hdWallet: this.signer.getHDWallet()
|
|
7950
|
-
});
|
|
7951
|
-
return { txHash: result.hash };
|
|
7952
|
-
}
|
|
7953
8028
|
async sendTransaction(params) {
|
|
7954
8029
|
await this.ensureConnected();
|
|
7955
8030
|
this.resetAutoLockTimer();
|
|
7956
|
-
const { getChainByEvmId: getChainByEvmId2, chainKeyToCoinType:
|
|
8031
|
+
const { getChainByEvmId: getChainByEvmId2, chainKeyToCoinType: chainKeyToCoinType3 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
7957
8032
|
let assetId;
|
|
7958
8033
|
if (params.chainId.startsWith("eip155:")) {
|
|
7959
8034
|
const chain2 = getChainByEvmId2(params.chainId.replace("eip155:", ""));
|
|
@@ -7962,7 +8037,7 @@ var init_dist3 = __esm({
|
|
|
7962
8037
|
} else if (params.chainId.startsWith("solana:")) {
|
|
7963
8038
|
assetId = "c501";
|
|
7964
8039
|
} else {
|
|
7965
|
-
const coinId =
|
|
8040
|
+
const coinId = chainKeyToCoinType3(params.chainId);
|
|
7966
8041
|
if (coinId === void 0) throw new Error(`Unsupported chain: ${params.chainId}`);
|
|
7967
8042
|
assetId = `c${coinId}`;
|
|
7968
8043
|
}
|
|
@@ -7974,7 +8049,7 @@ var init_dist3 = __esm({
|
|
|
7974
8049
|
serializedMessage: params.data,
|
|
7975
8050
|
hdWallet
|
|
7976
8051
|
});
|
|
7977
|
-
return result2.
|
|
8052
|
+
return result2.txHash;
|
|
7978
8053
|
}
|
|
7979
8054
|
if (hasCalldata || hasValue) {
|
|
7980
8055
|
const result2 = await this.blockchainClient.executeContract({
|
|
@@ -7984,7 +8059,7 @@ var init_dist3 = __esm({
|
|
|
7984
8059
|
data: params.data || "0x",
|
|
7985
8060
|
hdWallet
|
|
7986
8061
|
});
|
|
7987
|
-
return result2.
|
|
8062
|
+
return result2.txHash;
|
|
7988
8063
|
}
|
|
7989
8064
|
const result = await this.blockchainClient.transfer({
|
|
7990
8065
|
assetId,
|
|
@@ -7992,7 +8067,7 @@ var init_dist3 = __esm({
|
|
|
7992
8067
|
amount: params.value || "0",
|
|
7993
8068
|
hdWallet
|
|
7994
8069
|
});
|
|
7995
|
-
return result.
|
|
8070
|
+
return result.txHash;
|
|
7996
8071
|
}
|
|
7997
8072
|
supportsChain(chainId) {
|
|
7998
8073
|
return isChainSupported(chainId);
|
|
@@ -8032,6 +8107,39 @@ var init_dist3 = __esm({
|
|
|
8032
8107
|
return this.signer;
|
|
8033
8108
|
}
|
|
8034
8109
|
};
|
|
8110
|
+
TeeWalletProvider = class extends WalletProvider {
|
|
8111
|
+
name = "tee";
|
|
8112
|
+
address = null;
|
|
8113
|
+
async getAddress(_chainId) {
|
|
8114
|
+
if (!this.address) throw new Error("TEE wallet not initialized");
|
|
8115
|
+
return this.address;
|
|
8116
|
+
}
|
|
8117
|
+
async getAddresses() {
|
|
8118
|
+
if (!this.address) return [];
|
|
8119
|
+
return [{ address: this.address, chainId: "eip155:1" }];
|
|
8120
|
+
}
|
|
8121
|
+
async signMessage(_chainId, _message) {
|
|
8122
|
+
throw new Error("TEE signing not yet implemented");
|
|
8123
|
+
}
|
|
8124
|
+
async signTypedData(_chainKey, _chainId, _typedDataJson) {
|
|
8125
|
+
throw new Error("TEE signing not yet implemented");
|
|
8126
|
+
}
|
|
8127
|
+
async sendTransaction(_params) {
|
|
8128
|
+
throw new Error("TEE transaction sending not yet implemented");
|
|
8129
|
+
}
|
|
8130
|
+
supportsChain(_chainId) {
|
|
8131
|
+
return false;
|
|
8132
|
+
}
|
|
8133
|
+
async connect(_chains) {
|
|
8134
|
+
throw new Error("TEE wallet connect not yet implemented");
|
|
8135
|
+
}
|
|
8136
|
+
async disconnect() {
|
|
8137
|
+
this.address = null;
|
|
8138
|
+
}
|
|
8139
|
+
isConnected() {
|
|
8140
|
+
return this.address !== null;
|
|
8141
|
+
}
|
|
8142
|
+
};
|
|
8035
8143
|
WalletContext = class {
|
|
8036
8144
|
_state = "unbound";
|
|
8037
8145
|
_wallet;
|
|
@@ -8163,13 +8271,13 @@ var init_dist3 = __esm({
|
|
|
8163
8271
|
* to avoid forcing wallet-core to depend on it. The caller (typically the
|
|
8164
8272
|
* MCP action provider) constructs the WC provider and passes it in.
|
|
8165
8273
|
*/
|
|
8166
|
-
async beginWalletConnect(
|
|
8167
|
-
return this.enqueue(() => this.beginWalletConnectImpl(
|
|
8274
|
+
async beginWalletConnect(wc3, chains) {
|
|
8275
|
+
return this.enqueue(() => this.beginWalletConnectImpl(wc3, chains));
|
|
8168
8276
|
}
|
|
8169
|
-
async beginWalletConnectImpl(
|
|
8277
|
+
async beginWalletConnectImpl(wc3, chains) {
|
|
8170
8278
|
await this.disconnectWcIfAny();
|
|
8171
8279
|
try {
|
|
8172
|
-
this._pairing = await
|
|
8280
|
+
this._pairing = await wc3.connect(chains);
|
|
8173
8281
|
} catch (err) {
|
|
8174
8282
|
this._wallet = void 0;
|
|
8175
8283
|
this._pendingWc = void 0;
|
|
@@ -8183,20 +8291,20 @@ var init_dist3 = __esm({
|
|
|
8183
8291
|
throw err;
|
|
8184
8292
|
}
|
|
8185
8293
|
this._wallet = void 0;
|
|
8186
|
-
this._pendingWc =
|
|
8294
|
+
this._pendingWc = wc3;
|
|
8187
8295
|
this._state = "wc-pairing";
|
|
8188
8296
|
this._lastError = void 0;
|
|
8189
8297
|
this._peerName = void 0;
|
|
8190
|
-
|
|
8191
|
-
if (this._pendingWc !==
|
|
8192
|
-
this._wallet =
|
|
8298
|
+
wc3.onSessionEstablished((session) => {
|
|
8299
|
+
if (this._pendingWc !== wc3) return;
|
|
8300
|
+
this._wallet = wc3;
|
|
8193
8301
|
this._peerName = session.peerName;
|
|
8194
8302
|
this._state = "wc-connected";
|
|
8195
8303
|
this._pendingWc = void 0;
|
|
8196
8304
|
this._pairing = void 0;
|
|
8197
8305
|
});
|
|
8198
|
-
|
|
8199
|
-
if (this._wallet !==
|
|
8306
|
+
wc3.onSessionEnded((reason) => {
|
|
8307
|
+
if (this._wallet !== wc3) return;
|
|
8200
8308
|
this._wallet = void 0;
|
|
8201
8309
|
this._pairing = void 0;
|
|
8202
8310
|
this._peerName = void 0;
|
|
@@ -8226,35 +8334,6 @@ var init_dist3 = __esm({
|
|
|
8226
8334
|
}
|
|
8227
8335
|
});
|
|
8228
8336
|
|
|
8229
|
-
// ../blockchain/dist/index.mjs
|
|
8230
|
-
var dist_exports2 = {};
|
|
8231
|
-
__export(dist_exports2, {
|
|
8232
|
-
BlockchainClient: () => BlockchainClient,
|
|
8233
|
-
VerifyCredentialsError: () => VerifyCredentialsError,
|
|
8234
|
-
WalletCoreSigner: () => WalletCoreSigner,
|
|
8235
|
-
_setWalletCore: () => _setWalletCore,
|
|
8236
|
-
applyPolyfills: () => applyPolyfills,
|
|
8237
|
-
buildCoinTypeMap: () => buildCoinTypeMap,
|
|
8238
|
-
getAmberDomainsByCoinId: () => getAmberDomainsByCoinId,
|
|
8239
|
-
getCoinType: () => getCoinType,
|
|
8240
|
-
getWalletCore: () => getWalletCore,
|
|
8241
|
-
humanAmountToWei: () => humanAmountToWei,
|
|
8242
|
-
initTrustSDK: () => initTrustSDK,
|
|
8243
|
-
isInitialized: () => isInitialized,
|
|
8244
|
-
patchNaasSession: () => patchNaasSession,
|
|
8245
|
-
patchNodeStubs: () => patchNodeStubs,
|
|
8246
|
-
registerDeviceAndWallet: () => registerDeviceAndWallet,
|
|
8247
|
-
setPolyfillCredentials: () => setPolyfillCredentials,
|
|
8248
|
-
setPolyfillNaasSessionId: () => setPolyfillNaasSessionId,
|
|
8249
|
-
verifyCredentials: () => verifyCredentials
|
|
8250
|
-
});
|
|
8251
|
-
var init_dist4 = __esm({
|
|
8252
|
-
"../blockchain/dist/index.mjs"() {
|
|
8253
|
-
"use strict";
|
|
8254
|
-
init_dist3();
|
|
8255
|
-
}
|
|
8256
|
-
});
|
|
8257
|
-
|
|
8258
8337
|
// ../../node_modules/.pnpm/tslib@1.14.1/node_modules/tslib/tslib.es6.js
|
|
8259
8338
|
var tslib_es6_exports = {};
|
|
8260
8339
|
__export(tslib_es6_exports, {
|
|
@@ -13555,10 +13634,10 @@ var require_boxen = __commonJS({
|
|
|
13555
13634
|
}
|
|
13556
13635
|
return lines.join(NL);
|
|
13557
13636
|
};
|
|
13558
|
-
var
|
|
13559
|
-
var isColorValid = (color) => typeof color === "string" && (chalk9[color] ||
|
|
13560
|
-
var getColorFn = (color) =>
|
|
13561
|
-
var getBGColorFn = (color) =>
|
|
13637
|
+
var isHex2 = (color) => color.match(/^#(?:[0-f]{3}){1,2}$/i);
|
|
13638
|
+
var isColorValid = (color) => typeof color === "string" && (chalk9[color] || isHex2(color));
|
|
13639
|
+
var getColorFn = (color) => isHex2(color) ? chalk9.hex(color) : chalk9[color];
|
|
13640
|
+
var getBGColorFn = (color) => isHex2(color) ? chalk9.bgHex(color) : chalk9[camelCase(["bg", color])];
|
|
13562
13641
|
module2.exports = (text, options) => {
|
|
13563
13642
|
options = {
|
|
13564
13643
|
padding: 0,
|
|
@@ -23039,7 +23118,7 @@ function safeExit() {
|
|
|
23039
23118
|
// src/config-loader.ts
|
|
23040
23119
|
var import_chalk2 = __toESM(require_source());
|
|
23041
23120
|
init_dist();
|
|
23042
|
-
|
|
23121
|
+
init_dist3();
|
|
23043
23122
|
init_dist2();
|
|
23044
23123
|
var EMBEDDED_NAAS_SESSION_ID = "Nr8eogRKLULRxGpVkeMqQBoDpGnZxgYjKK7tu6bKxqUn6eZU";
|
|
23045
23124
|
function resolveNaasSessionId() {
|
|
@@ -28267,7 +28346,7 @@ function weierstrassPoints(opts) {
|
|
|
28267
28346
|
const CURVE = validatePointOpts(opts);
|
|
28268
28347
|
const { Fp } = CURVE;
|
|
28269
28348
|
const Fn3 = Field(CURVE.n, CURVE.nBitLength);
|
|
28270
|
-
const
|
|
28349
|
+
const toBytes5 = CURVE.toBytes || ((_c2, point, _isCompressed) => {
|
|
28271
28350
|
const a3 = point.toAffine();
|
|
28272
28351
|
return concatBytes2(Uint8Array.from([4]), Fp.toBytes(a3.x), Fp.toBytes(a3.y));
|
|
28273
28352
|
});
|
|
@@ -28647,7 +28726,7 @@ function weierstrassPoints(opts) {
|
|
|
28647
28726
|
toRawBytes(isCompressed = true) {
|
|
28648
28727
|
abool("isCompressed", isCompressed);
|
|
28649
28728
|
this.assertValidity();
|
|
28650
|
-
return
|
|
28729
|
+
return toBytes5(Point, this, isCompressed);
|
|
28651
28730
|
}
|
|
28652
28731
|
toHex(isCompressed = true) {
|
|
28653
28732
|
abool("isCompressed", isCompressed);
|
|
@@ -28951,16 +29030,16 @@ function weierstrass(curveDef) {
|
|
|
28951
29030
|
throw new Error("options.strict was renamed to lowS");
|
|
28952
29031
|
if (format !== void 0 && format !== "compact" && format !== "der")
|
|
28953
29032
|
throw new Error("format must be compact or der");
|
|
28954
|
-
const
|
|
28955
|
-
const isObj = !
|
|
28956
|
-
if (!
|
|
29033
|
+
const isHex2 = typeof sg === "string" || isBytes2(sg);
|
|
29034
|
+
const isObj = !isHex2 && !format && typeof sg === "object" && sg !== null && typeof sg.r === "bigint" && typeof sg.s === "bigint";
|
|
29035
|
+
if (!isHex2 && !isObj)
|
|
28957
29036
|
throw new Error("invalid signature, expected Uint8Array, hex string or Signature instance");
|
|
28958
29037
|
let _sig = void 0;
|
|
28959
29038
|
let P5;
|
|
28960
29039
|
try {
|
|
28961
29040
|
if (isObj)
|
|
28962
29041
|
_sig = new Signature(sg.r, sg.s);
|
|
28963
|
-
if (
|
|
29042
|
+
if (isHex2) {
|
|
28964
29043
|
try {
|
|
28965
29044
|
if (format !== "compact")
|
|
28966
29045
|
_sig = Signature.fromDER(sg);
|
|
@@ -39097,22 +39176,6 @@ var WalletConnectProvider = class extends WalletProvider {
|
|
|
39097
39176
|
}
|
|
39098
39177
|
});
|
|
39099
39178
|
}
|
|
39100
|
-
async approveErc20(chainKey, token, spender, amount) {
|
|
39101
|
-
const chain2 = getChainById(chainKey);
|
|
39102
|
-
if (!chain2) throw new WalletError("CHAIN_UNSUPPORTED", `Unsupported chain: ${chainKey}`);
|
|
39103
|
-
const caipChainId = `${chain2.namespace}:${chain2.chainId}`;
|
|
39104
|
-
const selector = "0x095ea7b3";
|
|
39105
|
-
const paddedSpender = spender.replace(/^0x/, "").toLowerCase().padStart(64, "0");
|
|
39106
|
-
const paddedAmount = BigInt(amount).toString(16).padStart(64, "0");
|
|
39107
|
-
const data = selector + paddedSpender + paddedAmount;
|
|
39108
|
-
const txHash = await this.sendTransaction({
|
|
39109
|
-
chainId: caipChainId,
|
|
39110
|
-
to: token,
|
|
39111
|
-
data,
|
|
39112
|
-
value: "0x0"
|
|
39113
|
-
});
|
|
39114
|
-
return { txHash };
|
|
39115
|
-
}
|
|
39116
39179
|
async sendTransaction(params) {
|
|
39117
39180
|
const session = this.getActiveSession();
|
|
39118
39181
|
if (!session) throw new WalletError("WC_NOT_CONNECTED", WC_NOT_CONNECTED_MSG);
|
|
@@ -39643,11 +39706,19 @@ var import_prompts = require("@inquirer/prompts");
|
|
|
39643
39706
|
init_dist2();
|
|
39644
39707
|
init_dist();
|
|
39645
39708
|
var PORTAL_URL = "https://portal.trustwallet.com/dashboard/apps";
|
|
39709
|
+
var WC_DASHBOARD_URL = "https://dashboard.walletconnect.com/";
|
|
39646
39710
|
var defaultPrompter = {
|
|
39647
39711
|
promptAccessId: () => (0, import_prompts.input)({
|
|
39648
39712
|
message: "Access ID:",
|
|
39649
39713
|
validate: (s2) => s2.trim().length > 0 ? true : "Access ID is required"
|
|
39650
39714
|
}),
|
|
39715
|
+
promptWalletConnectProjectId: async () => {
|
|
39716
|
+
const wc3 = await (0, import_prompts.input)({
|
|
39717
|
+
message: "WalletConnect Project ID (optional):",
|
|
39718
|
+
required: false
|
|
39719
|
+
});
|
|
39720
|
+
return wc3.trim() || void 0;
|
|
39721
|
+
},
|
|
39651
39722
|
promptHmacSecret: () => (0, import_prompts.password)({
|
|
39652
39723
|
message: "HMAC secret:",
|
|
39653
39724
|
mask: "\u25CF",
|
|
@@ -39668,19 +39739,13 @@ async function runCredentialsPhase(opts, deps = makeDefaultDeps()) {
|
|
|
39668
39739
|
success(`Credentials already configured (access ID ${existing.accessId.slice(0, 8)})`);
|
|
39669
39740
|
return;
|
|
39670
39741
|
}
|
|
39671
|
-
const isRotation = existing !== null;
|
|
39672
39742
|
if (opts.nonInteractive) {
|
|
39673
39743
|
await runNonInteractive(deps);
|
|
39674
39744
|
} else {
|
|
39675
|
-
info(`Get your API
|
|
39745
|
+
info(`Get your Trust Wallet API credentials at: ${PORTAL_URL}
|
|
39676
39746
|
`);
|
|
39677
39747
|
await runInteractive(deps);
|
|
39678
39748
|
}
|
|
39679
|
-
if (isRotation) {
|
|
39680
|
-
warn(
|
|
39681
|
-
"Heads up: if you previously hand-pasted `env: { TWAK_HMAC_SECRET: '\u2026' }` into a harness's MCP config, that value is now stale. The canonical twak entry pulls credentials from ~/.twak/credentials.json \u2014 drop any explicit env blocks so the new secret is used."
|
|
39682
|
-
);
|
|
39683
|
-
}
|
|
39684
39749
|
}
|
|
39685
39750
|
async function runNonInteractive(deps) {
|
|
39686
39751
|
const accessId = deps.env.TWAK_ACCESS_ID?.trim();
|
|
@@ -39691,13 +39756,21 @@ async function runNonInteractive(deps) {
|
|
|
39691
39756
|
"Non-interactive setup requires TWAK_ACCESS_ID and TWAK_HMAC_SECRET in the environment."
|
|
39692
39757
|
);
|
|
39693
39758
|
}
|
|
39694
|
-
const
|
|
39759
|
+
const walletConnectProjectId = deps.env.WALLETCONNECT_PROJECT_ID?.trim() || void 0;
|
|
39760
|
+
const result = await deps.credManager.save({ accessId, hmacSecret, walletConnectProjectId });
|
|
39695
39761
|
success(`Saved access ID ${accessId.slice(0, 8)} to ${storageLocation(result.keychain)}`);
|
|
39696
39762
|
}
|
|
39697
39763
|
async function runInteractive(deps) {
|
|
39698
39764
|
const accessId = (await deps.prompter.promptAccessId()).trim();
|
|
39699
39765
|
const hmacSecret = (await deps.prompter.promptHmacSecret()).trim();
|
|
39700
|
-
|
|
39766
|
+
info(
|
|
39767
|
+
`
|
|
39768
|
+
WalletConnect Project ID is only used when signing via WalletConnect in MCP mode.
|
|
39769
|
+
Skip if you only plan to use the local agent wallet. Get one at: ${WC_DASHBOARD_URL}
|
|
39770
|
+
`
|
|
39771
|
+
);
|
|
39772
|
+
const walletConnectProjectId = (await deps.prompter.promptWalletConnectProjectId())?.trim() || void 0;
|
|
39773
|
+
const result = await deps.credManager.save({ accessId, hmacSecret, walletConnectProjectId });
|
|
39701
39774
|
success(`Saved access ID ${accessId.slice(0, 8)} to ${storageLocation(result.keychain)}`);
|
|
39702
39775
|
}
|
|
39703
39776
|
function storageLocation(keychain) {
|
|
@@ -40184,6 +40257,16 @@ function isNonInteractive() {
|
|
|
40184
40257
|
function printSetupBanner() {
|
|
40185
40258
|
process.stderr.write("\n" + import_chalk3.default.bold("\u26D3 Trust Wallet Agent Kit \u2014 Setup") + "\n\n");
|
|
40186
40259
|
}
|
|
40260
|
+
function printNextSteps() {
|
|
40261
|
+
const harnessNames = ALL_HARNESSES.slice(0, 3).map((h4) => h4.displayName).join(", ");
|
|
40262
|
+
info(
|
|
40263
|
+
`
|
|
40264
|
+
Next steps:
|
|
40265
|
+
1. Restart your harness (${harnessNames}, etc.) so it picks up twak.
|
|
40266
|
+
2. Try a sample query, e.g. "What is my wallet address on Ethereum?"
|
|
40267
|
+
`
|
|
40268
|
+
);
|
|
40269
|
+
}
|
|
40187
40270
|
async function runSetup(opts, phases = defaultPhases) {
|
|
40188
40271
|
const noFlags = !opts.reconfigureCreds && !opts.harness && !opts.wallet;
|
|
40189
40272
|
if (noFlags || opts.reconfigureCreds) await phases.credentials(opts);
|
|
@@ -40206,6 +40289,7 @@ function registerSetupCommand(program3) {
|
|
|
40206
40289
|
printSetupBanner();
|
|
40207
40290
|
await runSetup(opts);
|
|
40208
40291
|
success("Setup complete.");
|
|
40292
|
+
printNextSteps();
|
|
40209
40293
|
} catch (err) {
|
|
40210
40294
|
handleCommandError(err, false, "Setup failed");
|
|
40211
40295
|
}
|
|
@@ -41539,11 +41623,11 @@ function registerTransferCommand(program3) {
|
|
|
41539
41623
|
} catch (err) {
|
|
41540
41624
|
mapBroadcastError(err);
|
|
41541
41625
|
}
|
|
41542
|
-
const explorerUrl = getExplorerTxUrl(result.chainKey, result.
|
|
41626
|
+
const explorerUrl = getExplorerTxUrl(result.chainKey, result.txHash);
|
|
41543
41627
|
success("Transfer sent!");
|
|
41544
41628
|
output(
|
|
41545
41629
|
{
|
|
41546
|
-
hash: result.
|
|
41630
|
+
hash: result.txHash,
|
|
41547
41631
|
chain: result.chainKey,
|
|
41548
41632
|
from: address,
|
|
41549
41633
|
to: resolvedTo,
|
|
@@ -41562,7 +41646,681 @@ function registerTransferCommand(program3) {
|
|
|
41562
41646
|
|
|
41563
41647
|
// src/commands/swap.ts
|
|
41564
41648
|
init_dist();
|
|
41565
|
-
|
|
41649
|
+
init_dist3();
|
|
41650
|
+
|
|
41651
|
+
// ../contracts/dist/index.mjs
|
|
41652
|
+
init_dist();
|
|
41653
|
+
init_dist();
|
|
41654
|
+
init_dist();
|
|
41655
|
+
init_dist();
|
|
41656
|
+
init_dist3();
|
|
41657
|
+
init_dist();
|
|
41658
|
+
var HEX_PATTERN = /^0x[0-9a-fA-F]*$/;
|
|
41659
|
+
function isHex(value) {
|
|
41660
|
+
return HEX_PATTERN.test(value);
|
|
41661
|
+
}
|
|
41662
|
+
function hexToBytes2(hex2) {
|
|
41663
|
+
if (!isHex(hex2)) {
|
|
41664
|
+
throw new TwakValidationError(`Invalid hex string: ${hex2}`, { kind: "invalidHex" });
|
|
41665
|
+
}
|
|
41666
|
+
const clean2 = hex2.slice(2);
|
|
41667
|
+
if (clean2.length % 2 !== 0) {
|
|
41668
|
+
throw new TwakValidationError(`Hex string has odd length: ${hex2}`, { kind: "invalidHex" });
|
|
41669
|
+
}
|
|
41670
|
+
const out = new Uint8Array(clean2.length / 2);
|
|
41671
|
+
for (let i2 = 0; i2 < out.length; i2++) {
|
|
41672
|
+
out[i2] = parseInt(clean2.slice(i2 * 2, i2 * 2 + 2), 16);
|
|
41673
|
+
}
|
|
41674
|
+
return out;
|
|
41675
|
+
}
|
|
41676
|
+
function bytesToHex2(bytes) {
|
|
41677
|
+
let out = "0x";
|
|
41678
|
+
for (let i2 = 0; i2 < bytes.length; i2++) {
|
|
41679
|
+
out += bytes[i2].toString(16).padStart(2, "0");
|
|
41680
|
+
}
|
|
41681
|
+
return out;
|
|
41682
|
+
}
|
|
41683
|
+
function wc2() {
|
|
41684
|
+
return getWalletCore();
|
|
41685
|
+
}
|
|
41686
|
+
var BRIDGE = {
|
|
41687
|
+
createAbiFunction(name2) {
|
|
41688
|
+
const ctor = wc2().EthereumAbiFunction;
|
|
41689
|
+
if (typeof ctor?.createWithString === "function") {
|
|
41690
|
+
return ctor.createWithString(name2);
|
|
41691
|
+
}
|
|
41692
|
+
return new ctor(name2);
|
|
41693
|
+
},
|
|
41694
|
+
encode(fn2) {
|
|
41695
|
+
return wc2().EthereumAbi.encode(fn2);
|
|
41696
|
+
},
|
|
41697
|
+
decodeOutput(fn2, data) {
|
|
41698
|
+
return wc2().EthereumAbi.decodeOutput(fn2, data);
|
|
41699
|
+
},
|
|
41700
|
+
keccak256(data) {
|
|
41701
|
+
return wc2().Hash.keccak256(data);
|
|
41702
|
+
}
|
|
41703
|
+
};
|
|
41704
|
+
function getAbiBridge() {
|
|
41705
|
+
return BRIDGE;
|
|
41706
|
+
}
|
|
41707
|
+
function findAbiFunction(abi, methodName) {
|
|
41708
|
+
const entry = abi.find(
|
|
41709
|
+
(e2) => e2.type === "function" && e2.name === methodName
|
|
41710
|
+
);
|
|
41711
|
+
if (!entry) {
|
|
41712
|
+
throw new TwakValidationError(`Unknown ABI method: ${methodName}`, {
|
|
41713
|
+
kind: "unknownMethod",
|
|
41714
|
+
methodName
|
|
41715
|
+
});
|
|
41716
|
+
}
|
|
41717
|
+
return entry;
|
|
41718
|
+
}
|
|
41719
|
+
function encodeFunctionCall(abi, methodName, args) {
|
|
41720
|
+
const fnDef = findAbiFunction(abi, methodName);
|
|
41721
|
+
if (args.length !== fnDef.inputs.length) {
|
|
41722
|
+
throw new TwakValidationError(
|
|
41723
|
+
`Wrong arg count for ${methodName}: expected ${fnDef.inputs.length}, got ${args.length}`,
|
|
41724
|
+
{
|
|
41725
|
+
kind: "wrongArgCount",
|
|
41726
|
+
methodName,
|
|
41727
|
+
expected: fnDef.inputs.length,
|
|
41728
|
+
actual: args.length
|
|
41729
|
+
}
|
|
41730
|
+
);
|
|
41731
|
+
}
|
|
41732
|
+
const bridge = getAbiBridge();
|
|
41733
|
+
const fn2 = bridge.createAbiFunction(methodName);
|
|
41734
|
+
fnDef.inputs.forEach((param, i2) => {
|
|
41735
|
+
pushParam(fn2, param, args[i2], methodName);
|
|
41736
|
+
});
|
|
41737
|
+
return bytesToHex2(bridge.encode(fn2));
|
|
41738
|
+
}
|
|
41739
|
+
function pushParam(fn2, param, value, methodName) {
|
|
41740
|
+
const type = param.type;
|
|
41741
|
+
const isOutput = false;
|
|
41742
|
+
if (type === "address") {
|
|
41743
|
+
fn2.addParamAddress(toAddressBytes(value, param, methodName), isOutput);
|
|
41744
|
+
return;
|
|
41745
|
+
}
|
|
41746
|
+
if (type === "bool") {
|
|
41747
|
+
if (typeof value !== "boolean") {
|
|
41748
|
+
throw argTypeMismatch(methodName, param, value, "boolean");
|
|
41749
|
+
}
|
|
41750
|
+
fn2.addParamBool(value, isOutput);
|
|
41751
|
+
return;
|
|
41752
|
+
}
|
|
41753
|
+
if (type === "string") {
|
|
41754
|
+
if (typeof value !== "string") {
|
|
41755
|
+
throw argTypeMismatch(methodName, param, value, "string");
|
|
41756
|
+
}
|
|
41757
|
+
fn2.addParamString(value, isOutput);
|
|
41758
|
+
return;
|
|
41759
|
+
}
|
|
41760
|
+
if (type === "bytes") {
|
|
41761
|
+
fn2.addParamBytes(toBytes4(value, param, methodName), isOutput);
|
|
41762
|
+
return;
|
|
41763
|
+
}
|
|
41764
|
+
const bytesNMatch = /^bytes(\d+)$/.exec(type);
|
|
41765
|
+
if (bytesNMatch) {
|
|
41766
|
+
const size3 = Number(bytesNMatch[1]);
|
|
41767
|
+
fn2.addParamBytesFix(toBytesFix(value, size3, param, methodName), size3, isOutput);
|
|
41768
|
+
return;
|
|
41769
|
+
}
|
|
41770
|
+
const intMatch = /^(u?)int(\d*)$/.exec(type);
|
|
41771
|
+
if (intMatch) {
|
|
41772
|
+
const isUnsigned = intMatch[1] === "u";
|
|
41773
|
+
const bits = intMatch[2] ? Number(intMatch[2]) : 256;
|
|
41774
|
+
if (bits < 8 || bits > 256 || bits % 8 !== 0) {
|
|
41775
|
+
throw new TwakValidationError(
|
|
41776
|
+
`Unsupported integer width ${type} for ${methodName}.${param.name} (expected 8..256, multiple of 8)`,
|
|
41777
|
+
{ kind: "argTypeMismatch", methodName, paramName: param.name, type }
|
|
41778
|
+
);
|
|
41779
|
+
}
|
|
41780
|
+
const bigValue = toBigInt2(value, param, methodName);
|
|
41781
|
+
if (isUnsigned) {
|
|
41782
|
+
if (bigValue < 0n || bigValue >= 1n << BigInt(bits)) {
|
|
41783
|
+
throw argTypeMismatch(methodName, param, value, `uint${bits} (0 <= x < 2^${bits})`);
|
|
41784
|
+
}
|
|
41785
|
+
} else {
|
|
41786
|
+
const limit = 1n << BigInt(bits - 1);
|
|
41787
|
+
if (bigValue < -limit || bigValue >= limit) {
|
|
41788
|
+
throw argTypeMismatch(
|
|
41789
|
+
methodName,
|
|
41790
|
+
param,
|
|
41791
|
+
value,
|
|
41792
|
+
`int${bits} (-2^${bits - 1} <= x < 2^${bits - 1})`
|
|
41793
|
+
);
|
|
41794
|
+
}
|
|
41795
|
+
}
|
|
41796
|
+
const bytes = bigIntToBytes32(bigValue, isUnsigned);
|
|
41797
|
+
if (isUnsigned) {
|
|
41798
|
+
fn2.addParamUInt256(bytes, isOutput);
|
|
41799
|
+
} else {
|
|
41800
|
+
fn2.addParamInt256(bytes, isOutput);
|
|
41801
|
+
}
|
|
41802
|
+
return;
|
|
41803
|
+
}
|
|
41804
|
+
throw new TwakValidationError(
|
|
41805
|
+
`Unsupported ABI param type "${type}" for ${methodName}.${param.name}`,
|
|
41806
|
+
{
|
|
41807
|
+
kind: "argTypeMismatch",
|
|
41808
|
+
methodName,
|
|
41809
|
+
paramName: param.name,
|
|
41810
|
+
type
|
|
41811
|
+
}
|
|
41812
|
+
);
|
|
41813
|
+
}
|
|
41814
|
+
function toAddressBytes(value, param, methodName) {
|
|
41815
|
+
if (typeof value !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(value)) {
|
|
41816
|
+
throw argTypeMismatch(methodName, param, value, "address (0x + 40 hex)");
|
|
41817
|
+
}
|
|
41818
|
+
return hexToBytes2(value);
|
|
41819
|
+
}
|
|
41820
|
+
function bigIntToBytes32(value, isUnsigned) {
|
|
41821
|
+
let v5 = value;
|
|
41822
|
+
if (!isUnsigned && v5 < 0n) {
|
|
41823
|
+
v5 = (1n << 256n) + v5;
|
|
41824
|
+
}
|
|
41825
|
+
const out = new Uint8Array(32);
|
|
41826
|
+
for (let i2 = 31; i2 >= 0; i2--) {
|
|
41827
|
+
out[i2] = Number(v5 & 0xffn);
|
|
41828
|
+
v5 >>= 8n;
|
|
41829
|
+
}
|
|
41830
|
+
return out;
|
|
41831
|
+
}
|
|
41832
|
+
function toBigInt2(value, param, methodName) {
|
|
41833
|
+
if (typeof value === "bigint") return value;
|
|
41834
|
+
if (typeof value === "number") {
|
|
41835
|
+
if (!Number.isSafeInteger(value)) {
|
|
41836
|
+
throw argTypeMismatch(
|
|
41837
|
+
methodName,
|
|
41838
|
+
param,
|
|
41839
|
+
value,
|
|
41840
|
+
"safe integer (use bigint or string for values >2^53)"
|
|
41841
|
+
);
|
|
41842
|
+
}
|
|
41843
|
+
return BigInt(value);
|
|
41844
|
+
}
|
|
41845
|
+
if (typeof value === "string" && /^-?\d+$/.test(value)) return BigInt(value);
|
|
41846
|
+
if (typeof value === "string" && /^0x[0-9a-fA-F]+$/.test(value)) return BigInt(value);
|
|
41847
|
+
throw argTypeMismatch(methodName, param, value, "integer (bigint | number | numeric string)");
|
|
41848
|
+
}
|
|
41849
|
+
function toBytes4(value, param, methodName) {
|
|
41850
|
+
if (value instanceof Uint8Array) return value;
|
|
41851
|
+
if (typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value)) {
|
|
41852
|
+
return hexToBytes2(value);
|
|
41853
|
+
}
|
|
41854
|
+
throw argTypeMismatch(methodName, param, value, "bytes (Uint8Array | 0x-prefixed hex)");
|
|
41855
|
+
}
|
|
41856
|
+
function toBytesFix(value, size3, param, methodName) {
|
|
41857
|
+
const bytes = toBytes4(value, param, methodName);
|
|
41858
|
+
if (bytes.length !== size3) {
|
|
41859
|
+
throw new TwakValidationError(
|
|
41860
|
+
`Wrong byte length for ${methodName}.${param.name}: expected ${size3}, got ${bytes.length}`,
|
|
41861
|
+
{
|
|
41862
|
+
kind: "argTypeMismatch",
|
|
41863
|
+
methodName,
|
|
41864
|
+
paramName: param.name,
|
|
41865
|
+
expected: size3,
|
|
41866
|
+
actual: bytes.length
|
|
41867
|
+
}
|
|
41868
|
+
);
|
|
41869
|
+
}
|
|
41870
|
+
return bytes;
|
|
41871
|
+
}
|
|
41872
|
+
function argTypeMismatch(methodName, param, value, expected) {
|
|
41873
|
+
return new TwakValidationError(
|
|
41874
|
+
`Arg type mismatch for ${methodName}.${param.name}: expected ${expected}, got ${typeof value}`,
|
|
41875
|
+
{
|
|
41876
|
+
kind: "argTypeMismatch",
|
|
41877
|
+
methodName,
|
|
41878
|
+
paramName: param.name,
|
|
41879
|
+
expected,
|
|
41880
|
+
actual: typeof value
|
|
41881
|
+
}
|
|
41882
|
+
);
|
|
41883
|
+
}
|
|
41884
|
+
function decodeFunctionResult(abi, methodName, returnHex) {
|
|
41885
|
+
const fnDef = findAbiFunction(abi, methodName);
|
|
41886
|
+
if (fnDef.outputs.length === 0) return void 0;
|
|
41887
|
+
const data = hexToBytes2(returnHex);
|
|
41888
|
+
if (data.length === 0) {
|
|
41889
|
+
throw new TwakValidationError(
|
|
41890
|
+
`Empty return data for ${methodName} \u2014 call likely reverted or contract has no code at address`,
|
|
41891
|
+
{ kind: "decodeFailed", methodName }
|
|
41892
|
+
);
|
|
41893
|
+
}
|
|
41894
|
+
const bridge = getAbiBridge();
|
|
41895
|
+
const fn2 = bridge.createAbiFunction(methodName);
|
|
41896
|
+
fnDef.outputs.forEach((param) => addOutputParam(fn2, param, methodName));
|
|
41897
|
+
const ok = bridge.decodeOutput(fn2, data);
|
|
41898
|
+
if (!ok) {
|
|
41899
|
+
throw new TwakValidationError(`decodeOutput failed for ${methodName}`, {
|
|
41900
|
+
kind: "decodeFailed",
|
|
41901
|
+
methodName
|
|
41902
|
+
});
|
|
41903
|
+
}
|
|
41904
|
+
if (fnDef.outputs.length === 1) {
|
|
41905
|
+
return readOutputParam(fn2, fnDef.outputs[0], 0, methodName);
|
|
41906
|
+
}
|
|
41907
|
+
const unnamed = fnDef.outputs.find((p3) => p3.name === "");
|
|
41908
|
+
if (unnamed) {
|
|
41909
|
+
throw new TwakValidationError(
|
|
41910
|
+
`Cannot decode multi-output function ${methodName}: every output must be named in the ABI (found unnamed output of type "${unnamed.type}")`,
|
|
41911
|
+
{ kind: "decodeFailed", methodName }
|
|
41912
|
+
);
|
|
41913
|
+
}
|
|
41914
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
41915
|
+
fnDef.outputs.forEach((param, i2) => {
|
|
41916
|
+
result[param.name] = readOutputParam(fn2, param, i2, methodName);
|
|
41917
|
+
});
|
|
41918
|
+
return result;
|
|
41919
|
+
}
|
|
41920
|
+
function decodeLog(abi, log) {
|
|
41921
|
+
const events = abi.filter((e2) => e2.type === "event");
|
|
41922
|
+
for (const event of events) {
|
|
41923
|
+
if (event.anonymous) {
|
|
41924
|
+
throw new TwakValidationError(
|
|
41925
|
+
`Decoding anonymous events is not supported (${event.name}): they emit no signature topic to match against`,
|
|
41926
|
+
{ kind: "unsupportedType", methodName: event.name }
|
|
41927
|
+
);
|
|
41928
|
+
}
|
|
41929
|
+
const topic0 = bytesToHex2(keccak256Utf8(canonicalEventSignature(event)));
|
|
41930
|
+
if (topic0.toLowerCase() !== (log.topics[0] ?? "").toLowerCase()) continue;
|
|
41931
|
+
const indexed = event.inputs.filter((p3) => p3.indexed);
|
|
41932
|
+
const unindexed = event.inputs.filter((p3) => !p3.indexed);
|
|
41933
|
+
const args = {};
|
|
41934
|
+
indexed.forEach((param, i2) => {
|
|
41935
|
+
const topic = log.topics[i2 + 1];
|
|
41936
|
+
if (!topic) {
|
|
41937
|
+
throw new TwakValidationError(`Missing indexed topic for ${event.name}.${param.name}`, {
|
|
41938
|
+
kind: "decodeFailed",
|
|
41939
|
+
methodName: event.name,
|
|
41940
|
+
paramName: param.name
|
|
41941
|
+
});
|
|
41942
|
+
}
|
|
41943
|
+
args[param.name] = decodeIndexedTopic(param, topic);
|
|
41944
|
+
});
|
|
41945
|
+
if (unindexed.length > 0) {
|
|
41946
|
+
const bridge = getAbiBridge();
|
|
41947
|
+
const fn2 = bridge.createAbiFunction(event.name);
|
|
41948
|
+
unindexed.forEach((param) => addOutputParam(fn2, param, event.name));
|
|
41949
|
+
const dataBytes = hexToBytes2(log.data);
|
|
41950
|
+
const ok = bridge.decodeOutput(fn2, dataBytes);
|
|
41951
|
+
if (!ok) {
|
|
41952
|
+
throw new TwakValidationError(`decodeOutput failed for event ${event.name}`, {
|
|
41953
|
+
kind: "decodeFailed",
|
|
41954
|
+
methodName: event.name
|
|
41955
|
+
});
|
|
41956
|
+
}
|
|
41957
|
+
unindexed.forEach((param, i2) => {
|
|
41958
|
+
args[param.name] = readOutputParam(fn2, param, i2, event.name);
|
|
41959
|
+
});
|
|
41960
|
+
}
|
|
41961
|
+
return { eventName: event.name, args };
|
|
41962
|
+
}
|
|
41963
|
+
return null;
|
|
41964
|
+
}
|
|
41965
|
+
function addOutputParam(fn2, param, methodName) {
|
|
41966
|
+
const type = param.type;
|
|
41967
|
+
if (type === "address") {
|
|
41968
|
+
fn2.addParamAddress(new Uint8Array(20), true);
|
|
41969
|
+
return;
|
|
41970
|
+
}
|
|
41971
|
+
if (type === "bool") {
|
|
41972
|
+
fn2.addParamBool(false, true);
|
|
41973
|
+
return;
|
|
41974
|
+
}
|
|
41975
|
+
if (type === "string") {
|
|
41976
|
+
fn2.addParamString("", true);
|
|
41977
|
+
return;
|
|
41978
|
+
}
|
|
41979
|
+
if (/^int\d*$/.test(type)) {
|
|
41980
|
+
throw new TwakValidationError(
|
|
41981
|
+
`Decoding signed integer outputs (${type}) is not supported in v1 \u2014 wallet-core lacks getParamInt*. See contract-abstraction sc-138140 follow-up.`,
|
|
41982
|
+
{ kind: "unsupportedType", methodName, paramName: param.name, type }
|
|
41983
|
+
);
|
|
41984
|
+
}
|
|
41985
|
+
if (/^uint\d*$/.test(type)) {
|
|
41986
|
+
fn2.addParamUInt256(new Uint8Array(32), true);
|
|
41987
|
+
return;
|
|
41988
|
+
}
|
|
41989
|
+
throw new TwakValidationError(
|
|
41990
|
+
`Unsupported output type "${type}" for ${methodName}.${param.name}`,
|
|
41991
|
+
{ kind: "decodeFailed", methodName, paramName: param.name }
|
|
41992
|
+
);
|
|
41993
|
+
}
|
|
41994
|
+
function readOutputParam(fn2, param, idx, methodName) {
|
|
41995
|
+
const type = param.type;
|
|
41996
|
+
if (type === "address") {
|
|
41997
|
+
const raw = fn2.getParamAddress(idx, true);
|
|
41998
|
+
return normalizeAddress(raw);
|
|
41999
|
+
}
|
|
42000
|
+
if (type === "bool") {
|
|
42001
|
+
return Boolean(fn2.getParamBool(idx, true));
|
|
42002
|
+
}
|
|
42003
|
+
if (type === "string") {
|
|
42004
|
+
return String(fn2.getParamString(idx, true));
|
|
42005
|
+
}
|
|
42006
|
+
if (/^uint\d*$/.test(type)) {
|
|
42007
|
+
const raw = fn2.getParamUInt256(idx, true);
|
|
42008
|
+
return bytesToBigInt(raw);
|
|
42009
|
+
}
|
|
42010
|
+
throw new TwakValidationError(
|
|
42011
|
+
`Unsupported output type "${type}" for ${methodName}.${param.name}`,
|
|
42012
|
+
{ kind: "decodeFailed", methodName, paramName: param.name }
|
|
42013
|
+
);
|
|
42014
|
+
}
|
|
42015
|
+
function decodeIndexedTopic(param, topicHex) {
|
|
42016
|
+
const bytes = hexToBytes2(topicHex);
|
|
42017
|
+
if (bytes.length !== 32) {
|
|
42018
|
+
throw new TwakValidationError(
|
|
42019
|
+
`Indexed topic for ${param.name} is not 32 bytes (got ${bytes.length})`,
|
|
42020
|
+
{ kind: "decodeFailed", paramName: param.name }
|
|
42021
|
+
);
|
|
42022
|
+
}
|
|
42023
|
+
const type = param.type;
|
|
42024
|
+
if (type === "address") {
|
|
42025
|
+
return normalizeAddress(bytes.slice(12));
|
|
42026
|
+
}
|
|
42027
|
+
if (type === "bool") {
|
|
42028
|
+
return bytes[31] !== 0;
|
|
42029
|
+
}
|
|
42030
|
+
if (/^int\d*$/.test(type)) {
|
|
42031
|
+
throw new TwakValidationError(
|
|
42032
|
+
`Decoding signed integer topics (${type}) is not supported in v1`,
|
|
42033
|
+
{ kind: "unsupportedType", paramName: param.name, type }
|
|
42034
|
+
);
|
|
42035
|
+
}
|
|
42036
|
+
if (/^uint\d*$/.test(type)) {
|
|
42037
|
+
return bytesToBigInt(bytes);
|
|
42038
|
+
}
|
|
42039
|
+
if (/^bytes32$/.test(type)) {
|
|
42040
|
+
return bytesToHex2(bytes);
|
|
42041
|
+
}
|
|
42042
|
+
return bytesToHex2(bytes);
|
|
42043
|
+
}
|
|
42044
|
+
function canonicalEventSignature(event) {
|
|
42045
|
+
return `${event.name}(${event.inputs.map((i2) => i2.type).join(",")})`;
|
|
42046
|
+
}
|
|
42047
|
+
function keccak256Utf8(s2) {
|
|
42048
|
+
return getAbiBridge().keccak256(new TextEncoder().encode(s2));
|
|
42049
|
+
}
|
|
42050
|
+
function bytesToBigInt(bytes) {
|
|
42051
|
+
let v5 = 0n;
|
|
42052
|
+
for (let i2 = 0; i2 < bytes.length; i2++) {
|
|
42053
|
+
v5 = v5 << 8n | BigInt(bytes[i2]);
|
|
42054
|
+
}
|
|
42055
|
+
return v5;
|
|
42056
|
+
}
|
|
42057
|
+
function normalizeAddress(raw) {
|
|
42058
|
+
if (typeof raw === "string") {
|
|
42059
|
+
return raw.startsWith("0x") ? raw.toLowerCase() : "0x" + raw.toLowerCase();
|
|
42060
|
+
}
|
|
42061
|
+
if (raw && typeof raw === "object" && "length" in raw) {
|
|
42062
|
+
return bytesToHex2(raw);
|
|
42063
|
+
}
|
|
42064
|
+
throw new TwakValidationError(`Unexpected address shape from wallet-core: ${typeof raw}`, {
|
|
42065
|
+
kind: "decodeFailed"
|
|
42066
|
+
});
|
|
42067
|
+
}
|
|
42068
|
+
function createContractInstance(opts) {
|
|
42069
|
+
const { abi, address, chainKey, client } = opts;
|
|
42070
|
+
if (!EVM_ADDRESS_RE.test(address)) {
|
|
42071
|
+
throw new TwakValidationError(`Invalid EVM contract address: ${address}`, {
|
|
42072
|
+
kind: "invalidAddress",
|
|
42073
|
+
address
|
|
42074
|
+
});
|
|
42075
|
+
}
|
|
42076
|
+
const chainDef = requireChain(chainKey);
|
|
42077
|
+
if (chainDef.namespace !== "eip155") {
|
|
42078
|
+
throw new TwakValidationError(
|
|
42079
|
+
`Contract calls are EVM-only; ${chainKey} is namespace ${chainDef.namespace}`,
|
|
42080
|
+
{ chainKey, namespace: chainDef.namespace }
|
|
42081
|
+
);
|
|
42082
|
+
}
|
|
42083
|
+
return {
|
|
42084
|
+
address,
|
|
42085
|
+
chainKey,
|
|
42086
|
+
abi,
|
|
42087
|
+
read: buildReadProxy(abi, address, chainKey, client),
|
|
42088
|
+
write: buildWriteProxy(abi, address, chainKey, client)
|
|
42089
|
+
};
|
|
42090
|
+
}
|
|
42091
|
+
function indexAbiFunctions(abi) {
|
|
42092
|
+
const names = /* @__PURE__ */ new Set();
|
|
42093
|
+
for (const entry of abi) {
|
|
42094
|
+
if (entry.type === "function") {
|
|
42095
|
+
const name2 = entry.name;
|
|
42096
|
+
if (typeof name2 === "string") names.add(name2);
|
|
42097
|
+
}
|
|
42098
|
+
}
|
|
42099
|
+
return names;
|
|
42100
|
+
}
|
|
42101
|
+
function buildReadProxy(abi, address, chainKey, client) {
|
|
42102
|
+
const fnNames = indexAbiFunctions(abi);
|
|
42103
|
+
return new Proxy({}, {
|
|
42104
|
+
get(_2, prop) {
|
|
42105
|
+
if (typeof prop !== "string") return void 0;
|
|
42106
|
+
if (isJsInternal(prop)) return void 0;
|
|
42107
|
+
if (!fnNames.has(prop)) findAbiFunction(abi, prop);
|
|
42108
|
+
return async (args = []) => {
|
|
42109
|
+
const data = encodeFunctionCall(abi, prop, args);
|
|
42110
|
+
const returnHex = await client.ethCall(chainKey, address, data);
|
|
42111
|
+
return decodeFunctionResult(abi, prop, returnHex);
|
|
42112
|
+
};
|
|
42113
|
+
}
|
|
42114
|
+
});
|
|
42115
|
+
}
|
|
42116
|
+
function buildWriteProxy(abi, address, chainKey, client) {
|
|
42117
|
+
const fnNames = indexAbiFunctions(abi);
|
|
42118
|
+
return new Proxy({}, {
|
|
42119
|
+
get(_2, prop) {
|
|
42120
|
+
if (typeof prop !== "string") return void 0;
|
|
42121
|
+
if (isJsInternal(prop)) return void 0;
|
|
42122
|
+
if (!fnNames.has(prop)) findAbiFunction(abi, prop);
|
|
42123
|
+
return async (args = [], signer) => {
|
|
42124
|
+
if (!signer) {
|
|
42125
|
+
throw new TwakValidationError(`Missing signer for write call to ${prop}`, {
|
|
42126
|
+
kind: "missingSigner",
|
|
42127
|
+
methodName: prop
|
|
42128
|
+
});
|
|
42129
|
+
}
|
|
42130
|
+
const data = encodeFunctionCall(abi, prop, args);
|
|
42131
|
+
const txHash = await signer.sendTransaction({
|
|
42132
|
+
chainId: chainKeyToCaip2(chainKey),
|
|
42133
|
+
to: address,
|
|
42134
|
+
data,
|
|
42135
|
+
value: "0x0"
|
|
42136
|
+
});
|
|
42137
|
+
return makeContractPendingTransaction({
|
|
42138
|
+
txHash,
|
|
42139
|
+
abi,
|
|
42140
|
+
address,
|
|
42141
|
+
chainKey,
|
|
42142
|
+
client,
|
|
42143
|
+
methodName: prop
|
|
42144
|
+
});
|
|
42145
|
+
};
|
|
42146
|
+
}
|
|
42147
|
+
});
|
|
42148
|
+
}
|
|
42149
|
+
function makeContractPendingTransaction(opts) {
|
|
42150
|
+
const { txHash, abi, address, chainKey, client, methodName } = opts;
|
|
42151
|
+
return {
|
|
42152
|
+
txHash,
|
|
42153
|
+
async wait(waitOpts) {
|
|
42154
|
+
const receipt = await client.waitForReceipt(chainKey, txHash, {
|
|
42155
|
+
timeoutMs: waitOpts?.timeoutMs
|
|
42156
|
+
});
|
|
42157
|
+
if (receipt.status === "0x0") {
|
|
42158
|
+
throw new TwakError(
|
|
42159
|
+
`Transaction reverted on-chain: ${receipt.transactionHash}`,
|
|
42160
|
+
"TX_FAILED",
|
|
42161
|
+
{ txHash: receipt.transactionHash, methodName, receipt }
|
|
42162
|
+
);
|
|
42163
|
+
}
|
|
42164
|
+
return {
|
|
42165
|
+
txHash: receipt.transactionHash,
|
|
42166
|
+
blockNumber: hexToSafeNumber(receipt.blockNumber, "blockNumber"),
|
|
42167
|
+
events: extractEvents(abi, receipt.logs, address)
|
|
42168
|
+
};
|
|
42169
|
+
}
|
|
42170
|
+
};
|
|
42171
|
+
}
|
|
42172
|
+
function extractEvents(abi, logs, contractAddress) {
|
|
42173
|
+
const events = /* @__PURE__ */ Object.create(null);
|
|
42174
|
+
for (const entry of abi) {
|
|
42175
|
+
if (entry.type === "event") {
|
|
42176
|
+
const name2 = entry.name;
|
|
42177
|
+
if (typeof name2 === "string") events[name2] = [];
|
|
42178
|
+
}
|
|
42179
|
+
}
|
|
42180
|
+
const lowerContract = contractAddress.toLowerCase();
|
|
42181
|
+
for (const log of logs) {
|
|
42182
|
+
if (log.address.toLowerCase() !== lowerContract) continue;
|
|
42183
|
+
const decoded = decodeLog(abi, log);
|
|
42184
|
+
if (!decoded) continue;
|
|
42185
|
+
(events[decoded.eventName] ??= []).push(decoded.args);
|
|
42186
|
+
}
|
|
42187
|
+
return events;
|
|
42188
|
+
}
|
|
42189
|
+
function hexToSafeNumber(hex2, fieldName) {
|
|
42190
|
+
let big;
|
|
42191
|
+
try {
|
|
42192
|
+
big = BigInt(hex2);
|
|
42193
|
+
} catch {
|
|
42194
|
+
throw new TwakValidationError(`Malformed hex for ${fieldName}: ${hex2}`, {
|
|
42195
|
+
kind: "invalidHex",
|
|
42196
|
+
fieldName
|
|
42197
|
+
});
|
|
42198
|
+
}
|
|
42199
|
+
if (big > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
42200
|
+
throw new TwakValidationError(`${fieldName} exceeds safe integer range (${hex2})`, {
|
|
42201
|
+
kind: "unsafeNumber",
|
|
42202
|
+
fieldName
|
|
42203
|
+
});
|
|
42204
|
+
}
|
|
42205
|
+
return Number(big);
|
|
42206
|
+
}
|
|
42207
|
+
function chainKeyToCaip2(chainKey) {
|
|
42208
|
+
const def = requireChain(chainKey);
|
|
42209
|
+
return `${def.namespace}:${def.chainId}`;
|
|
42210
|
+
}
|
|
42211
|
+
var JS_INTERNAL_PROPS = /* @__PURE__ */ new Set([
|
|
42212
|
+
"then",
|
|
42213
|
+
"toJSON",
|
|
42214
|
+
"toString",
|
|
42215
|
+
"inspect",
|
|
42216
|
+
"constructor",
|
|
42217
|
+
"Symbol(nodejs.util.inspect.custom)"
|
|
42218
|
+
]);
|
|
42219
|
+
function isJsInternal(prop) {
|
|
42220
|
+
return JS_INTERNAL_PROPS.has(prop);
|
|
42221
|
+
}
|
|
42222
|
+
function defineContract(def) {
|
|
42223
|
+
const { id, abi, addresses } = def;
|
|
42224
|
+
return {
|
|
42225
|
+
id,
|
|
42226
|
+
abi,
|
|
42227
|
+
using(client) {
|
|
42228
|
+
return {
|
|
42229
|
+
id,
|
|
42230
|
+
abi,
|
|
42231
|
+
withAddress(address, chainKey) {
|
|
42232
|
+
return createContractInstance({ abi, address, chainKey, client });
|
|
42233
|
+
},
|
|
42234
|
+
bindAt(chainKey) {
|
|
42235
|
+
const address = lookupAddress(id, addresses, chainKey);
|
|
42236
|
+
return createContractInstance({ abi, address, chainKey, client });
|
|
42237
|
+
},
|
|
42238
|
+
resolve(opts) {
|
|
42239
|
+
const address = opts.addressOverride ?? lookupAddress(id, addresses, opts.chainKey);
|
|
42240
|
+
return createContractInstance({ abi, address, chainKey: opts.chainKey, client });
|
|
42241
|
+
}
|
|
42242
|
+
};
|
|
42243
|
+
}
|
|
42244
|
+
};
|
|
42245
|
+
}
|
|
42246
|
+
function lookupAddress(id, addresses, chainKey) {
|
|
42247
|
+
if (!addresses) {
|
|
42248
|
+
throw new TwakValidationError(
|
|
42249
|
+
`Contract "${id}" has no address registry; use .withAddress(address, chainKey)`,
|
|
42250
|
+
{ kind: "noAddressRegistry", contractId: id, chainKey }
|
|
42251
|
+
);
|
|
42252
|
+
}
|
|
42253
|
+
const address = addresses[chainKey];
|
|
42254
|
+
if (!address) {
|
|
42255
|
+
throw new TwakValidationError(
|
|
42256
|
+
`Contract "${id}" has no address for chain "${chainKey}". Known: ${Object.keys(addresses).join(", ") || "(none)"}`,
|
|
42257
|
+
{ kind: "noAddressForChain", contractId: id, chainKey }
|
|
42258
|
+
);
|
|
42259
|
+
}
|
|
42260
|
+
return address;
|
|
42261
|
+
}
|
|
42262
|
+
var ERC20_ABI = [
|
|
42263
|
+
{
|
|
42264
|
+
type: "function",
|
|
42265
|
+
name: "transfer",
|
|
42266
|
+
inputs: [
|
|
42267
|
+
{ name: "to", type: "address" },
|
|
42268
|
+
{ name: "amount", type: "uint256" }
|
|
42269
|
+
],
|
|
42270
|
+
outputs: [{ name: "", type: "bool" }],
|
|
42271
|
+
stateMutability: "nonpayable"
|
|
42272
|
+
},
|
|
42273
|
+
{
|
|
42274
|
+
type: "function",
|
|
42275
|
+
name: "approve",
|
|
42276
|
+
inputs: [
|
|
42277
|
+
{ name: "spender", type: "address" },
|
|
42278
|
+
{ name: "amount", type: "uint256" }
|
|
42279
|
+
],
|
|
42280
|
+
outputs: [{ name: "", type: "bool" }],
|
|
42281
|
+
stateMutability: "nonpayable"
|
|
42282
|
+
},
|
|
42283
|
+
{
|
|
42284
|
+
type: "function",
|
|
42285
|
+
name: "allowance",
|
|
42286
|
+
inputs: [
|
|
42287
|
+
{ name: "owner", type: "address" },
|
|
42288
|
+
{ name: "spender", type: "address" }
|
|
42289
|
+
],
|
|
42290
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
42291
|
+
stateMutability: "view"
|
|
42292
|
+
},
|
|
42293
|
+
{
|
|
42294
|
+
type: "function",
|
|
42295
|
+
name: "balanceOf",
|
|
42296
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
42297
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
42298
|
+
stateMutability: "view"
|
|
42299
|
+
},
|
|
42300
|
+
{
|
|
42301
|
+
type: "event",
|
|
42302
|
+
name: "Transfer",
|
|
42303
|
+
inputs: [
|
|
42304
|
+
{ name: "from", type: "address", indexed: true },
|
|
42305
|
+
{ name: "to", type: "address", indexed: true },
|
|
42306
|
+
{ name: "value", type: "uint256" }
|
|
42307
|
+
],
|
|
42308
|
+
anonymous: false
|
|
42309
|
+
},
|
|
42310
|
+
{
|
|
42311
|
+
type: "event",
|
|
42312
|
+
name: "Approval",
|
|
42313
|
+
inputs: [
|
|
42314
|
+
{ name: "owner", type: "address", indexed: true },
|
|
42315
|
+
{ name: "spender", type: "address", indexed: true },
|
|
42316
|
+
{ name: "value", type: "uint256" }
|
|
42317
|
+
],
|
|
42318
|
+
anonymous: false
|
|
42319
|
+
}
|
|
42320
|
+
];
|
|
42321
|
+
var erc20 = defineContract({ id: "erc20", abi: ERC20_ABI });
|
|
42322
|
+
|
|
42323
|
+
// src/commands/swap.ts
|
|
41566
42324
|
var MAX_SLIPPAGE_PCT = 50;
|
|
41567
42325
|
var WARN_SLIPPAGE_PCT = 5;
|
|
41568
42326
|
function registerSwapCommand(program3) {
|
|
@@ -41736,24 +42494,13 @@ function registerSwapCommand(program3) {
|
|
|
41736
42494
|
const fromAssetAddr = firstStep.from.asset.address;
|
|
41737
42495
|
const isNative = !fromAssetAddr || fromAssetAddr === NATIVE_TOKEN_ADDRESS;
|
|
41738
42496
|
if (!isNative && chainConfig.coinId) {
|
|
41739
|
-
const assetId = formatTokenId(chainConfig.coinId, fromAssetAddr);
|
|
41740
42497
|
try {
|
|
41741
|
-
const allowance = await blockchainClient.
|
|
41742
|
-
assetId,
|
|
41743
|
-
owner: walletAddress,
|
|
41744
|
-
spender: stepApproval.operatorAddress
|
|
41745
|
-
});
|
|
42498
|
+
const allowance = await blockchainClient.contract(erc20).withAddress(fromAssetAddr, fromChain).read.allowance([walletAddress, stepApproval.operatorAddress]);
|
|
41746
42499
|
const requiredAmount = BigInt(stepApproval.minAmount || parsedAmount);
|
|
41747
42500
|
if (allowance < requiredAmount) {
|
|
41748
42501
|
info("Sending token approval...");
|
|
41749
|
-
const
|
|
41750
|
-
|
|
41751
|
-
assetId,
|
|
41752
|
-
spender: stepApproval.operatorAddress,
|
|
41753
|
-
amount: requiredAmount.toString(),
|
|
41754
|
-
hdWallet
|
|
41755
|
-
});
|
|
41756
|
-
info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.hash)}`);
|
|
42502
|
+
const approveResult = await blockchainClient.contract(erc20).withAddress(fromAssetAddr, fromChain).write.approve([stepApproval.operatorAddress, requiredAmount], provider);
|
|
42503
|
+
info(`Approval tx: ${getExplorerTxUrl(fromChain, approveResult.txHash)}`);
|
|
41757
42504
|
const approvalDeadline = Date.now() + 6e4;
|
|
41758
42505
|
while (Date.now() < approvalDeadline) {
|
|
41759
42506
|
await new Promise((r3) => setTimeout(r3, 2e3));
|
|
@@ -41761,7 +42508,7 @@ function registerSwapCommand(program3) {
|
|
|
41761
42508
|
const status = await blockchainClient.findTransactionStatus(
|
|
41762
42509
|
fromChain,
|
|
41763
42510
|
walletAddress,
|
|
41764
|
-
approveResult.
|
|
42511
|
+
approveResult.txHash
|
|
41765
42512
|
);
|
|
41766
42513
|
if (status.confirmed) break;
|
|
41767
42514
|
if (status.failed)
|
|
@@ -41870,10 +42617,10 @@ function registerSwapCommand(program3) {
|
|
|
41870
42617
|
|
|
41871
42618
|
// src/commands/erc20.ts
|
|
41872
42619
|
init_dist();
|
|
41873
|
-
|
|
42620
|
+
init_dist3();
|
|
41874
42621
|
function registerErc20Command(program3) {
|
|
41875
|
-
const
|
|
41876
|
-
|
|
42622
|
+
const erc202 = program3.command("erc20").description("ERC-20 token operations");
|
|
42623
|
+
erc202.command("approve").description("Approve a spender for token spending").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address").requiredOption("--amount <n>", 'Amount to approve (in smallest unit, or "unlimited")').option(
|
|
41877
42624
|
"--password <password>",
|
|
41878
42625
|
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
41879
42626
|
).option(
|
|
@@ -41917,19 +42664,13 @@ function registerErc20Command(program3) {
|
|
|
41917
42664
|
}
|
|
41918
42665
|
info(`Approving ${opts.spender} to spend from ${address} on ${chainKey}...`);
|
|
41919
42666
|
const client = provider.getBlockchainClient();
|
|
41920
|
-
const
|
|
41921
|
-
const
|
|
41922
|
-
assetId: opts.token,
|
|
41923
|
-
spender: opts.spender,
|
|
41924
|
-
amount,
|
|
41925
|
-
hdWallet
|
|
41926
|
-
});
|
|
41927
|
-
const explorerUrl = getExplorerTxUrl(result.chainKey, result.hash);
|
|
42667
|
+
const pending = await client.contract(erc20).withAddress(contractAddress, chainKey).write.approve([opts.spender, BigInt(amount)], provider);
|
|
42668
|
+
const explorerUrl = getExplorerTxUrl(chainKey, pending.txHash);
|
|
41928
42669
|
success("Approval sent!");
|
|
41929
42670
|
output(
|
|
41930
42671
|
{
|
|
41931
|
-
hash:
|
|
41932
|
-
chain:
|
|
42672
|
+
hash: pending.txHash,
|
|
42673
|
+
chain: chainKey,
|
|
41933
42674
|
owner: address,
|
|
41934
42675
|
spender: opts.spender,
|
|
41935
42676
|
token: opts.token,
|
|
@@ -41943,7 +42684,7 @@ function registerErc20Command(program3) {
|
|
|
41943
42684
|
}
|
|
41944
42685
|
safeExit();
|
|
41945
42686
|
});
|
|
41946
|
-
|
|
42687
|
+
erc202.command("revoke").description("Revoke token approval (set allowance to 0)").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--spender <address>", "Spender address to revoke").option(
|
|
41947
42688
|
"--password <password>",
|
|
41948
42689
|
"Wallet password (falls back to keychain / TWAK_WALLET_PASSWORD)"
|
|
41949
42690
|
).option("--json", "JSON output").action(async (opts) => {
|
|
@@ -41974,19 +42715,13 @@ function registerErc20Command(program3) {
|
|
|
41974
42715
|
const address = await provider.getAddress(chainKey);
|
|
41975
42716
|
info(`Revoking approval for ${opts.spender} on ${chainKey}...`);
|
|
41976
42717
|
const client = provider.getBlockchainClient();
|
|
41977
|
-
const
|
|
41978
|
-
const
|
|
41979
|
-
assetId: opts.token,
|
|
41980
|
-
spender: opts.spender,
|
|
41981
|
-
amount: "0",
|
|
41982
|
-
hdWallet
|
|
41983
|
-
});
|
|
41984
|
-
const explorerUrl = getExplorerTxUrl(result.chainKey, result.hash);
|
|
42718
|
+
const pending = await client.contract(erc20).withAddress(contractAddress, chainKey).write.approve([opts.spender, 0n], provider);
|
|
42719
|
+
const explorerUrl = getExplorerTxUrl(chainKey, pending.txHash);
|
|
41985
42720
|
success("Approval revoked!");
|
|
41986
42721
|
output(
|
|
41987
42722
|
{
|
|
41988
|
-
hash:
|
|
41989
|
-
chain:
|
|
42723
|
+
hash: pending.txHash,
|
|
42724
|
+
chain: chainKey,
|
|
41990
42725
|
owner: address,
|
|
41991
42726
|
spender: opts.spender,
|
|
41992
42727
|
token: opts.token,
|
|
@@ -41999,7 +42734,7 @@ function registerErc20Command(program3) {
|
|
|
41999
42734
|
}
|
|
42000
42735
|
safeExit();
|
|
42001
42736
|
});
|
|
42002
|
-
|
|
42737
|
+
erc202.command("allowance").description("Check token allowance").requiredOption("--token <assetId>", "Token asset ID (e.g., c60_t0xA0b8...)").requiredOption("--owner <address>", "Owner address").requiredOption("--spender <address>", "Spender address").option("--json", "JSON output").action(async (opts) => {
|
|
42003
42738
|
try {
|
|
42004
42739
|
await ensureTrustSDK();
|
|
42005
42740
|
let coinId;
|
|
@@ -42019,10 +42754,10 @@ function registerErc20Command(program3) {
|
|
|
42019
42754
|
);
|
|
42020
42755
|
}
|
|
42021
42756
|
try {
|
|
42022
|
-
const { getWalletCore: getWalletCore2, getCoinType: getCoinType2 } = await Promise.resolve().then(() => (
|
|
42023
|
-
const
|
|
42757
|
+
const { getWalletCore: getWalletCore2, getCoinType: getCoinType2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports2));
|
|
42758
|
+
const wc3 = getWalletCore2();
|
|
42024
42759
|
const ct5 = getCoinType2(coinId);
|
|
42025
|
-
if (ct5 && !
|
|
42760
|
+
if (ct5 && !wc3.CoinTypeExt.validate(ct5, contractAddress)) {
|
|
42026
42761
|
throw new Erc20Error(
|
|
42027
42762
|
"VALIDATION_ERROR",
|
|
42028
42763
|
`Invalid contract address in asset ID: ${contractAddress}`
|
|
@@ -42031,12 +42766,15 @@ function registerErc20Command(program3) {
|
|
|
42031
42766
|
} catch (err) {
|
|
42032
42767
|
if (err instanceof CommandError) throw err;
|
|
42033
42768
|
}
|
|
42769
|
+
const chainKey = coinTypeToChainKey(coinId);
|
|
42770
|
+
if (!chainKey) {
|
|
42771
|
+
throw new Erc20Error(
|
|
42772
|
+
"CHAIN_UNSUPPORTED",
|
|
42773
|
+
`Unsupported coin type in asset ID: ${opts.token}`
|
|
42774
|
+
);
|
|
42775
|
+
}
|
|
42034
42776
|
const client = new BlockchainClient();
|
|
42035
|
-
const allowance = await client.
|
|
42036
|
-
assetId: opts.token,
|
|
42037
|
-
owner: opts.owner,
|
|
42038
|
-
spender: opts.spender
|
|
42039
|
-
});
|
|
42777
|
+
const allowance = await client.contract(erc20).withAddress(contractAddress, chainKey).read.allowance([opts.owner, opts.spender]);
|
|
42040
42778
|
output(
|
|
42041
42779
|
{
|
|
42042
42780
|
token: opts.token,
|
|
@@ -42072,8 +42810,8 @@ async function resolveTokenLabel(tokenId, service) {
|
|
|
42072
42810
|
const chainTokens = COMMON_TOKENS[chainKey];
|
|
42073
42811
|
if (chainTokens) {
|
|
42074
42812
|
const normalizedAddr = contractAddress.toLowerCase();
|
|
42075
|
-
for (const [sym,
|
|
42076
|
-
if (
|
|
42813
|
+
for (const [sym, entry] of Object.entries(chainTokens)) {
|
|
42814
|
+
if (entry.address.toLowerCase() === normalizedAddr) {
|
|
42077
42815
|
await assetCache.set(tokenId, sym);
|
|
42078
42816
|
return sym;
|
|
42079
42817
|
}
|
|
@@ -42350,7 +43088,7 @@ var TransactionService = class {
|
|
|
42350
43088
|
};
|
|
42351
43089
|
|
|
42352
43090
|
// src/commands/query.ts
|
|
42353
|
-
|
|
43091
|
+
init_dist3();
|
|
42354
43092
|
var import_chalk7 = __toESM(require_source());
|
|
42355
43093
|
function handleError(err, json) {
|
|
42356
43094
|
handleCommandError(err, json ?? false, "Query failed");
|
|
@@ -42364,7 +43102,7 @@ function registerQueryCommand(program3) {
|
|
|
42364
43102
|
const client = await getClient();
|
|
42365
43103
|
const swapClient = new SwapClient(client);
|
|
42366
43104
|
const tokens = token.split(",").map((t) => t.trim()).filter(Boolean);
|
|
42367
|
-
const
|
|
43105
|
+
const resolveChain2 = (t) => {
|
|
42368
43106
|
if (opts.chain) return opts.chain;
|
|
42369
43107
|
if (Object.prototype.hasOwnProperty.call(CHAINS, t.toLowerCase())) return t.toLowerCase();
|
|
42370
43108
|
const bySymbol = Object.entries(CHAINS).find(
|
|
@@ -42387,7 +43125,7 @@ function registerQueryCommand(program3) {
|
|
|
42387
43125
|
const assetIds = [];
|
|
42388
43126
|
const tokenMeta = [];
|
|
42389
43127
|
for (const t of tokens) {
|
|
42390
|
-
const chain3 =
|
|
43128
|
+
const chain3 = resolveChain2(t);
|
|
42391
43129
|
const info2 = resolveToken(t, chain3);
|
|
42392
43130
|
if (!info2) {
|
|
42393
43131
|
warn(`Unknown token: ${t.toUpperCase()}`);
|
|
@@ -42432,7 +43170,7 @@ function registerQueryCommand(program3) {
|
|
|
42432
43170
|
}
|
|
42433
43171
|
return;
|
|
42434
43172
|
}
|
|
42435
|
-
const chain2 =
|
|
43173
|
+
const chain2 = resolveChain2(token);
|
|
42436
43174
|
const tokenInfo = resolveToken(token, chain2);
|
|
42437
43175
|
let price = null;
|
|
42438
43176
|
if (tokenInfo) {
|
|
@@ -42695,10 +43433,10 @@ function registerQueryCommand(program3) {
|
|
|
42695
43433
|
if (!chain2) throw new QueryError("CHAIN_UNSUPPORTED", `Unknown coin ID: ${coinId}`);
|
|
42696
43434
|
try {
|
|
42697
43435
|
await ensureTrustSDK();
|
|
42698
|
-
const { getWalletCore: getWalletCore2, getCoinType: getCoinType2 } = await Promise.resolve().then(() => (
|
|
42699
|
-
const
|
|
43436
|
+
const { getWalletCore: getWalletCore2, getCoinType: getCoinType2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports2));
|
|
43437
|
+
const wc3 = getWalletCore2();
|
|
42700
43438
|
const ct5 = getCoinType2(coinId);
|
|
42701
|
-
if (ct5 && !
|
|
43439
|
+
if (ct5 && !wc3.CoinTypeExt.validate(ct5, opts.address)) {
|
|
42702
43440
|
throw new QueryError("VALIDATION_ERROR", `Invalid address format for ${chain2.name}`);
|
|
42703
43441
|
}
|
|
42704
43442
|
} catch (err) {
|
|
@@ -42777,11 +43515,11 @@ function registerQueryCommand(program3) {
|
|
|
42777
43515
|
const coinId = parseInt(coinIdStr, 10);
|
|
42778
43516
|
try {
|
|
42779
43517
|
await ensureTrustSDK();
|
|
42780
|
-
const { getWalletCore: getWalletCore2, getCoinType: getCoinType2 } = await Promise.resolve().then(() => (
|
|
42781
|
-
const
|
|
43518
|
+
const { getWalletCore: getWalletCore2, getCoinType: getCoinType2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports2));
|
|
43519
|
+
const wc3 = getWalletCore2();
|
|
42782
43520
|
const ct5 = getCoinType2(coinId);
|
|
42783
43521
|
if (ct5) {
|
|
42784
|
-
const isValid =
|
|
43522
|
+
const isValid = wc3.CoinTypeExt.validate(ct5, opts.address);
|
|
42785
43523
|
if (!isValid) {
|
|
42786
43524
|
const chainInfo = getChainByCoinId(coinId);
|
|
42787
43525
|
const label = chainInfo?.name ?? `coin ${coinId}`;
|
|
@@ -42940,6 +43678,7 @@ init_dist();
|
|
|
42940
43678
|
var import_zod5 = require("zod");
|
|
42941
43679
|
init_dist();
|
|
42942
43680
|
var import_zod6 = require("zod");
|
|
43681
|
+
init_dist();
|
|
42943
43682
|
var import_zod7 = require("zod");
|
|
42944
43683
|
init_dist();
|
|
42945
43684
|
var import_zod8 = require("zod");
|
|
@@ -42950,6 +43689,7 @@ var import_zod10 = require("zod");
|
|
|
42950
43689
|
init_dist3();
|
|
42951
43690
|
init_dist();
|
|
42952
43691
|
var import_zod11 = require("zod");
|
|
43692
|
+
init_dist();
|
|
42953
43693
|
var import_zod12 = require("zod");
|
|
42954
43694
|
init_dist();
|
|
42955
43695
|
var import_zod13 = require("zod");
|
|
@@ -43383,7 +44123,6 @@ async function payAndFetch(args) {
|
|
|
43383
44123
|
blockchainClient: bc2,
|
|
43384
44124
|
chainKey,
|
|
43385
44125
|
chainId: evmChainId,
|
|
43386
|
-
coinId: CHAINS[chainKey].coinId,
|
|
43387
44126
|
owner: fromAddress,
|
|
43388
44127
|
tokenAddress,
|
|
43389
44128
|
requiredAmount: BigInt(amount),
|
|
@@ -43433,12 +44172,8 @@ async function payAndFetch(args) {
|
|
|
43433
44172
|
}
|
|
43434
44173
|
var MAX_UINT256_DECIMAL = (2n ** 256n - 1n).toString();
|
|
43435
44174
|
async function ensurePermit2Allowance(args) {
|
|
43436
|
-
const
|
|
43437
|
-
const allowance = await
|
|
43438
|
-
assetId,
|
|
43439
|
-
owner: args.owner,
|
|
43440
|
-
spender: PERMIT2_ADDRESS
|
|
43441
|
-
});
|
|
44175
|
+
const token = args.blockchainClient.contract(erc20).withAddress(args.tokenAddress, args.chainKey);
|
|
44176
|
+
const allowance = await token.read.allowance([args.owner, PERMIT2_ADDRESS]);
|
|
43442
44177
|
if (allowance >= args.requiredAmount) return null;
|
|
43443
44178
|
const ctx = {
|
|
43444
44179
|
token: args.tokenAddress,
|
|
@@ -43449,19 +44184,18 @@ async function ensurePermit2Allowance(args) {
|
|
|
43449
44184
|
};
|
|
43450
44185
|
const approved = await args.confirmApproval?.(ctx) === true;
|
|
43451
44186
|
if (!approved) {
|
|
44187
|
+
const assetId = formatTokenId(CHAINS[args.chainKey].coinId, args.tokenAddress);
|
|
43452
44188
|
throw new X402Error(
|
|
43453
44189
|
"PERMIT2_APPROVAL_REQUIRED",
|
|
43454
|
-
`Permit2 needs a one-time approval for ${args.tokenAddress} on ${args.chainKey}. Run: twak erc20 approve --
|
|
44190
|
+
`Permit2 needs a one-time approval for ${args.tokenAddress} on ${args.chainKey}. Run: twak erc20 approve --token ${assetId} --spender ${PERMIT2_ADDRESS} --amount unlimited`
|
|
43455
44191
|
);
|
|
43456
44192
|
}
|
|
43457
|
-
const
|
|
43458
|
-
|
|
43459
|
-
args.
|
|
43460
|
-
PERMIT2_ADDRESS,
|
|
43461
|
-
MAX_UINT256_DECIMAL
|
|
44193
|
+
const pending = await token.write.approve(
|
|
44194
|
+
[PERMIT2_ADDRESS, BigInt(MAX_UINT256_DECIMAL)],
|
|
44195
|
+
args.walletProvider
|
|
43462
44196
|
);
|
|
43463
|
-
await waitForTxConfirmation(args.blockchainClient, args.chainKey, args.owner, txHash);
|
|
43464
|
-
return { token: args.tokenAddress, chain: args.chainKey, txHash };
|
|
44197
|
+
await waitForTxConfirmation(args.blockchainClient, args.chainKey, args.owner, pending.txHash);
|
|
44198
|
+
return { token: args.tokenAddress, chain: args.chainKey, txHash: pending.txHash };
|
|
43465
44199
|
}
|
|
43466
44200
|
var APPROVE_TIMEOUT_MS = 6e4;
|
|
43467
44201
|
var APPROVE_POLL_MS = 2e3;
|
|
@@ -43812,6 +44546,8 @@ var ActionRegistry = class {
|
|
|
43812
44546
|
return this.providers;
|
|
43813
44547
|
}
|
|
43814
44548
|
};
|
|
44549
|
+
var CHAIN_DESC = 'Chain (e.g., "ethereum", "bsc", "solana"). Accepts chain key, CAIP-2 ("eip155:1"), EVM chainId ("56", "0x38"), aliases ("matic", "op"), and asset IDs ("c60").';
|
|
44550
|
+
var TOKEN_ADDR_DESC = "Token contract address (ERC-20 / SPL). Omit for the chain native coin (ETH, BNB, SOL, etc.).";
|
|
43815
44551
|
var BalanceActionProvider = class extends ActionProvider {
|
|
43816
44552
|
name = "balance";
|
|
43817
44553
|
description = "Check token balances and holdings";
|
|
@@ -43821,25 +44557,14 @@ var BalanceActionProvider = class extends ActionProvider {
|
|
|
43821
44557
|
getBalance() {
|
|
43822
44558
|
return {
|
|
43823
44559
|
name: "get_balance",
|
|
43824
|
-
description: 'Read the on-chain balance of a single asset for a wallet address.
|
|
44560
|
+
description: 'Read the on-chain balance of a single asset for a wallet address. Omit `tokenAddress` for the chain native coin (ETH, BNB, SOL, BTC, ...). Provide `tokenAddress` to read an ERC-20 / SPL token (e.g. tokenAddress="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" with chain="base" for USDC on Base).',
|
|
43825
44561
|
schema: import_zod3.z.object({
|
|
43826
44562
|
address: import_zod3.z.string().describe("Wallet address on the specified chain."),
|
|
43827
|
-
|
|
43828
|
-
|
|
43829
|
-
),
|
|
43830
|
-
tokenAddress: import_zod3.z.string().trim().min(1).optional().describe(
|
|
43831
|
-
"Optional. ERC-20 or SPL token contract address on the chosen chain. Omit (or pass undefined) to read the chain's native coin balance. Empty or whitespace-only strings are rejected."
|
|
43832
|
-
)
|
|
44563
|
+
chain: import_zod3.z.string().describe(CHAIN_DESC),
|
|
44564
|
+
tokenAddress: import_zod3.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC)
|
|
43833
44565
|
}),
|
|
43834
44566
|
invoke: async (ctx, input2) => {
|
|
43835
|
-
const
|
|
43836
|
-
if (coinId === void 0) {
|
|
43837
|
-
throw new WalletError(
|
|
43838
|
-
"CHAIN_UNSUPPORTED",
|
|
43839
|
-
`Unsupported chain: "${input2.chainKey}". Supported keys include "ethereum", "bsc", "base", "polygon", "arbitrum", "optimism", "avalanche", "solana", "bitcoin" and others.`
|
|
43840
|
-
);
|
|
43841
|
-
}
|
|
43842
|
-
const assetId = formatTokenId(coinId, input2.tokenAddress);
|
|
44567
|
+
const { assetId } = resolveAsset(input2.chain, input2.tokenAddress);
|
|
43843
44568
|
return fetchBalance(ctx.httpClient, assetId, input2.address);
|
|
43844
44569
|
}
|
|
43845
44570
|
};
|
|
@@ -43847,26 +44572,32 @@ var BalanceActionProvider = class extends ActionProvider {
|
|
|
43847
44572
|
getTokenHoldings() {
|
|
43848
44573
|
return {
|
|
43849
44574
|
name: "get_token_holdings",
|
|
43850
|
-
description: "Get ERC-20 token holdings for
|
|
44575
|
+
description: "Get ERC-20 / SPL token holdings for a wallet address on a chain. Returns the list of tokens the address holds. Omit `tokenAddress` to list all token holdings on the chain.",
|
|
43851
44576
|
schema: import_zod3.z.object({
|
|
43852
|
-
address: import_zod3.z.string().describe("Wallet address"),
|
|
43853
|
-
|
|
44577
|
+
address: import_zod3.z.string().describe("Wallet address on the specified chain."),
|
|
44578
|
+
chain: import_zod3.z.string().describe(CHAIN_DESC),
|
|
44579
|
+
tokenAddress: import_zod3.z.string().trim().min(1).optional().describe(
|
|
44580
|
+
"Token contract address (ERC-20 / SPL). Omit to list all token holdings on the chain; provide an address to filter to that single token."
|
|
44581
|
+
)
|
|
43854
44582
|
}),
|
|
43855
44583
|
invoke: async (ctx, input2) => {
|
|
44584
|
+
const { chainKey, coinId } = resolveAsset(input2.chain);
|
|
43856
44585
|
const response = await ctx.httpClient.gateway(
|
|
43857
44586
|
"POST",
|
|
43858
44587
|
"/v1/assets",
|
|
43859
44588
|
{
|
|
43860
44589
|
version: 2,
|
|
43861
44590
|
includeDisabledAssets: false,
|
|
43862
|
-
assets: [{ address: input2.address, coin:
|
|
44591
|
+
assets: [{ address: input2.address, coin: coinId }]
|
|
43863
44592
|
}
|
|
43864
44593
|
);
|
|
43865
44594
|
const holdings = Array.isArray(response) ? response : response.docs ?? response.data ?? [];
|
|
44595
|
+
const tokenFilter = input2.tokenAddress?.toLowerCase();
|
|
43866
44596
|
const tokens = [];
|
|
43867
44597
|
for (const h4 of holdings) {
|
|
43868
44598
|
if (Array.isArray(h4.tokens)) {
|
|
43869
44599
|
for (const t of h4.tokens) {
|
|
44600
|
+
if (tokenFilter && String(t.contract).toLowerCase() !== tokenFilter) continue;
|
|
43870
44601
|
tokens.push({
|
|
43871
44602
|
symbol: t.symbol ?? t.contract,
|
|
43872
44603
|
contract: t.contract,
|
|
@@ -43878,7 +44609,7 @@ var BalanceActionProvider = class extends ActionProvider {
|
|
|
43878
44609
|
}
|
|
43879
44610
|
}
|
|
43880
44611
|
}
|
|
43881
|
-
return { address: input2.address,
|
|
44612
|
+
return { address: input2.address, chain: chainKey, tokens };
|
|
43882
44613
|
}
|
|
43883
44614
|
};
|
|
43884
44615
|
}
|
|
@@ -43888,7 +44619,9 @@ var BalanceActionProvider = class extends ActionProvider {
|
|
|
43888
44619
|
description: "Search for tokens by name, symbol, or address",
|
|
43889
44620
|
schema: import_zod3.z.object({
|
|
43890
44621
|
query: import_zod3.z.string().describe("Search query"),
|
|
43891
|
-
|
|
44622
|
+
chains: import_zod3.z.string().optional().describe(
|
|
44623
|
+
`Comma-separated list of chains to restrict the search. Each entry accepts the same forms as the canonical \`chain\` input. ${CHAIN_DESC}`
|
|
44624
|
+
),
|
|
43892
44625
|
limit: import_zod3.z.coerce.number().optional().default(10).describe("Max results")
|
|
43893
44626
|
}),
|
|
43894
44627
|
invoke: async (ctx, input2) => {
|
|
@@ -43898,52 +44631,41 @@ var BalanceActionProvider = class extends ActionProvider {
|
|
|
43898
44631
|
version: "24",
|
|
43899
44632
|
limit: (input2.limit || 10).toString()
|
|
43900
44633
|
});
|
|
43901
|
-
|
|
44634
|
+
const chainsInput = input2.chains;
|
|
44635
|
+
if (chainsInput) {
|
|
44636
|
+
const coinIds = chainsInput.split(",").map((c3) => c3.trim()).filter((c3) => c3.length > 0).map((c3) => resolveAsset(c3).coinId.toString()).join(",");
|
|
44637
|
+
if (coinIds) params.set("networks", coinIds);
|
|
44638
|
+
}
|
|
43902
44639
|
return ctx.httpClient.gateway("GET", "/v1/search/assets", void 0, params.toString());
|
|
43903
44640
|
}
|
|
43904
44641
|
};
|
|
43905
44642
|
}
|
|
43906
44643
|
};
|
|
43907
|
-
var ERC20_TRANSFER_SIG = "0xa9059cbb";
|
|
43908
44644
|
var NATIVE_TOKEN_PLACEHOLDER = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
43909
44645
|
var DEFAULT_MAX_TRANSFER_USD2 = 1e4;
|
|
43910
|
-
function
|
|
43911
|
-
const
|
|
43912
|
-
const
|
|
43913
|
-
|
|
43914
|
-
}
|
|
43915
|
-
function extractEvmChainId(chainId) {
|
|
43916
|
-
if (chainId.startsWith("eip155:")) return chainId.split(":")[1];
|
|
43917
|
-
return chainId;
|
|
43918
|
-
}
|
|
43919
|
-
function toCaip2(chain2) {
|
|
43920
|
-
let caip2;
|
|
43921
|
-
if (chain2.includes(":")) {
|
|
43922
|
-
caip2 = chain2;
|
|
43923
|
-
} else {
|
|
43924
|
-
const def = getChainById(chain2);
|
|
43925
|
-
if (!def) {
|
|
43926
|
-
throw new TransferError(
|
|
43927
|
-
"CHAIN_UNSUPPORTED",
|
|
43928
|
-
`Unknown chain: ${chain2}. Use a chain key like ethereum, bsc, polygon, arbitrum, optimism, base, or a CAIP-2 id like eip155:1.`
|
|
43929
|
-
);
|
|
43930
|
-
}
|
|
43931
|
-
caip2 = `${def.namespace}:${def.chainId}`;
|
|
43932
|
-
}
|
|
43933
|
-
if (!caip2.startsWith("eip155:")) {
|
|
44646
|
+
function resolveEvmChain(chain2) {
|
|
44647
|
+
const chainKey = resolveChain(chain2);
|
|
44648
|
+
const def = requireChain(chainKey);
|
|
44649
|
+
if (def.namespace !== "eip155") {
|
|
43934
44650
|
throw new TransferError(
|
|
43935
44651
|
"CHAIN_UNSUPPORTED",
|
|
43936
|
-
`Transfer is only supported for EVM chains
|
|
44652
|
+
`Transfer is only supported for EVM chains. Got: ${chain2}`
|
|
43937
44653
|
);
|
|
43938
44654
|
}
|
|
43939
|
-
return caip2;
|
|
44655
|
+
return { chainKey, caip2: `eip155:${def.chainId}` };
|
|
43940
44656
|
}
|
|
43941
44657
|
var TransferActionProvider = class extends ActionProvider {
|
|
43942
|
-
|
|
44658
|
+
// `blockchainClient` is the 4th (optional) arg, not the 1st, to avoid
|
|
44659
|
+
// churning the ~14 existing test sites that already pass only walletContext
|
|
44660
|
+
// (+ optional priceLookup / maxTransferUsd). TokenActionProvider uses
|
|
44661
|
+
// blockchainClient as its 1st arg — that asymmetry is intentional for this
|
|
44662
|
+
// PR; normalize in a follow-up once the integration branch settles.
|
|
44663
|
+
constructor(walletContext, priceLookup, maxTransferUsd = DEFAULT_MAX_TRANSFER_USD2, blockchainClient) {
|
|
43943
44664
|
super();
|
|
43944
44665
|
this.walletContext = walletContext;
|
|
43945
44666
|
this.priceLookup = priceLookup;
|
|
43946
44667
|
this.maxTransferUsd = maxTransferUsd;
|
|
44668
|
+
this.blockchainClient = blockchainClient;
|
|
43947
44669
|
}
|
|
43948
44670
|
name = "transfer";
|
|
43949
44671
|
description = "Transfer tokens to another address";
|
|
@@ -43955,24 +44677,28 @@ var TransferActionProvider = class extends ActionProvider {
|
|
|
43955
44677
|
name: "transfer",
|
|
43956
44678
|
description: 'Transfer native or ERC-20 tokens to a destination address on any EVM chain. Resolves ENS for the "to" field, applies a USD safety cap, and works with both the local agent wallet and an external WalletConnect-paired wallet.',
|
|
43957
44679
|
schema: import_zod4.z.object({
|
|
43958
|
-
chain: import_zod4.z.string().describe(
|
|
43959
|
-
'Chain name (e.g., ethereum, bsc, polygon, arbitrum, base). CAIP-2 ids like "eip155:1" also accepted.'
|
|
43960
|
-
),
|
|
44680
|
+
chain: import_zod4.z.string().describe(CHAIN_DESC),
|
|
43961
44681
|
to: import_zod4.z.string().describe("Destination address or ENS name (e.g., vitalik.eth)"),
|
|
43962
44682
|
amount: import_zod4.z.string().describe('Amount in human-readable format (e.g., "1.5", "0.01")'),
|
|
43963
|
-
|
|
43964
|
-
decimals: import_zod4.z.coerce.number().
|
|
44683
|
+
tokenAddress: import_zod4.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC),
|
|
44684
|
+
decimals: import_zod4.z.coerce.number().int().min(0).max(36).optional().describe(
|
|
44685
|
+
"Token decimals (non-negative integer). Omit for native coins and for tokens twak recognizes \u2014 decimals are derived from `COMMON_TOKENS` per chain (BSC stables are 18, USDC on Ethereum is 6, etc.). REQUIRED when tokenAddress is a contract twak does not recognize; look up the value on the token contract."
|
|
44686
|
+
)
|
|
43965
44687
|
}),
|
|
43966
44688
|
invoke: async (_ctx, input2) => {
|
|
43967
44689
|
const signer = this.walletContext.activeWallet();
|
|
43968
|
-
const caip2 =
|
|
44690
|
+
const { chainKey, caip2 } = resolveEvmChain(input2.chain);
|
|
44691
|
+
const tokenAddress = input2.tokenAddress?.toLowerCase() === NATIVE_TOKEN_PLACEHOLDER.toLowerCase() ? void 0 : input2.tokenAddress;
|
|
44692
|
+
const decimals = input2.decimals ?? lookupKnownTokenDecimals(chainKey, tokenAddress);
|
|
44693
|
+
if (decimals === void 0) {
|
|
44694
|
+
throw new TransferError(
|
|
44695
|
+
"VALIDATION_ERROR",
|
|
44696
|
+
`Cannot derive decimals for ${tokenAddress} on ${chainKey}. Provide \`decimals\` explicitly.`
|
|
44697
|
+
);
|
|
44698
|
+
}
|
|
43969
44699
|
if (this.priceLookup) {
|
|
43970
44700
|
try {
|
|
43971
|
-
const
|
|
43972
|
-
const chain22 = getChainById(evmId2) ?? getChainByEvmId(evmId2);
|
|
43973
|
-
const chainKey = chain22 ? coinTypeToChainKey(chain22.coinId) ?? evmId2 : caip2;
|
|
43974
|
-
const tokenAddress = input2.token || "";
|
|
43975
|
-
const price = await this.priceLookup.getTokenPrice(chainKey, tokenAddress);
|
|
44701
|
+
const price = await this.priceLookup.getTokenPrice(chainKey, tokenAddress ?? "");
|
|
43976
44702
|
if (price && price > 0) {
|
|
43977
44703
|
const usdValue = parseFloat(input2.amount) * price;
|
|
43978
44704
|
if (usdValue > this.maxTransferUsd) {
|
|
@@ -43987,18 +44713,18 @@ var TransferActionProvider = class extends ActionProvider {
|
|
|
43987
44713
|
}
|
|
43988
44714
|
}
|
|
43989
44715
|
const resolvedTo = await resolveAddressOrName(input2.to);
|
|
43990
|
-
const atomicAmount = parseAmount(input2.amount,
|
|
43991
|
-
const isErc20 =
|
|
44716
|
+
const atomicAmount = parseAmount(input2.amount, decimals);
|
|
44717
|
+
const isErc20 = Boolean(tokenAddress);
|
|
43992
44718
|
let txHash;
|
|
43993
|
-
const evmId = extractEvmChainId(caip2);
|
|
43994
44719
|
if (isErc20) {
|
|
43995
|
-
|
|
43996
|
-
|
|
43997
|
-
|
|
43998
|
-
|
|
43999
|
-
|
|
44000
|
-
|
|
44001
|
-
|
|
44720
|
+
if (!this.blockchainClient) {
|
|
44721
|
+
throw new TransferError(
|
|
44722
|
+
"WALLET_NOT_FOUND",
|
|
44723
|
+
"BlockchainClient not available \u2014 ERC-20 transfer requires a configured blockchain client"
|
|
44724
|
+
);
|
|
44725
|
+
}
|
|
44726
|
+
const pending = await this.blockchainClient.contract(erc20).withAddress(tokenAddress, chainKey).write.transfer([resolvedTo, BigInt(atomicAmount)], signer);
|
|
44727
|
+
txHash = pending.txHash;
|
|
44002
44728
|
} else {
|
|
44003
44729
|
txHash = await signer.sendTransaction({
|
|
44004
44730
|
chainId: caip2,
|
|
@@ -44007,8 +44733,7 @@ var TransferActionProvider = class extends ActionProvider {
|
|
|
44007
44733
|
value: "0x" + BigInt(atomicAmount).toString(16)
|
|
44008
44734
|
});
|
|
44009
44735
|
}
|
|
44010
|
-
const
|
|
44011
|
-
const explorerUrl = getExplorerTxUrl(chain2?.id ?? caip2, txHash);
|
|
44736
|
+
const explorerUrl = getExplorerTxUrl(chainKey, txHash);
|
|
44012
44737
|
return {
|
|
44013
44738
|
success: true,
|
|
44014
44739
|
txHash,
|
|
@@ -44077,9 +44802,9 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44077
44802
|
name: "swap",
|
|
44078
44803
|
description: "Execute a token swap via Trust Wallet (supports all chains including cross-chain)",
|
|
44079
44804
|
schema: import_zod5.z.object({
|
|
44080
|
-
fromChain: import_zod5.z.string().describe(
|
|
44805
|
+
fromChain: import_zod5.z.string().describe(`Source chain. ${CHAIN_DESC}`),
|
|
44081
44806
|
fromToken: import_zod5.z.string().describe("Source token symbol or address"),
|
|
44082
|
-
toChain: import_zod5.z.string().describe(
|
|
44807
|
+
toChain: import_zod5.z.string().describe(`Destination chain. ${CHAIN_DESC}`),
|
|
44083
44808
|
toToken: import_zod5.z.string().describe("Destination token symbol or address"),
|
|
44084
44809
|
amount: import_zod5.z.string().describe("Human-readable amount to swap"),
|
|
44085
44810
|
slippage: import_zod5.z.string().optional().default("1").describe("Slippage tolerance % (max 50)")
|
|
@@ -44089,6 +44814,8 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44089
44814
|
throw new SwapError("WALLET_NOT_FOUND", "Swap client not configured");
|
|
44090
44815
|
}
|
|
44091
44816
|
const signer = this.walletContext.activeWallet();
|
|
44817
|
+
const fromChain = resolveChain(input2.fromChain);
|
|
44818
|
+
const toChain = resolveChain(input2.toChain);
|
|
44092
44819
|
const slippage = parseFloat(input2.slippage ?? "1");
|
|
44093
44820
|
if (isNaN(slippage) || slippage <= 0 || slippage > 50) {
|
|
44094
44821
|
return {
|
|
@@ -44097,28 +44824,28 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44097
44824
|
message: `Slippage must be greater than 0% and at most 50%. Got: ${input2.slippage}`
|
|
44098
44825
|
};
|
|
44099
44826
|
}
|
|
44100
|
-
const fromTokenInfo = resolveToken(input2.fromToken,
|
|
44827
|
+
const fromTokenInfo = resolveToken(input2.fromToken, fromChain);
|
|
44101
44828
|
if (!fromTokenInfo) {
|
|
44102
44829
|
return {
|
|
44103
44830
|
success: false,
|
|
44104
44831
|
code: "TOKEN_NOT_FOUND",
|
|
44105
|
-
message: `Unknown token: ${input2.fromToken} on ${
|
|
44832
|
+
message: `Unknown token: ${input2.fromToken} on ${fromChain}`
|
|
44106
44833
|
};
|
|
44107
44834
|
}
|
|
44108
|
-
const toTokenInfo = resolveToken(input2.toToken,
|
|
44835
|
+
const toTokenInfo = resolveToken(input2.toToken, toChain);
|
|
44109
44836
|
if (!toTokenInfo) {
|
|
44110
44837
|
return {
|
|
44111
44838
|
success: false,
|
|
44112
44839
|
code: "TOKEN_NOT_FOUND",
|
|
44113
|
-
message: `Unknown token: ${input2.toToken} on ${
|
|
44840
|
+
message: `Unknown token: ${input2.toToken} on ${toChain}`
|
|
44114
44841
|
};
|
|
44115
44842
|
}
|
|
44116
44843
|
const parsedAmount = parseAmount(input2.amount, fromTokenInfo.decimals);
|
|
44117
|
-
const address = await signer.getAddress(
|
|
44844
|
+
const address = await signer.getAddress(fromChain);
|
|
44118
44845
|
const quote = await this.swapClient.getQuote({
|
|
44119
|
-
fromChain
|
|
44846
|
+
fromChain,
|
|
44120
44847
|
fromToken: fromTokenInfo.address,
|
|
44121
|
-
toChain
|
|
44848
|
+
toChain,
|
|
44122
44849
|
toToken: toTokenInfo.address,
|
|
44123
44850
|
amount: parsedAmount,
|
|
44124
44851
|
walletAddress: address,
|
|
@@ -44128,18 +44855,13 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44128
44855
|
return { success: false, code: "NO_ROUTES", message: "No swap routes found" };
|
|
44129
44856
|
}
|
|
44130
44857
|
const route = quote.routes[0];
|
|
44131
|
-
const chainConfig = getChainById(
|
|
44858
|
+
const chainConfig = getChainById(fromChain);
|
|
44132
44859
|
const transactions = await this.swapClient.getSwapTransactions(route);
|
|
44133
44860
|
let txHash = "";
|
|
44134
44861
|
for (const txData of transactions) {
|
|
44135
44862
|
txHash = await this.sendSwapTx(txData, chainConfig, signer);
|
|
44136
44863
|
const isApproval = txData.transaction.type === "approval" || txData.transaction.type === "revokeApproval";
|
|
44137
|
-
await this.pollConfirmation(
|
|
44138
|
-
input2.fromChain,
|
|
44139
|
-
address,
|
|
44140
|
-
txHash,
|
|
44141
|
-
isApproval ? 6e4 : 12e4
|
|
44142
|
-
);
|
|
44864
|
+
await this.pollConfirmation(fromChain, address, txHash, isApproval ? 6e4 : 12e4);
|
|
44143
44865
|
}
|
|
44144
44866
|
const firstStep = route.steps[0];
|
|
44145
44867
|
const lastStep = route.steps[route.steps.length - 1];
|
|
@@ -44148,7 +44870,7 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44148
44870
|
firstStep.from.asset.metadata.decimals
|
|
44149
44871
|
);
|
|
44150
44872
|
const outputAmt = formatAmount(lastStep.to.amount, lastStep.to.asset.metadata.decimals);
|
|
44151
|
-
const explorerUrl = getExplorerTxUrl(
|
|
44873
|
+
const explorerUrl = getExplorerTxUrl(fromChain, txHash);
|
|
44152
44874
|
return {
|
|
44153
44875
|
success: true,
|
|
44154
44876
|
txHash,
|
|
@@ -44164,9 +44886,9 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44164
44886
|
name: "get_swap_quote",
|
|
44165
44887
|
description: "Get a swap quote without executing",
|
|
44166
44888
|
schema: import_zod5.z.object({
|
|
44167
|
-
fromChain: import_zod5.z.string().describe(
|
|
44889
|
+
fromChain: import_zod5.z.string().describe(`Source chain. ${CHAIN_DESC}`),
|
|
44168
44890
|
fromToken: import_zod5.z.string().describe("Source token symbol or address"),
|
|
44169
|
-
toChain: import_zod5.z.string().describe(
|
|
44891
|
+
toChain: import_zod5.z.string().describe(`Destination chain. ${CHAIN_DESC}`),
|
|
44170
44892
|
toToken: import_zod5.z.string().describe("Destination token symbol or address"),
|
|
44171
44893
|
amount: import_zod5.z.string().describe("Human-readable amount")
|
|
44172
44894
|
}),
|
|
@@ -44174,30 +44896,32 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44174
44896
|
if (!this.swapClient) {
|
|
44175
44897
|
throw new SwapError("WALLET_NOT_FOUND", "Swap client required");
|
|
44176
44898
|
}
|
|
44177
|
-
const
|
|
44899
|
+
const fromChain = resolveChain(input2.fromChain);
|
|
44900
|
+
const toChain = resolveChain(input2.toChain);
|
|
44901
|
+
const fromTokenInfo = resolveToken(input2.fromToken, fromChain);
|
|
44178
44902
|
if (!fromTokenInfo) {
|
|
44179
44903
|
return {
|
|
44180
44904
|
success: false,
|
|
44181
44905
|
code: "TOKEN_NOT_FOUND",
|
|
44182
|
-
message: `Unknown token: ${input2.fromToken} on ${
|
|
44906
|
+
message: `Unknown token: ${input2.fromToken} on ${fromChain}`
|
|
44183
44907
|
};
|
|
44184
44908
|
}
|
|
44185
|
-
const toTokenInfo = resolveToken(input2.toToken,
|
|
44909
|
+
const toTokenInfo = resolveToken(input2.toToken, toChain);
|
|
44186
44910
|
if (!toTokenInfo) {
|
|
44187
44911
|
return {
|
|
44188
44912
|
success: false,
|
|
44189
44913
|
code: "TOKEN_NOT_FOUND",
|
|
44190
|
-
message: `Unknown token: ${input2.toToken} on ${
|
|
44914
|
+
message: `Unknown token: ${input2.toToken} on ${toChain}`
|
|
44191
44915
|
};
|
|
44192
44916
|
}
|
|
44193
44917
|
const parsedAmount = parseAmount(input2.amount, fromTokenInfo.decimals);
|
|
44194
44918
|
const quote = await this.swapClient.getQuote({
|
|
44195
|
-
fromChain
|
|
44919
|
+
fromChain,
|
|
44196
44920
|
fromToken: fromTokenInfo.address,
|
|
44197
|
-
toChain
|
|
44921
|
+
toChain,
|
|
44198
44922
|
toToken: toTokenInfo.address,
|
|
44199
44923
|
amount: parsedAmount,
|
|
44200
|
-
walletAddress: getSampleAccount(
|
|
44924
|
+
walletAddress: getSampleAccount(fromChain)
|
|
44201
44925
|
});
|
|
44202
44926
|
if (quote.routes.length === 0) {
|
|
44203
44927
|
return { success: false, code: "NO_ROUTES", message: "No routes found" };
|
|
@@ -44222,22 +44946,23 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44222
44946
|
name: "get_token_price",
|
|
44223
44947
|
description: "Get current USD price for a token",
|
|
44224
44948
|
schema: import_zod5.z.object({
|
|
44225
|
-
chain: import_zod5.z.string().describe(
|
|
44949
|
+
chain: import_zod5.z.string().describe(CHAIN_DESC),
|
|
44226
44950
|
token: import_zod5.z.string().describe("Token symbol or address")
|
|
44227
44951
|
}),
|
|
44228
44952
|
invoke: async (_ctx, input2) => {
|
|
44229
44953
|
if (!this.swapClient) {
|
|
44230
44954
|
throw new SwapError("WALLET_NOT_FOUND", "Swap client required");
|
|
44231
44955
|
}
|
|
44232
|
-
const
|
|
44956
|
+
const chain2 = resolveChain(input2.chain);
|
|
44957
|
+
const tokenInfo = resolveToken(input2.token, chain2);
|
|
44233
44958
|
if (!tokenInfo) {
|
|
44234
44959
|
return {
|
|
44235
44960
|
success: false,
|
|
44236
44961
|
code: "TOKEN_NOT_FOUND",
|
|
44237
|
-
message: `Unknown token: ${input2.token} on ${
|
|
44962
|
+
message: `Unknown token: ${input2.token} on ${chain2}`
|
|
44238
44963
|
};
|
|
44239
44964
|
}
|
|
44240
|
-
const price = await this.swapClient.getTokenPrice(
|
|
44965
|
+
const price = await this.swapClient.getTokenPrice(chain2, tokenInfo.address);
|
|
44241
44966
|
if (price === null) {
|
|
44242
44967
|
return {
|
|
44243
44968
|
success: false,
|
|
@@ -44248,7 +44973,7 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44248
44973
|
return {
|
|
44249
44974
|
success: true,
|
|
44250
44975
|
token: input2.token.toUpperCase(),
|
|
44251
|
-
chain:
|
|
44976
|
+
chain: chain2,
|
|
44252
44977
|
priceUsd: price
|
|
44253
44978
|
};
|
|
44254
44979
|
}
|
|
@@ -44271,26 +44996,37 @@ var SecurityActionProvider = class extends ActionProvider {
|
|
|
44271
44996
|
validateTransaction() {
|
|
44272
44997
|
return {
|
|
44273
44998
|
name: "validate_transaction",
|
|
44274
|
-
description: "Validate an address or transaction for security",
|
|
44999
|
+
description: "Validate an address or transaction for security. Provide `chain` (and optional `tokenAddress`) to scope the validation to a specific asset; omit both for a generic address check.",
|
|
44275
45000
|
schema: import_zod6.z.object({
|
|
44276
45001
|
address: import_zod6.z.string().describe("Address to validate"),
|
|
44277
|
-
|
|
45002
|
+
chain: import_zod6.z.string().optional().describe(CHAIN_DESC),
|
|
45003
|
+
tokenAddress: import_zod6.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC),
|
|
44278
45004
|
type: import_zod6.z.string().optional().describe("Validation type")
|
|
45005
|
+
}).refine((data) => data.tokenAddress === void 0 || data.chain !== void 0, {
|
|
45006
|
+
path: ["tokenAddress"],
|
|
45007
|
+
message: "tokenAddress requires chain to be provided."
|
|
44279
45008
|
}),
|
|
44280
45009
|
invoke: async (ctx, input2) => {
|
|
44281
|
-
|
|
45010
|
+
const assetId = input2.chain !== void 0 ? resolveAsset(input2.chain, input2.tokenAddress).assetId : void 0;
|
|
45011
|
+
return this.getService(ctx).validate({
|
|
45012
|
+
address: input2.address,
|
|
45013
|
+
assetId,
|
|
45014
|
+
type: input2.type
|
|
45015
|
+
});
|
|
44282
45016
|
}
|
|
44283
45017
|
};
|
|
44284
45018
|
}
|
|
44285
45019
|
getAssetInfo() {
|
|
44286
45020
|
return {
|
|
44287
45021
|
name: "get_asset_info",
|
|
44288
|
-
description: "Get detailed information about an asset",
|
|
45022
|
+
description: "Get detailed information about an asset (name, symbol, price, market cap). Omit `tokenAddress` for the chain native coin.",
|
|
44289
45023
|
schema: import_zod6.z.object({
|
|
44290
|
-
|
|
45024
|
+
chain: import_zod6.z.string().describe(CHAIN_DESC),
|
|
45025
|
+
tokenAddress: import_zod6.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC)
|
|
44291
45026
|
}),
|
|
44292
45027
|
invoke: async (ctx, input2) => {
|
|
44293
|
-
|
|
45028
|
+
const { assetId } = resolveAsset(input2.chain, input2.tokenAddress);
|
|
45029
|
+
return this.getService(ctx).getAssetInfo(assetId);
|
|
44294
45030
|
}
|
|
44295
45031
|
};
|
|
44296
45032
|
}
|
|
@@ -44314,13 +45050,14 @@ var HistoryActionProvider = class extends ActionProvider {
|
|
|
44314
45050
|
description: "Get transaction history for an address",
|
|
44315
45051
|
schema: import_zod7.z.object({
|
|
44316
45052
|
address: import_zod7.z.string().describe("Wallet address"),
|
|
44317
|
-
chain: import_zod7.z.string().optional().describe(
|
|
45053
|
+
chain: import_zod7.z.string().optional().describe(CHAIN_DESC),
|
|
44318
45054
|
limit: import_zod7.z.coerce.number().optional().default(20).describe("Max results"),
|
|
44319
45055
|
from: import_zod7.z.string().optional().describe("Start date (ISO)"),
|
|
44320
45056
|
to: import_zod7.z.string().optional().describe("End date (ISO)")
|
|
44321
45057
|
}),
|
|
44322
45058
|
invoke: async (ctx, input2) => {
|
|
44323
|
-
|
|
45059
|
+
const chain2 = input2.chain !== void 0 ? resolveChain(input2.chain) : void 0;
|
|
45060
|
+
return this.getService(ctx).getHistory({ ...input2, chain: chain2 });
|
|
44324
45061
|
}
|
|
44325
45062
|
};
|
|
44326
45063
|
}
|
|
@@ -44330,11 +45067,12 @@ var HistoryActionProvider = class extends ActionProvider {
|
|
|
44330
45067
|
description: "Get details of a specific transaction by hash",
|
|
44331
45068
|
schema: import_zod7.z.object({
|
|
44332
45069
|
hash: import_zod7.z.string().describe("Transaction hash"),
|
|
44333
|
-
chain: import_zod7.z.string().describe(
|
|
45070
|
+
chain: import_zod7.z.string().describe(CHAIN_DESC)
|
|
44334
45071
|
}),
|
|
44335
45072
|
invoke: async (ctx, input2) => {
|
|
44336
|
-
const
|
|
44337
|
-
const
|
|
45073
|
+
const chain2 = resolveChain(input2.chain);
|
|
45074
|
+
const details = await this.getService(ctx).getByHash(input2.hash, chain2);
|
|
45075
|
+
const explorer = getExplorerTxUrl(chain2, input2.hash);
|
|
44338
45076
|
return { ...details, explorer: explorer || void 0 };
|
|
44339
45077
|
}
|
|
44340
45078
|
};
|
|
@@ -44345,11 +45083,12 @@ var HistoryActionProvider = class extends ActionProvider {
|
|
|
44345
45083
|
description: "Get swap transaction history",
|
|
44346
45084
|
schema: import_zod7.z.object({
|
|
44347
45085
|
address: import_zod7.z.string().describe("Wallet address"),
|
|
44348
|
-
chain: import_zod7.z.string().optional().describe(
|
|
45086
|
+
chain: import_zod7.z.string().optional().describe(CHAIN_DESC),
|
|
44349
45087
|
limit: import_zod7.z.coerce.number().optional().default(20).describe("Max results")
|
|
44350
45088
|
}),
|
|
44351
45089
|
invoke: async (ctx, input2) => {
|
|
44352
|
-
|
|
45090
|
+
const chain2 = input2.chain !== void 0 ? resolveChain(input2.chain) : void 0;
|
|
45091
|
+
return this.getService(ctx).getSwapHistory({ ...input2, chain: chain2 });
|
|
44353
45092
|
}
|
|
44354
45093
|
};
|
|
44355
45094
|
}
|
|
@@ -44365,38 +45104,30 @@ var AlertActionProvider = class extends ActionProvider {
|
|
|
44365
45104
|
this.swapClient = swapClient;
|
|
44366
45105
|
}
|
|
44367
45106
|
getActions() {
|
|
44368
|
-
return [
|
|
44369
|
-
this.createAlert(),
|
|
44370
|
-
this.listAlerts(),
|
|
44371
|
-
this.checkAlerts(),
|
|
44372
|
-
this.deleteAlert()
|
|
44373
|
-
];
|
|
45107
|
+
return [this.createAlert(), this.listAlerts(), this.checkAlerts(), this.deleteAlert()];
|
|
44374
45108
|
}
|
|
44375
45109
|
createAlert() {
|
|
44376
45110
|
return {
|
|
44377
45111
|
name: "create_alert",
|
|
44378
|
-
description: "Create a price alert
|
|
45112
|
+
description: "Create a price alert. Triggers when the asset price crosses a target. Omit `tokenAddress` to watch the chain native coin (ETH, BNB, SOL, etc.).",
|
|
44379
45113
|
schema: import_zod8.z.object({
|
|
44380
|
-
|
|
44381
|
-
|
|
45114
|
+
chain: import_zod8.z.string().describe(CHAIN_DESC),
|
|
45115
|
+
tokenAddress: import_zod8.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC),
|
|
44382
45116
|
condition: import_zod8.z.enum(["above", "below"]).describe("Alert when price goes above or below target"),
|
|
44383
45117
|
targetPrice: import_zod8.z.coerce.number().positive().describe("Target price in USD")
|
|
44384
45118
|
}),
|
|
44385
45119
|
invoke: async (_ctx, input2) => {
|
|
44386
|
-
|
|
44387
|
-
parseTokenId(input2.tokenId);
|
|
44388
|
-
} catch {
|
|
44389
|
-
return { error: `Invalid tokenId format: ${input2.tokenId}. Expected format: "c60" or "c60_t0xABC..."` };
|
|
44390
|
-
}
|
|
45120
|
+
const { chainKey, assetId: tokenId } = resolveAsset(input2.chain, input2.tokenAddress);
|
|
44391
45121
|
const alert = await this.alertStore.create({
|
|
44392
|
-
tokenId
|
|
44393
|
-
chainKey
|
|
45122
|
+
tokenId,
|
|
45123
|
+
chainKey,
|
|
44394
45124
|
condition: input2.condition,
|
|
44395
45125
|
targetPrice: input2.targetPrice
|
|
44396
45126
|
});
|
|
45127
|
+
const label = input2.tokenAddress ? `${input2.tokenAddress} on ${chainKey}` : `${requireChain(chainKey).symbol} on ${chainKey}`;
|
|
44397
45128
|
return {
|
|
44398
45129
|
id: alert.id,
|
|
44399
|
-
message: `Alert created: notify when ${
|
|
45130
|
+
message: `Alert created: notify when ${label} goes ${input2.condition} $${input2.targetPrice}`
|
|
44400
45131
|
};
|
|
44401
45132
|
}
|
|
44402
45133
|
};
|
|
@@ -44425,10 +45156,7 @@ var AlertActionProvider = class extends ActionProvider {
|
|
|
44425
45156
|
for (const alert of activeAlerts) {
|
|
44426
45157
|
if (!this.swapClient) continue;
|
|
44427
45158
|
const { contractAddress } = parseTokenId(alert.tokenId);
|
|
44428
|
-
const price = await this.swapClient.getTokenPrice(
|
|
44429
|
-
alert.chainKey,
|
|
44430
|
-
contractAddress || ""
|
|
44431
|
-
);
|
|
45159
|
+
const price = await this.swapClient.getTokenPrice(alert.chainKey, contractAddress || "");
|
|
44432
45160
|
if (price === null) continue;
|
|
44433
45161
|
const shouldTrigger = alert.condition === "above" && price >= alert.targetPrice || alert.condition === "below" && price <= alert.targetPrice;
|
|
44434
45162
|
if (shouldTrigger) {
|
|
@@ -44483,12 +45211,13 @@ var AgentWalletActionProvider = class extends ActionProvider {
|
|
|
44483
45211
|
name: "get_address",
|
|
44484
45212
|
description: "Get the active wallet's address for a specific chain.",
|
|
44485
45213
|
schema: import_zod9.z.object({
|
|
44486
|
-
|
|
45214
|
+
chain: import_zod9.z.string().describe(CHAIN_DESC)
|
|
44487
45215
|
}),
|
|
44488
45216
|
invoke: async (_ctx, input2) => {
|
|
45217
|
+
const chainKey = resolveChain(input2.chain);
|
|
44489
45218
|
const wallet = this.requireWallet();
|
|
44490
|
-
const address = await wallet.getAddress(
|
|
44491
|
-
return {
|
|
45219
|
+
const address = await wallet.getAddress(chainKey);
|
|
45220
|
+
return { chain: chainKey, address };
|
|
44492
45221
|
}
|
|
44493
45222
|
};
|
|
44494
45223
|
}
|
|
@@ -44509,21 +45238,17 @@ var AgentWalletActionProvider = class extends ActionProvider {
|
|
|
44509
45238
|
name: "wallet_balance",
|
|
44510
45239
|
description: "Read the native-coin balance (BNB on BSC, ETH on Ethereum / Base / Arbitrum, SOL on Solana, etc.) for the active wallet on a specific chain. For ERC-20 / SPL token balances, use `get_balance`.",
|
|
44511
45240
|
schema: import_zod9.z.object({
|
|
44512
|
-
|
|
45241
|
+
chain: import_zod9.z.string().describe(CHAIN_DESC)
|
|
44513
45242
|
}),
|
|
44514
45243
|
invoke: async (_ctx, input2) => {
|
|
45244
|
+
const { chainKey, assetId } = resolveAsset(input2.chain);
|
|
44515
45245
|
const wallet = this.requireWallet();
|
|
44516
|
-
const address = await wallet.getAddress(
|
|
45246
|
+
const address = await wallet.getAddress(chainKey);
|
|
44517
45247
|
if (!this.blockchainClient) {
|
|
44518
|
-
return {
|
|
45248
|
+
return { chain: chainKey, address, balance: "BlockchainClient not available" };
|
|
44519
45249
|
}
|
|
44520
|
-
const coinType = chainKeyToCoinType(input2.chainKey);
|
|
44521
|
-
if (coinType === void 0) {
|
|
44522
|
-
throw new WalletError("CHAIN_UNSUPPORTED", `Unsupported chain: ${input2.chainKey}`);
|
|
44523
|
-
}
|
|
44524
|
-
const assetId = formatTokenId(coinType);
|
|
44525
45250
|
const balance = await this.blockchainClient.getBalance(assetId, address);
|
|
44526
|
-
return {
|
|
45251
|
+
return { chain: chainKey, address, balance };
|
|
44527
45252
|
}
|
|
44528
45253
|
};
|
|
44529
45254
|
}
|
|
@@ -44548,17 +45273,19 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44548
45273
|
tokenBalance() {
|
|
44549
45274
|
return {
|
|
44550
45275
|
name: "token_balance",
|
|
44551
|
-
description: "Get balance for a specific token or native asset
|
|
45276
|
+
description: "Get balance for a specific token or native asset on a chain.",
|
|
44552
45277
|
schema: import_zod10.z.object({
|
|
44553
|
-
|
|
45278
|
+
chain: import_zod10.z.string().describe(CHAIN_DESC),
|
|
45279
|
+
tokenAddress: import_zod10.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC),
|
|
44554
45280
|
address: import_zod10.z.string().describe("Wallet address to check balance for")
|
|
44555
45281
|
}),
|
|
44556
45282
|
invoke: async (_ctx, input2) => {
|
|
44557
45283
|
if (!this.blockchainClient) {
|
|
44558
45284
|
throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
|
|
44559
45285
|
}
|
|
44560
|
-
const
|
|
44561
|
-
|
|
45286
|
+
const { chainKey, assetId } = resolveAsset(input2.chain, input2.tokenAddress);
|
|
45287
|
+
const balance = await this.blockchainClient.getBalance(assetId, input2.address);
|
|
45288
|
+
return { chain: chainKey, address: input2.address, balance };
|
|
44562
45289
|
}
|
|
44563
45290
|
};
|
|
44564
45291
|
}
|
|
@@ -44567,54 +45294,60 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44567
45294
|
name: "transfer_token",
|
|
44568
45295
|
description: "Transfer a token or native asset to a destination address. Works in both local and WalletConnect modes for EVM chains; non-EVM chains (e.g. Solana) require local wallet mode.",
|
|
44569
45296
|
schema: import_zod10.z.object({
|
|
44570
|
-
|
|
45297
|
+
chain: import_zod10.z.string().describe(CHAIN_DESC),
|
|
45298
|
+
tokenAddress: import_zod10.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC),
|
|
44571
45299
|
to: import_zod10.z.string().describe("Destination address"),
|
|
44572
|
-
amount: import_zod10.z.string().describe(
|
|
45300
|
+
amount: import_zod10.z.string().describe('Amount in human-readable format (e.g., "1.5", "0.01")'),
|
|
45301
|
+
decimals: import_zod10.z.coerce.number().int().min(0).max(36).optional().describe(
|
|
45302
|
+
"Token decimals (non-negative integer). Omit for native coins and for tokens twak recognizes \u2014 decimals are derived from `COMMON_TOKENS` per chain (BSC stables are 18, USDC on Ethereum is 6, etc.). REQUIRED when tokenAddress is a contract twak does not recognize; look up the value on the token contract."
|
|
45303
|
+
),
|
|
44573
45304
|
memo: import_zod10.z.string().optional().describe("Optional memo/note")
|
|
44574
45305
|
}),
|
|
44575
45306
|
invoke: async (_ctx, input2) => {
|
|
44576
45307
|
const wallet = this.walletContext.activeWallet();
|
|
45308
|
+
const { chainKey, assetId } = resolveAsset(input2.chain, input2.tokenAddress);
|
|
45309
|
+
const decimals = input2.decimals ?? lookupKnownTokenDecimals(chainKey, input2.tokenAddress);
|
|
45310
|
+
if (decimals === void 0) {
|
|
45311
|
+
throw new WalletError(
|
|
45312
|
+
"VALIDATION_ERROR",
|
|
45313
|
+
`Cannot derive decimals for ${input2.tokenAddress} on ${chainKey}. Provide \`decimals\` explicitly.`
|
|
45314
|
+
);
|
|
45315
|
+
}
|
|
44577
45316
|
if (wallet instanceof AgentWalletProvider) {
|
|
44578
45317
|
if (!this.blockchainClient) {
|
|
44579
45318
|
throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
|
|
44580
45319
|
}
|
|
44581
45320
|
const hdWallet = wallet.getSigner().getHDWallet();
|
|
44582
45321
|
const result = await this.blockchainClient.transfer({
|
|
44583
|
-
assetId
|
|
45322
|
+
assetId,
|
|
44584
45323
|
to: input2.to,
|
|
44585
45324
|
amount: input2.amount,
|
|
45325
|
+
decimals,
|
|
44586
45326
|
memo: input2.memo,
|
|
44587
45327
|
hdWallet
|
|
44588
45328
|
});
|
|
44589
|
-
return { hash: result.
|
|
44590
|
-
}
|
|
44591
|
-
const { coinId, contractAddress } = parseTokenId(input2.assetId);
|
|
44592
|
-
const chainKey = coinTypeToChainKey(coinId);
|
|
44593
|
-
if (!chainKey) {
|
|
44594
|
-
throw new WalletError("CHAIN_UNSUPPORTED", `Unknown coin type: ${coinId}`);
|
|
45329
|
+
return { hash: result.txHash, chain: result.chainKey };
|
|
44595
45330
|
}
|
|
44596
|
-
const chain2 =
|
|
44597
|
-
if (
|
|
45331
|
+
const chain2 = requireChain(chainKey);
|
|
45332
|
+
if (chain2.namespace !== "eip155") {
|
|
44598
45333
|
throw new WalletError(
|
|
44599
45334
|
"CHAIN_UNSUPPORTED",
|
|
44600
45335
|
`transfer_token on ${chainKey} is not supported in WalletConnect mode (EVM-only). Switch to local wallet mode for ${chainKey} transfers.`
|
|
44601
45336
|
);
|
|
44602
45337
|
}
|
|
45338
|
+
if (input2.tokenAddress) assertEvmAddress(input2.tokenAddress, "tokenAddress");
|
|
44603
45339
|
const caipChainId = `${chain2.namespace}:${chain2.chainId}`;
|
|
45340
|
+
const atomicAmount = parseAmount(input2.amount, decimals);
|
|
44604
45341
|
let txHash;
|
|
44605
|
-
if (
|
|
44606
|
-
|
|
44607
|
-
|
|
44608
|
-
|
|
44609
|
-
const
|
|
44610
|
-
txHash =
|
|
44611
|
-
chainId: caipChainId,
|
|
44612
|
-
to: contractAddress,
|
|
44613
|
-
data,
|
|
44614
|
-
value: "0x0"
|
|
44615
|
-
});
|
|
45342
|
+
if (input2.tokenAddress) {
|
|
45343
|
+
if (!this.blockchainClient) {
|
|
45344
|
+
throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
|
|
45345
|
+
}
|
|
45346
|
+
const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.transfer([input2.to, BigInt(atomicAmount)], wallet);
|
|
45347
|
+
txHash = pending.txHash;
|
|
44616
45348
|
} else {
|
|
44617
|
-
|
|
45349
|
+
assertEvmAddress(input2.to, "to");
|
|
45350
|
+
const valueHex = "0x" + BigInt(atomicAmount).toString(16);
|
|
44618
45351
|
txHash = await wallet.sendTransaction({
|
|
44619
45352
|
chainId: caipChainId,
|
|
44620
45353
|
to: input2.to,
|
|
@@ -44622,80 +45355,77 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44622
45355
|
value: valueHex
|
|
44623
45356
|
});
|
|
44624
45357
|
}
|
|
44625
|
-
return { hash: txHash, chainKey };
|
|
45358
|
+
return { hash: txHash, chain: chainKey };
|
|
44626
45359
|
}
|
|
44627
45360
|
};
|
|
44628
45361
|
}
|
|
44629
45362
|
approveToken() {
|
|
44630
45363
|
return {
|
|
44631
45364
|
name: "approve_token",
|
|
44632
|
-
description: `Approve a spender to use tokens on your behalf
|
|
45365
|
+
description: `Approve a spender to use ERC-20 tokens on your behalf. SAFETY: when amount is "unlimited" (or the MaxUint256 decimal string), you MUST surface the risk to the user and get their explicit consent before calling this tool \u2014 unlimited approvals grant the spender access to ALL of the user's tokens of this type, including future transfers, and have been used in many drainer attacks. Prefer a finite amount equal to the immediate need. Use revoke_token_approval (or amount: "0") to revoke an existing approval.`,
|
|
44633
45366
|
schema: import_zod10.z.object({
|
|
44634
|
-
|
|
45367
|
+
chain: import_zod10.z.string().describe(CHAIN_DESC),
|
|
45368
|
+
tokenAddress: import_zod10.z.string().trim().min(1).describe("ERC-20 token contract address."),
|
|
44635
45369
|
spender: import_zod10.z.string().describe("Spender address to approve"),
|
|
44636
45370
|
amount: import_zod10.z.string().describe('Amount to approve (in smallest unit, or "unlimited")')
|
|
44637
45371
|
}),
|
|
44638
45372
|
invoke: async (_ctx, input2) => {
|
|
44639
45373
|
const wallet = this.walletContext.activeWallet();
|
|
44640
|
-
const
|
|
44641
|
-
|
|
44642
|
-
|
|
44643
|
-
}
|
|
44644
|
-
const chainKey = coinTypeToChainKey(coinId);
|
|
44645
|
-
if (!chainKey) {
|
|
44646
|
-
throw new WalletError("CHAIN_UNSUPPORTED", `Unsupported coin type: ${coinId}`);
|
|
44647
|
-
}
|
|
44648
|
-
const chain2 = getChainById(chainKey);
|
|
44649
|
-
if (!chain2 || chain2.namespace !== "eip155") {
|
|
45374
|
+
const chainKey = resolveChain(input2.chain);
|
|
45375
|
+
const chain2 = requireChain(chainKey);
|
|
45376
|
+
if (chain2.namespace !== "eip155") {
|
|
44650
45377
|
throw new WalletError(
|
|
44651
45378
|
"CHAIN_UNSUPPORTED",
|
|
44652
45379
|
`approve_token on ${chainKey} is not supported \u2014 ERC-20 approvals are EVM-only.`
|
|
44653
45380
|
);
|
|
44654
45381
|
}
|
|
45382
|
+
assertEvmAddress(input2.tokenAddress, "tokenAddress");
|
|
45383
|
+
assertEvmAddress(input2.spender, "spender");
|
|
45384
|
+
if (!this.blockchainClient) {
|
|
45385
|
+
throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
|
|
45386
|
+
}
|
|
44655
45387
|
const amount = input2.amount === "unlimited" ? "115792089237316195423570985008687907853269984665640564039457584007913129639935" : input2.amount;
|
|
44656
|
-
const
|
|
44657
|
-
return { hash:
|
|
45388
|
+
const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.approve([input2.spender, BigInt(amount)], wallet);
|
|
45389
|
+
return { hash: pending.txHash, chain: chainKey };
|
|
44658
45390
|
}
|
|
44659
45391
|
};
|
|
44660
45392
|
}
|
|
44661
45393
|
revokeTokenApproval() {
|
|
44662
45394
|
return {
|
|
44663
45395
|
name: "revoke_token_approval",
|
|
44664
|
-
description:
|
|
45396
|
+
description: "Revoke an ERC-20 token approval by setting the spender allowance to 0. Use this to remove a previously-granted allowance \u2014 especially important after unlimited approvals or any approval you no longer need. This is the inverse of approve_token.",
|
|
44665
45397
|
schema: import_zod10.z.object({
|
|
44666
|
-
|
|
44667
|
-
|
|
44668
|
-
),
|
|
45398
|
+
chain: import_zod10.z.string().describe(CHAIN_DESC),
|
|
45399
|
+
tokenAddress: import_zod10.z.string().trim().min(1).describe("ERC-20 token contract address whose allowance you want to revoke."),
|
|
44669
45400
|
spender: import_zod10.z.string().describe("Spender address whose allowance should be set to 0")
|
|
44670
45401
|
}),
|
|
44671
45402
|
invoke: async (_ctx, input2) => {
|
|
44672
45403
|
const wallet = this.walletContext.activeWallet();
|
|
44673
|
-
const
|
|
44674
|
-
|
|
44675
|
-
|
|
44676
|
-
}
|
|
44677
|
-
const chainKey = coinTypeToChainKey(coinId);
|
|
44678
|
-
if (!chainKey) {
|
|
44679
|
-
throw new WalletError("CHAIN_UNSUPPORTED", `Unsupported coin type: ${coinId}`);
|
|
44680
|
-
}
|
|
44681
|
-
const chain2 = getChainById(chainKey);
|
|
44682
|
-
if (!chain2 || chain2.namespace !== "eip155") {
|
|
45404
|
+
const chainKey = resolveChain(input2.chain);
|
|
45405
|
+
const chain2 = requireChain(chainKey);
|
|
45406
|
+
if (chain2.namespace !== "eip155") {
|
|
44683
45407
|
throw new WalletError(
|
|
44684
45408
|
"CHAIN_UNSUPPORTED",
|
|
44685
45409
|
`revoke_token_approval on ${chainKey} is not supported \u2014 ERC-20 approvals are EVM-only.`
|
|
44686
45410
|
);
|
|
44687
45411
|
}
|
|
44688
|
-
|
|
44689
|
-
|
|
45412
|
+
assertEvmAddress(input2.tokenAddress, "tokenAddress");
|
|
45413
|
+
assertEvmAddress(input2.spender, "spender");
|
|
45414
|
+
if (!this.blockchainClient) {
|
|
45415
|
+
throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
|
|
45416
|
+
}
|
|
45417
|
+
const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.approve([input2.spender, 0n], wallet);
|
|
45418
|
+
return { hash: pending.txHash, chain: chainKey };
|
|
44690
45419
|
}
|
|
44691
45420
|
};
|
|
44692
45421
|
}
|
|
44693
45422
|
checkAllowance() {
|
|
44694
45423
|
return {
|
|
44695
45424
|
name: "check_allowance",
|
|
44696
|
-
description: "Check the
|
|
45425
|
+
description: "Check the ERC-20 allowance for a spender address.",
|
|
44697
45426
|
schema: import_zod10.z.object({
|
|
44698
|
-
|
|
45427
|
+
chain: import_zod10.z.string().describe(CHAIN_DESC),
|
|
45428
|
+
tokenAddress: import_zod10.z.string().trim().min(1).describe("ERC-20 token contract address."),
|
|
44699
45429
|
owner: import_zod10.z.string().describe("Token owner address"),
|
|
44700
45430
|
spender: import_zod10.z.string().describe("Spender address to check")
|
|
44701
45431
|
}),
|
|
@@ -44703,13 +45433,11 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44703
45433
|
if (!this.blockchainClient) {
|
|
44704
45434
|
throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
|
|
44705
45435
|
}
|
|
44706
|
-
const
|
|
44707
|
-
|
|
44708
|
-
owner: input2.owner,
|
|
44709
|
-
spender: input2.spender
|
|
44710
|
-
});
|
|
45436
|
+
const chainKey = resolveChain(input2.chain);
|
|
45437
|
+
const allowance = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).read.allowance([input2.owner, input2.spender]);
|
|
44711
45438
|
return {
|
|
44712
|
-
|
|
45439
|
+
chain: chainKey,
|
|
45440
|
+
tokenAddress: input2.tokenAddress,
|
|
44713
45441
|
owner: input2.owner,
|
|
44714
45442
|
spender: input2.spender,
|
|
44715
45443
|
allowance: allowance.toString()
|
|
@@ -44747,17 +45475,19 @@ var MarketActionProvider = class extends ActionProvider {
|
|
|
44747
45475
|
checkTokenRisk() {
|
|
44748
45476
|
return {
|
|
44749
45477
|
name: "check_token_risk",
|
|
44750
|
-
description: "Check security and risk information for a token (honeypot detection, audit status, freeze authority)",
|
|
45478
|
+
description: "Check security and risk information for a token (honeypot detection, audit status, freeze authority). Omit `tokenAddress` to check the chain native coin.",
|
|
44751
45479
|
schema: import_zod11.z.object({
|
|
44752
|
-
|
|
45480
|
+
chain: import_zod11.z.string().describe(CHAIN_DESC),
|
|
45481
|
+
tokenAddress: import_zod11.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC)
|
|
44753
45482
|
}),
|
|
44754
45483
|
invoke: async (ctx, input2) => {
|
|
44755
|
-
const
|
|
45484
|
+
const { assetId } = resolveAsset(input2.chain, input2.tokenAddress);
|
|
45485
|
+
const status = await this.getService(ctx).checkCoinStatus(assetId);
|
|
44756
45486
|
const riskLevel = status.securityInfo?.riskLevel || status.solanaSecurityInfo?.riskLevel || "unknown";
|
|
44757
45487
|
const isHoneypot = status.securityInfo?.isHoneypot ?? false;
|
|
44758
45488
|
return {
|
|
44759
45489
|
success: true,
|
|
44760
|
-
assetId
|
|
45490
|
+
assetId,
|
|
44761
45491
|
name: status.name,
|
|
44762
45492
|
symbol: status.symbol,
|
|
44763
45493
|
isActive: status.isActive,
|
|
@@ -44773,32 +45503,58 @@ var MarketActionProvider = class extends ActionProvider {
|
|
|
44773
45503
|
};
|
|
44774
45504
|
}
|
|
44775
45505
|
};
|
|
44776
|
-
var ERC20_TRANSFER_SIG2 = "0xa9059cbb";
|
|
44777
|
-
var EVM_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
|
|
44778
45506
|
var DEFAULT_MAX_OFFRAMP_USD = 1e4;
|
|
44779
|
-
function
|
|
44780
|
-
|
|
44781
|
-
|
|
44782
|
-
|
|
45507
|
+
function validateOfframpInput(input2, chainKey, nativeDecimals) {
|
|
45508
|
+
if (!isEvmAddress(input2.depositAddress)) {
|
|
45509
|
+
throw new TransferError(
|
|
45510
|
+
"VALIDATION_ERROR",
|
|
45511
|
+
`depositAddress is not a valid EVM address: ${input2.depositAddress}. Double-check what the provider displayed.`
|
|
45512
|
+
);
|
|
45513
|
+
}
|
|
45514
|
+
if (input2.tokenAddress && !isEvmAddress(input2.tokenAddress)) {
|
|
45515
|
+
throw new TransferError("VALIDATION_ERROR", `Malformed tokenAddress: ${input2.tokenAddress}`);
|
|
45516
|
+
}
|
|
45517
|
+
let decimals;
|
|
45518
|
+
if (input2.tokenAddress) {
|
|
45519
|
+
if (input2.decimals === void 0) {
|
|
45520
|
+
throw new TransferError(
|
|
45521
|
+
"VALIDATION_ERROR",
|
|
45522
|
+
`decimals is required for ERC-20 token ${input2.tokenAddress} on ${chainKey}. Pass the token's decimals explicitly (6 for USDC/USDT, 18 for DAI, etc.) \u2014 silently defaulting to 18 could over-pay the provider by orders of magnitude.`
|
|
45523
|
+
);
|
|
45524
|
+
}
|
|
45525
|
+
decimals = input2.decimals;
|
|
45526
|
+
} else {
|
|
45527
|
+
decimals = nativeDecimals;
|
|
45528
|
+
}
|
|
45529
|
+
try {
|
|
45530
|
+
assertStrictPositiveAmount(input2.cryptoAmount, decimals);
|
|
45531
|
+
} catch (err) {
|
|
45532
|
+
throw new TransferError("VALIDATION_ERROR", err instanceof Error ? err.message : String(err));
|
|
45533
|
+
}
|
|
45534
|
+
return decimals;
|
|
44783
45535
|
}
|
|
44784
|
-
function
|
|
44785
|
-
if (!cryptoAsset) return void 0;
|
|
44786
|
-
let parsed;
|
|
45536
|
+
async function checkOfframpUsdCap(priceLookup, maxUsd, chainKey, tokenAddress, cryptoAmount) {
|
|
44787
45537
|
try {
|
|
44788
|
-
|
|
45538
|
+
const tokenInfo = tokenAddress ? resolveToken(tokenAddress, chainKey) : void 0;
|
|
45539
|
+
const lookupAddress2 = tokenInfo?.address ?? tokenAddress ?? "";
|
|
45540
|
+
const price = await priceLookup.getTokenPrice(chainKey, lookupAddress2);
|
|
45541
|
+
if (price && price > 0) {
|
|
45542
|
+
const usdValue = parseFloat(cryptoAmount) * price;
|
|
45543
|
+
if (usdValue > maxUsd) {
|
|
45544
|
+
return {
|
|
45545
|
+
code: "USD_LIMIT_EXCEEDED",
|
|
45546
|
+
message: `Off-ramp payout value ~$${usdValue.toFixed(2)} USD exceeds safety limit of $${maxUsd}. Use the CLI with --max-usd ${Math.ceil(usdValue)} or --skip-safety-check to proceed.`
|
|
45547
|
+
};
|
|
45548
|
+
}
|
|
45549
|
+
}
|
|
44789
45550
|
} catch {
|
|
44790
|
-
return void 0;
|
|
44791
|
-
}
|
|
44792
|
-
const isErc20 = Boolean(parsed.contractAddress);
|
|
44793
|
-
const action = isErc20 ? "get_token_holdings" : "get_balance";
|
|
44794
|
-
let args;
|
|
44795
|
-
if (action === "get_balance") {
|
|
44796
|
-
const chainKey = coinTypeToChainKey(parsed.coinId);
|
|
44797
|
-
if (!chainKey) return void 0;
|
|
44798
|
-
args = parsed.contractAddress ? { address, chainKey, tokenAddress: parsed.contractAddress } : { address, chainKey };
|
|
44799
|
-
} else {
|
|
44800
|
-
args = { address, coin: parsed.coinId };
|
|
44801
45551
|
}
|
|
45552
|
+
return void 0;
|
|
45553
|
+
}
|
|
45554
|
+
function buildFollowUp(chain2, tokenAddress, address, direction, paymentMethod) {
|
|
45555
|
+
if (!chain2) return void 0;
|
|
45556
|
+
const action = tokenAddress ? "get_token_holdings" : "get_balance";
|
|
45557
|
+
const args = tokenAddress ? { address, chain: chain2, tokenAddress } : { address, chain: chain2 };
|
|
44802
45558
|
if (direction === "onramp") {
|
|
44803
45559
|
return {
|
|
44804
45560
|
action,
|
|
@@ -44855,11 +45611,15 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44855
45611
|
// TransferActionProvider for the off-ramp payout path (confirm_offramp_transfer).
|
|
44856
45612
|
// Off-ramp sends crypto to a third-party deposit address, so the cap is
|
|
44857
45613
|
// strictly more important here than for peer-to-peer transfers.
|
|
44858
|
-
|
|
45614
|
+
// `blockchainClient` is the 4th (optional) arg, not the 1st — see
|
|
45615
|
+
// TransferActionProvider for the rationale (avoid churning ~70 test sites).
|
|
45616
|
+
// Normalize across action providers in a follow-up.
|
|
45617
|
+
constructor(walletContext, priceLookup, maxTransferUsd = DEFAULT_MAX_OFFRAMP_USD, blockchainClient) {
|
|
44859
45618
|
super();
|
|
44860
45619
|
this.walletContext = walletContext;
|
|
44861
45620
|
this.priceLookup = priceLookup;
|
|
44862
45621
|
this.maxTransferUsd = maxTransferUsd;
|
|
45622
|
+
this.blockchainClient = blockchainClient;
|
|
44863
45623
|
}
|
|
44864
45624
|
name = "onramp";
|
|
44865
45625
|
description = "Fiat on-ramp and off-ramp \u2014 buy crypto with fiat (USD, EUR, etc.) or sell crypto for fiat payouts. On-ramp: two-step flow (get quote \u2192 get checkout URL), provider sends crypto to the destination address. Off-ramp: three-step flow (get quote \u2192 get checkout URL \u2192 confirm_offramp_transfer). The off-ramp checkout URL runs in a plain browser which has no access to wallet keys, so the payout transaction must be broadcast natively via confirm_offramp_transfer once the user has read the provider's deposit address and exact amount. Destination/source address auto-derives from the configured wallet using the asset's chain. IMPORTANT: TWAK has no background polling or completion webhook for ramps. Tool responses include a `followUp` hint telling the agent exactly which balance tool to poll (with args and timing) to observe completion.";
|
|
@@ -44872,19 +45632,12 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44872
45632
|
this.confirmOfframpTransfer()
|
|
44873
45633
|
];
|
|
44874
45634
|
}
|
|
44875
|
-
async resolveAddress(providedAddress,
|
|
45635
|
+
async resolveAddress(providedAddress, chainKey) {
|
|
44876
45636
|
if (providedAddress) return providedAddress;
|
|
44877
|
-
if (!cryptoAsset) {
|
|
44878
|
-
throw new TransferError(
|
|
44879
|
-
"VALIDATION_ERROR",
|
|
44880
|
-
"walletAddress not provided and cryptoAsset is missing \u2014 cannot derive chain. Pass walletAddress or cryptoAsset."
|
|
44881
|
-
);
|
|
44882
|
-
}
|
|
44883
|
-
const chainKey = tokenIdToChain(cryptoAsset);
|
|
44884
45637
|
if (!chainKey) {
|
|
44885
45638
|
throw new TransferError(
|
|
44886
|
-
"
|
|
44887
|
-
|
|
45639
|
+
"VALIDATION_ERROR",
|
|
45640
|
+
"walletAddress not provided and chain is missing \u2014 cannot derive address. Pass walletAddress or chain."
|
|
44888
45641
|
);
|
|
44889
45642
|
}
|
|
44890
45643
|
return this.walletContext.activeWallet().getAddress(chainKey);
|
|
@@ -44896,15 +45649,15 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44896
45649
|
schema: import_zod12.z.object({
|
|
44897
45650
|
fiatAmount: import_zod12.z.coerce.number().positive().describe("Amount of fiat currency to spend (e.g., 100 for $100)."),
|
|
44898
45651
|
fiatCurrency: import_zod12.z.string().length(3).default("USD").describe("Fiat currency as a 3-letter ISO 4217 code (e.g., USD, EUR, GBP)."),
|
|
44899
|
-
|
|
44900
|
-
|
|
44901
|
-
),
|
|
45652
|
+
chain: import_zod12.z.string().describe(CHAIN_DESC),
|
|
45653
|
+
tokenAddress: import_zod12.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC),
|
|
44902
45654
|
walletAddress: import_zod12.z.string().optional().describe(
|
|
44903
|
-
"Destination wallet address where the purchased crypto will be sent. OMIT to auto-derive from the configured wallet using
|
|
45655
|
+
"Destination wallet address where the purchased crypto will be sent. OMIT to auto-derive from the configured wallet using the chain \u2014 this is the safe default and prevents sending funds to a stranger by mistake. Only supply this when operating on behalf of a different user."
|
|
44904
45656
|
)
|
|
44905
45657
|
}),
|
|
44906
45658
|
invoke: async (ctx, input2) => {
|
|
44907
|
-
const
|
|
45659
|
+
const { chainKey, assetId } = resolveAsset(input2.chain, input2.tokenAddress);
|
|
45660
|
+
const address = await this.resolveAddress(input2.walletAddress, chainKey);
|
|
44908
45661
|
const query = new URLSearchParams({
|
|
44909
45662
|
amount: input2.fiatAmount.toString(),
|
|
44910
45663
|
currency: input2.fiatCurrency,
|
|
@@ -44912,7 +45665,7 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44912
45665
|
});
|
|
44913
45666
|
const result = await ctx.httpClient.gateway(
|
|
44914
45667
|
"GET",
|
|
44915
|
-
`/v1/buycrypto/quote/${encodeURIComponent(
|
|
45668
|
+
`/v1/buycrypto/quote/${encodeURIComponent(assetId)}`,
|
|
44916
45669
|
void 0,
|
|
44917
45670
|
query.toString()
|
|
44918
45671
|
);
|
|
@@ -44922,9 +45675,10 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44922
45675
|
success: true,
|
|
44923
45676
|
summary: summarizeOnrampQuotes(quotes),
|
|
44924
45677
|
address,
|
|
44925
|
-
|
|
45678
|
+
chain: chainKey,
|
|
45679
|
+
tokenAddress: input2.tokenAddress,
|
|
44926
45680
|
quotes,
|
|
44927
|
-
nextAction: quotes.length > 0 ? "Call get_onramp_url with the chosen quoteId to receive a browser redirect URL for checkout. quotes[0] is the best deal (lowest vsBestPct)." : "No quotes available \u2014 try a different fiatAmount or
|
|
45681
|
+
nextAction: quotes.length > 0 ? "Call get_onramp_url with the chosen quoteId to receive a browser redirect URL for checkout. quotes[0] is the best deal (lowest vsBestPct)." : "No quotes available \u2014 try a different fiatAmount or chain/tokenAddress."
|
|
44928
45682
|
};
|
|
44929
45683
|
}
|
|
44930
45684
|
};
|
|
@@ -44936,14 +45690,16 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44936
45690
|
schema: import_zod12.z.object({
|
|
44937
45691
|
quoteId: import_zod12.z.string().describe("Quote ID returned by get_onramp_quote."),
|
|
44938
45692
|
walletAddress: import_zod12.z.string().optional().describe(
|
|
44939
|
-
"Destination wallet address. OMIT to auto-derive from the configured wallet \u2014 requires
|
|
45693
|
+
"Destination wallet address. OMIT to auto-derive from the configured wallet \u2014 requires chain so the address can be derived. Only supply this when operating on behalf of a different user."
|
|
44940
45694
|
),
|
|
44941
|
-
|
|
44942
|
-
|
|
44943
|
-
)
|
|
45695
|
+
chain: import_zod12.z.string().optional().describe(
|
|
45696
|
+
`${CHAIN_DESC} Required when walletAddress is omitted so the address can be derived.`
|
|
45697
|
+
),
|
|
45698
|
+
tokenAddress: import_zod12.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC)
|
|
44944
45699
|
}),
|
|
44945
45700
|
invoke: async (ctx, input2) => {
|
|
44946
|
-
const
|
|
45701
|
+
const chainKey = input2.chain !== void 0 ? resolveAsset(input2.chain).chainKey : void 0;
|
|
45702
|
+
const address = await this.resolveAddress(input2.walletAddress, chainKey);
|
|
44947
45703
|
const query = new URLSearchParams({ address });
|
|
44948
45704
|
let result;
|
|
44949
45705
|
try {
|
|
@@ -44966,7 +45722,7 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44966
45722
|
throw err;
|
|
44967
45723
|
}
|
|
44968
45724
|
const url = result.url ?? result.redirect_url;
|
|
44969
|
-
const followUp = url ? buildFollowUp(input2.
|
|
45725
|
+
const followUp = url ? buildFollowUp(chainKey, input2.tokenAddress, address, "onramp") : void 0;
|
|
44970
45726
|
return {
|
|
44971
45727
|
success: Boolean(url),
|
|
44972
45728
|
address,
|
|
@@ -44983,24 +45739,24 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44983
45739
|
description: "Step 1 of selling crypto for fiat: get quotes from available providers for converting a crypto amount into fiat. Returns an array of quotes each with a quoteId \u2014 pass one to get_offramp_url to generate the checkout redirect for completing KYC and order creation. The provider page cannot sign the payout (no key access in the browser); the agent must broadcast it natively via confirm_offramp_transfer once the deposit address is known.",
|
|
44984
45740
|
schema: import_zod12.z.object({
|
|
44985
45741
|
cryptoAmount: import_zod12.z.coerce.number().positive().describe("Amount of crypto to sell (e.g., 0.1 for 0.1 ETH)."),
|
|
44986
|
-
|
|
44987
|
-
|
|
44988
|
-
),
|
|
45742
|
+
chain: import_zod12.z.string().describe(CHAIN_DESC),
|
|
45743
|
+
tokenAddress: import_zod12.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC),
|
|
44989
45744
|
fiatCurrency: import_zod12.z.string().length(3).default("USD").describe("Target fiat currency as a 3-letter ISO 4217 code (e.g., USD, EUR, GBP)."),
|
|
44990
45745
|
paymentMethod: import_zod12.z.enum(["ANY", "bank_transfer", "card"]).optional().default("ANY").describe(
|
|
44991
45746
|
'Payout method. Default "ANY" is a sentinel that matches any provider \u2014 recommended unless you specifically need "card" or "bank_transfer". Sending an unrecognised value (or `bank_transfer` in regions without bank-payout coverage) silently returns an empty quotes array.'
|
|
44992
45747
|
),
|
|
44993
45748
|
walletAddress: import_zod12.z.string().optional().describe(
|
|
44994
|
-
"Source wallet address holding the crypto to be sold. OMIT to auto-derive from the configured wallet using
|
|
45749
|
+
"Source wallet address holding the crypto to be sold. OMIT to auto-derive from the configured wallet using the chain \u2014 this is the safe default. Only supply this when operating on behalf of a different user."
|
|
44995
45750
|
)
|
|
44996
45751
|
}),
|
|
44997
45752
|
invoke: async (ctx, input2) => {
|
|
44998
|
-
const
|
|
45753
|
+
const { chainKey, assetId } = resolveAsset(input2.chain, input2.tokenAddress);
|
|
45754
|
+
const address = await this.resolveAddress(input2.walletAddress, chainKey);
|
|
44999
45755
|
const query = new URLSearchParams({
|
|
45000
45756
|
amount: input2.cryptoAmount.toString(),
|
|
45001
45757
|
currency: input2.fiatCurrency,
|
|
45002
45758
|
// iOS sends `asset_id` as both path segment AND query param. Mirroring that.
|
|
45003
|
-
asset_id:
|
|
45759
|
+
asset_id: assetId,
|
|
45004
45760
|
address,
|
|
45005
45761
|
// The HTTP gateway uses `payment_method` for both buy and sell, mirroring the
|
|
45006
45762
|
// iOS production client. The sell proto declares `payout_method` but the
|
|
@@ -45012,7 +45768,7 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
45012
45768
|
});
|
|
45013
45769
|
const result = await ctx.httpClient.gateway(
|
|
45014
45770
|
"GET",
|
|
45015
|
-
`/v1/sellcrypto/quote/${encodeURIComponent(
|
|
45771
|
+
`/v1/sellcrypto/quote/${encodeURIComponent(assetId)}`,
|
|
45016
45772
|
void 0,
|
|
45017
45773
|
query.toString()
|
|
45018
45774
|
);
|
|
@@ -45022,9 +45778,10 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
45022
45778
|
success: true,
|
|
45023
45779
|
summary: summarizeOfframpQuotes(quotes),
|
|
45024
45780
|
address,
|
|
45025
|
-
|
|
45781
|
+
chain: chainKey,
|
|
45782
|
+
tokenAddress: input2.tokenAddress,
|
|
45026
45783
|
quotes,
|
|
45027
|
-
nextAction: quotes.length > 0 ? "Call get_offramp_url with the chosen quoteId to receive a browser redirect URL for the sale. quotes[0] is the best deal (lowest vsBestPct)." : "No quotes available \u2014 try a different cryptoAmount,
|
|
45784
|
+
nextAction: quotes.length > 0 ? "Call get_offramp_url with the chosen quoteId to receive a browser redirect URL for the sale. quotes[0] is the best deal (lowest vsBestPct)." : "No quotes available \u2014 try a different cryptoAmount, chain/tokenAddress, or paymentMethod."
|
|
45028
45785
|
};
|
|
45029
45786
|
}
|
|
45030
45787
|
};
|
|
@@ -45036,14 +45793,16 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
45036
45793
|
schema: import_zod12.z.object({
|
|
45037
45794
|
quoteId: import_zod12.z.string().describe("Quote ID returned by get_offramp_quote."),
|
|
45038
45795
|
walletAddress: import_zod12.z.string().optional().describe(
|
|
45039
|
-
"Source wallet address. OMIT to auto-derive from the configured wallet \u2014 requires
|
|
45796
|
+
"Source wallet address. OMIT to auto-derive from the configured wallet \u2014 requires chain so the address can be derived. Only supply this when operating on behalf of a different user."
|
|
45040
45797
|
),
|
|
45041
|
-
|
|
45042
|
-
|
|
45043
|
-
)
|
|
45798
|
+
chain: import_zod12.z.string().optional().describe(
|
|
45799
|
+
`${CHAIN_DESC} Required when walletAddress is omitted so the address can be derived.`
|
|
45800
|
+
),
|
|
45801
|
+
tokenAddress: import_zod12.z.string().trim().min(1).optional().describe(TOKEN_ADDR_DESC)
|
|
45044
45802
|
}),
|
|
45045
45803
|
invoke: async (ctx, input2) => {
|
|
45046
|
-
const
|
|
45804
|
+
const chainKey = input2.chain !== void 0 ? resolveAsset(input2.chain).chainKey : void 0;
|
|
45805
|
+
const address = await this.resolveAddress(input2.walletAddress, chainKey);
|
|
45047
45806
|
const query = new URLSearchParams({ address });
|
|
45048
45807
|
let result;
|
|
45049
45808
|
try {
|
|
@@ -45066,7 +45825,7 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
45066
45825
|
throw err;
|
|
45067
45826
|
}
|
|
45068
45827
|
const url = result.url ?? result.redirect_url;
|
|
45069
|
-
const followUp = url ? buildFollowUp(input2.
|
|
45828
|
+
const followUp = url ? buildFollowUp(chainKey, input2.tokenAddress, address, "offramp") : void 0;
|
|
45070
45829
|
return {
|
|
45071
45830
|
success: Boolean(url),
|
|
45072
45831
|
address,
|
|
@@ -45081,7 +45840,7 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
45081
45840
|
confirmOfframpTransfer() {
|
|
45082
45841
|
return {
|
|
45083
45842
|
name: "confirm_offramp_transfer",
|
|
45084
|
-
description: "Step 3 of selling crypto for fiat: sign and broadcast the payout transaction to the provider's deposit address. Call this AFTER the user has completed KYC in the browser and can read the deposit address + exact amount the provider displayed. Works with any wallet provider that implements sendTransaction (the local agent wallet, or a WalletConnect-paired external wallet). EVM-only \u2014
|
|
45843
|
+
description: "Step 3 of selling crypto for fiat: sign and broadcast the payout transaction to the provider's deposit address. Call this AFTER the user has completed KYC in the browser and can read the deposit address + exact amount the provider displayed. Works with any wallet provider that implements sendTransaction (the local agent wallet, or a WalletConnect-paired external wallet). EVM-only \u2014 non-EVM chains (BTC, Solana, Cosmos, etc.) are not supported by this action. Returns a tx hash on success; fiat payout from the provider then arrives separately (no on-chain signal for that step).",
|
|
45085
45844
|
schema: import_zod12.z.object({
|
|
45086
45845
|
depositAddress: import_zod12.z.string().describe(
|
|
45087
45846
|
"The deposit address displayed by the provider after KYC. This is a third-party address \u2014 verify it matches what the provider shows before calling."
|
|
@@ -45089,8 +45848,9 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
45089
45848
|
cryptoAmount: import_zod12.z.string().describe(
|
|
45090
45849
|
'Exact amount to send in human-readable format (e.g., "0.1" for 0.1 ETH). Must match the amount the provider displays; under-sending will invalidate the sell order.'
|
|
45091
45850
|
),
|
|
45092
|
-
|
|
45093
|
-
|
|
45851
|
+
chain: import_zod12.z.string().describe(CHAIN_DESC),
|
|
45852
|
+
tokenAddress: import_zod12.z.string().trim().min(1).optional().describe(
|
|
45853
|
+
"ERC-20 token contract address. Omit for the chain native coin. This action is EVM-only \u2014 non-EVM tokens are rejected."
|
|
45094
45854
|
),
|
|
45095
45855
|
decimals: import_zod12.z.coerce.number().optional().describe(
|
|
45096
45856
|
"Token decimals. REQUIRED for ERC-20 assets (pass 6 for USDC/USDT, 18 for DAI, etc.) \u2014 omitting it will be rejected rather than silently defaulting to 18, because a wrong-decimals encode can over-pay the provider by orders of magnitude. Ignored for native coins."
|
|
@@ -45104,82 +45864,37 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
45104
45864
|
}),
|
|
45105
45865
|
invoke: async (_ctx, input2) => {
|
|
45106
45866
|
const signer = this.walletContext.activeWallet();
|
|
45107
|
-
|
|
45108
|
-
|
|
45109
|
-
|
|
45110
|
-
} catch {
|
|
45111
|
-
throw new TransferError("VALIDATION_ERROR", `Invalid cryptoAsset: ${input2.cryptoAsset}`);
|
|
45112
|
-
}
|
|
45113
|
-
const chainKey = coinTypeToChainKey(parsed.coinId);
|
|
45114
|
-
if (!chainKey) {
|
|
45867
|
+
const { chainKey } = resolveAsset(input2.chain);
|
|
45868
|
+
const chainDef = requireChain(chainKey);
|
|
45869
|
+
if (chainDef.namespace !== "eip155") {
|
|
45115
45870
|
throw new TransferError(
|
|
45116
45871
|
"CHAIN_UNSUPPORTED",
|
|
45117
|
-
`
|
|
45118
|
-
);
|
|
45119
|
-
}
|
|
45120
|
-
const chainDef = getChainById(chainKey);
|
|
45121
|
-
if (!chainDef || chainDef.namespace !== "eip155") {
|
|
45122
|
-
throw new TransferError(
|
|
45123
|
-
"CHAIN_UNSUPPORTED",
|
|
45124
|
-
`confirm_offramp_transfer is EVM-only. For ${chainKey} off-ramps use the CLI: twak onramp sell-confirm --asset ${input2.cryptoAsset} --to <deposit> --amount <n>${input2.memo ? " --memo <tag>" : ""}.`
|
|
45872
|
+
`confirm_offramp_transfer is EVM-only. ${chainKey} is not supported.`
|
|
45125
45873
|
);
|
|
45126
45874
|
}
|
|
45127
45875
|
const caip2 = `eip155:${chainDef.chainId}`;
|
|
45128
|
-
|
|
45129
|
-
throw new TransferError(
|
|
45130
|
-
"VALIDATION_ERROR",
|
|
45131
|
-
`depositAddress is not a valid EVM address: ${input2.depositAddress}. Double-check what the provider displayed.`
|
|
45132
|
-
);
|
|
45133
|
-
}
|
|
45134
|
-
if (parsed.contractAddress && !EVM_ADDRESS_RE.test(parsed.contractAddress)) {
|
|
45135
|
-
throw new TransferError(
|
|
45136
|
-
"VALIDATION_ERROR",
|
|
45137
|
-
`Malformed token contract in cryptoAsset: ${parsed.contractAddress}`
|
|
45138
|
-
);
|
|
45139
|
-
}
|
|
45140
|
-
if (parsed.contractAddress && input2.decimals === void 0) {
|
|
45141
|
-
throw new TransferError(
|
|
45142
|
-
"VALIDATION_ERROR",
|
|
45143
|
-
`decimals is required for ERC-20 asset ${input2.cryptoAsset}. Pass the token's decimals explicitly (6 for USDC/USDT, 18 for DAI, etc.) \u2014 silently defaulting to 18 could over-pay the provider by orders of magnitude.`
|
|
45144
|
-
);
|
|
45145
|
-
}
|
|
45146
|
-
const decimals = parsed.contractAddress ? input2.decimals : 18;
|
|
45147
|
-
try {
|
|
45148
|
-
assertStrictPositiveAmount(input2.cryptoAmount, decimals);
|
|
45149
|
-
} catch (err) {
|
|
45150
|
-
throw new TransferError(
|
|
45151
|
-
"VALIDATION_ERROR",
|
|
45152
|
-
err instanceof Error ? err.message : String(err)
|
|
45153
|
-
);
|
|
45154
|
-
}
|
|
45876
|
+
const decimals = validateOfframpInput(input2, chainKey, chainDef.decimals);
|
|
45155
45877
|
if (this.priceLookup) {
|
|
45156
|
-
|
|
45157
|
-
|
|
45158
|
-
|
|
45159
|
-
|
|
45160
|
-
|
|
45161
|
-
|
|
45162
|
-
|
|
45163
|
-
|
|
45164
|
-
success: false,
|
|
45165
|
-
code: "USD_LIMIT_EXCEEDED",
|
|
45166
|
-
message: `Off-ramp payout value ~$${usdValue.toFixed(2)} USD exceeds safety limit of $${this.maxTransferUsd}. Use the CLI with --max-usd ${Math.ceil(usdValue)} or --skip-safety-check to proceed.`
|
|
45167
|
-
};
|
|
45168
|
-
}
|
|
45169
|
-
}
|
|
45170
|
-
} catch {
|
|
45171
|
-
}
|
|
45878
|
+
const cap = await checkOfframpUsdCap(
|
|
45879
|
+
this.priceLookup,
|
|
45880
|
+
this.maxTransferUsd,
|
|
45881
|
+
chainKey,
|
|
45882
|
+
input2.tokenAddress,
|
|
45883
|
+
input2.cryptoAmount
|
|
45884
|
+
);
|
|
45885
|
+
if (cap) return { success: false, ...cap };
|
|
45172
45886
|
}
|
|
45173
45887
|
const atomic = parseAmount(input2.cryptoAmount, decimals);
|
|
45174
45888
|
let txHash;
|
|
45175
|
-
if (
|
|
45176
|
-
|
|
45177
|
-
|
|
45178
|
-
|
|
45179
|
-
|
|
45180
|
-
|
|
45181
|
-
|
|
45182
|
-
|
|
45889
|
+
if (input2.tokenAddress) {
|
|
45890
|
+
if (!this.blockchainClient) {
|
|
45891
|
+
throw new TransferError(
|
|
45892
|
+
"WALLET_NOT_FOUND",
|
|
45893
|
+
"BlockchainClient not available \u2014 off-ramp token payout requires a configured blockchain client"
|
|
45894
|
+
);
|
|
45895
|
+
}
|
|
45896
|
+
const pending = await this.blockchainClient.contract(erc20).withAddress(input2.tokenAddress, chainKey).write.transfer([input2.depositAddress, BigInt(atomic)], signer);
|
|
45897
|
+
txHash = pending.txHash;
|
|
45183
45898
|
} else {
|
|
45184
45899
|
txHash = await signer.sendTransaction({
|
|
45185
45900
|
chainId: caip2,
|
|
@@ -45193,13 +45908,13 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
45193
45908
|
success: true,
|
|
45194
45909
|
txHash,
|
|
45195
45910
|
chain: chainKey,
|
|
45911
|
+
tokenAddress: input2.tokenAddress,
|
|
45196
45912
|
to: input2.depositAddress,
|
|
45197
45913
|
amount: input2.cryptoAmount,
|
|
45198
|
-
asset: input2.cryptoAsset,
|
|
45199
45914
|
memoIgnored: input2.memo ? `A memo was supplied (${input2.memo}) but EVM transactions have no memo field \u2014 it was ignored. If the provider requires a memo, they likely expect a non-EVM chain.` : void 0,
|
|
45200
45915
|
quoteId: input2.quoteId,
|
|
45201
45916
|
explorer: explorerUrl,
|
|
45202
|
-
summary: `Off-ramp payout of ${input2.cryptoAmount} ${input2.
|
|
45917
|
+
summary: `Off-ramp payout of ${input2.cryptoAmount}${input2.tokenAddress ? ` ${input2.tokenAddress.slice(0, 10)}\u2026` : ` ${chainDef.symbol}`} broadcast to ${input2.depositAddress} on ${chainKey}. Fiat payout from the provider arrives separately.`
|
|
45203
45918
|
};
|
|
45204
45919
|
}
|
|
45205
45920
|
};
|
|
@@ -45231,7 +45946,7 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45231
45946
|
type: import_zod13.z.enum(["dca", "limit"]).describe("Automation type: dca (recurring) or limit (price trigger)"),
|
|
45232
45947
|
fromToken: import_zod13.z.string().describe("Source token symbol or address"),
|
|
45233
45948
|
toToken: import_zod13.z.string().describe("Destination token symbol or address"),
|
|
45234
|
-
chain: import_zod13.z.string().default("ethereum").describe(
|
|
45949
|
+
chain: import_zod13.z.string().default("ethereum").describe(CHAIN_DESC),
|
|
45235
45950
|
amount: import_zod13.z.string().describe("Amount of fromToken per execution"),
|
|
45236
45951
|
intervalMs: import_zod13.z.coerce.number().optional().describe("Interval in milliseconds for DCA (e.g., 86400000 for daily)"),
|
|
45237
45952
|
targetPrice: import_zod13.z.coerce.number().optional().describe("Target price in USD for limit orders"),
|
|
@@ -45252,11 +45967,12 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45252
45967
|
message: "Limit orders require targetPrice and condition"
|
|
45253
45968
|
};
|
|
45254
45969
|
}
|
|
45970
|
+
const chainKey = resolveChain(input2.chain);
|
|
45255
45971
|
const record2 = await this.automationStore.create({
|
|
45256
45972
|
type: input2.type,
|
|
45257
45973
|
fromToken: input2.fromToken,
|
|
45258
45974
|
toToken: input2.toToken,
|
|
45259
|
-
chain:
|
|
45975
|
+
chain: chainKey,
|
|
45260
45976
|
amount: input2.amount,
|
|
45261
45977
|
intervalMs: input2.intervalMs,
|
|
45262
45978
|
targetPrice: input2.targetPrice,
|
|
@@ -45432,7 +46148,7 @@ var X402ActionProvider = class extends ActionProvider {
|
|
|
45432
46148
|
x402Quote() {
|
|
45433
46149
|
return {
|
|
45434
46150
|
name: "x402_quote",
|
|
45435
|
-
description: 'Preview the payment requirements at an x402 payment-gated endpoint WITHOUT signing or paying. Returns the full list of payment options (each entry has chain, asset address, tokenName, amount, transferMethod, requiresApproval, and a pre-formatted `description` string). CRITICAL \u2014 after this returns, ALWAYS surface ALL entries in `accepts[]` to the user (as a short numbered list: chain \xB7 token \xB7 amount \xB7 method \xB7 "preferred" tag if applicable) and ask which to use BEFORE calling x402_request. Do not silently pick the `preferred` entry just because it is marked preferred \u2014 that is the picker default if no override is passed, but the user deserves to see every option (other chains may have cheaper gas, the user may hold balance only on a specific chain, etc.). The only exception: if you have already done a balance check and only one entry is viable, you may proceed with that one after noting the others as "unavailable (no balance)". IMPORTANT: each entry in `accepts[]` is an INDEPENDENT payment option \u2014 a unique (chain, token, transfer method) triple. Do NOT merge entries by chain (e.g. "BSC USDC via EIP-3009 or Permit2" is wrong if BSC USDC is only offered via Permit2 and a separate BSC entry for a different token offers EIP-3009). Read each entry as its own row; use the per-entry `description` field verbatim when surfacing options to the user. Once the user picks, call x402_request with the agreed maxPaymentAtomic plus optionally preferNetwork/preferMethod/preferAsset to disambiguate. INTERPRETING `requiresApproval`: `false` (EIP-3009, e.g. USDC on Base / Ethereum) \u2014 gasless. Sign the payment and you are done. No setup, no on-chain tx for the buyer, ever. `true` (Permit2, e.g. USDC on BSC) \u2014 the industry-standard Permit2 flow used by Uniswap, 1inch, and most major DeFi apps. THE FIRST x402_request to a Permit2 endpoint for a given token+chain broadcasts a one-time `approve(Permit2, MAX_UINT256)` tx (the buyer pays the gas \u2014 usually a few cents on BSC / Base, more on Ethereum mainnet). The allowance granted is unlimited for the canonical Permit2 contract at 0x000000000022D473030F116dDEE9F6B43aC78BA3. After that, every future Permit2 payment for that token is signature-only (gasless). Revocable any time via `twak erc20 revoke`. The per-call spend is still capped at the request `maxPaymentAtomic` \u2014 unlimited allowance does NOT mean unlimited per-call spend. When `requiresApproval: true` AND the buyer has not used Permit2 for that token on that chain before, the agent SHOULD tell the user concretely what is about to happen (one-time approval on <chain> for <token>, gas paid in the chain native token, then the payment) before calling x402_request. BALANCE CHECK \u2014 AFTER this quote returns and BEFORE recommending any route or showing the menu to the user, check the wallet balance for every viable entry in `accepts`. For each entry: call `get_address(
|
|
46151
|
+
description: 'Preview the payment requirements at an x402 payment-gated endpoint WITHOUT signing or paying. Returns the full list of payment options (each entry has chain, asset address, tokenName, amount, transferMethod, requiresApproval, and a pre-formatted `description` string). CRITICAL \u2014 after this returns, ALWAYS surface ALL entries in `accepts[]` to the user (as a short numbered list: chain \xB7 token \xB7 amount \xB7 method \xB7 "preferred" tag if applicable) and ask which to use BEFORE calling x402_request. Do not silently pick the `preferred` entry just because it is marked preferred \u2014 that is the picker default if no override is passed, but the user deserves to see every option (other chains may have cheaper gas, the user may hold balance only on a specific chain, etc.). The only exception: if you have already done a balance check and only one entry is viable, you may proceed with that one after noting the others as "unavailable (no balance)". IMPORTANT: each entry in `accepts[]` is an INDEPENDENT payment option \u2014 a unique (chain, token, transfer method) triple. Do NOT merge entries by chain (e.g. "BSC USDC via EIP-3009 or Permit2" is wrong if BSC USDC is only offered via Permit2 and a separate BSC entry for a different token offers EIP-3009). Read each entry as its own row; use the per-entry `description` field verbatim when surfacing options to the user. Once the user picks, call x402_request with the agreed maxPaymentAtomic plus optionally preferNetwork/preferMethod/preferAsset to disambiguate. INTERPRETING `requiresApproval`: `false` (EIP-3009, e.g. USDC on Base / Ethereum) \u2014 gasless. Sign the payment and you are done. No setup, no on-chain tx for the buyer, ever. `true` (Permit2, e.g. USDC on BSC) \u2014 the industry-standard Permit2 flow used by Uniswap, 1inch, and most major DeFi apps. THE FIRST x402_request to a Permit2 endpoint for a given token+chain broadcasts a one-time `approve(Permit2, MAX_UINT256)` tx (the buyer pays the gas \u2014 usually a few cents on BSC / Base, more on Ethereum mainnet). The allowance granted is unlimited for the canonical Permit2 contract at 0x000000000022D473030F116dDEE9F6B43aC78BA3. After that, every future Permit2 payment for that token is signature-only (gasless). Revocable any time via `twak erc20 revoke`. The per-call spend is still capped at the request `maxPaymentAtomic` \u2014 unlimited allowance does NOT mean unlimited per-call spend. When `requiresApproval: true` AND the buyer has not used Permit2 for that token on that chain before, the agent SHOULD tell the user concretely what is about to happen (one-time approval on <chain> for <token>, gas paid in the chain native token, then the payment) before calling x402_request. BALANCE CHECK \u2014 AFTER this quote returns and BEFORE recommending any route or showing the menu to the user, check the wallet balance for every viable entry in `accepts`. For each entry: call `get_address(chain)` to find the wallet address on that chain, then `get_balance` for the entry\'s asset (or `wallet_balance(chain)` for native coins). Filter or annotate the menu \u2014 only recommend routes where the wallet has enough of the asset to cover `amount`. If a Permit2 entry needs a first-time approval (`requiresApproval: true`), also confirm the wallet holds some native chain gas (gas amount varies by chain). If no viable route has sufficient balance, tell the user upfront \u2014 do not present a menu that includes routes they cannot pay. This avoids the failure mode where the agent recommends a chain the user has no balance on and the user finds out only after committing. No wallet required \u2014 read-only HTTPS + 402 parse. HTTPS-only; private IPs and non-HTTPS schemes are rejected.',
|
|
45436
46152
|
schema: import_zod14.z.object({
|
|
45437
46153
|
url: import_zod14.z.string().describe(
|
|
45438
46154
|
"Target endpoint URL. Must be HTTPS. Resolves via DNS; private/local IPs are rejected."
|
|
@@ -45448,7 +46164,7 @@ var X402ActionProvider = class extends ActionProvider {
|
|
|
45448
46164
|
x402Request() {
|
|
45449
46165
|
return {
|
|
45450
46166
|
name: "x402_request",
|
|
45451
|
-
description: "Make an HTTP request to an x402 payment-gated endpoint. Handles the full flow: sends the initial request, parses any 402 challenge, signs an EIP-3009 or Permit2 payment authorization with the agent wallet, retries with the X-Payment header. PAYMENT METHODS: x402 endpoints offer one or more options in their 402 response. EIP-3009 is gasless for the buyer (no setup, no approval) \u2014 preferred when available, e.g. USDC on Base / Ethereum. Permit2 needs a one-time approve(Permit2, MAX) tx on the chain (the buyer pays the gas \u2014 usually a few cents on BSC / Base, more on Ethereum mainnet), then every future Permit2 payment for that token is signature-only. Permit2 is required when the token does not support EIP-3009 \u2014 e.g. USDC on BSC. SAFETY \u2014 autoApprove decision: When autoApprove=true (default), this action silently broadcasts the one-time Permit2 approval if your wallet has not yet approved the canonical Permit2 contract for that token. The approval is to the same Permit2 contract Uniswap, 1inch, and most major DeFi apps use \u2014 it is not exotic infrastructure. The approval alone does not move funds; each subsequent payment still needs a fresh signature. Revocable any time via `twak erc20 revoke`. For first-time use of an endpoint you SHOULD tell the user concretely what is about to happen (one-time approval on <chain> for <token>, gas paid in the chain native token, then the payment) and get their go-ahead before calling. After the approval lands, subsequent Permit2 payments for that token are free of further approvals. Set autoApprove=false to surface code: 'PERMIT2_APPROVAL_REQUIRED' instead of broadcasting \u2014 useful when the user is not around or you want to handle the approval out-of-band. maxPaymentAtomic is the user's pre-commitment to a maximum payment in the asset's smallest unit (e.g. \"10000\" = 0.01 USDC at 6 decimals). If the server demands more than this cap, the call returns code: 'PAYMENT_AMOUNT_EXCEEDED' \u2014 DO NOT retry with a higher cap without asking the user. BALANCE CHECK \u2014 Before calling this action, verify the wallet holds enough of the chosen asset on the chosen chain. Call `get_address(
|
|
46167
|
+
description: "Make an HTTP request to an x402 payment-gated endpoint. Handles the full flow: sends the initial request, parses any 402 challenge, signs an EIP-3009 or Permit2 payment authorization with the agent wallet, retries with the X-Payment header. PAYMENT METHODS: x402 endpoints offer one or more options in their 402 response. EIP-3009 is gasless for the buyer (no setup, no approval) \u2014 preferred when available, e.g. USDC on Base / Ethereum. Permit2 needs a one-time approve(Permit2, MAX) tx on the chain (the buyer pays the gas \u2014 usually a few cents on BSC / Base, more on Ethereum mainnet), then every future Permit2 payment for that token is signature-only. Permit2 is required when the token does not support EIP-3009 \u2014 e.g. USDC on BSC. SAFETY \u2014 autoApprove decision: When autoApprove=true (default), this action silently broadcasts the one-time Permit2 approval if your wallet has not yet approved the canonical Permit2 contract for that token. The approval is to the same Permit2 contract Uniswap, 1inch, and most major DeFi apps use \u2014 it is not exotic infrastructure. The approval alone does not move funds; each subsequent payment still needs a fresh signature. Revocable any time via `twak erc20 revoke`. For first-time use of an endpoint you SHOULD tell the user concretely what is about to happen (one-time approval on <chain> for <token>, gas paid in the chain native token, then the payment) and get their go-ahead before calling. After the approval lands, subsequent Permit2 payments for that token are free of further approvals. Set autoApprove=false to surface code: 'PERMIT2_APPROVAL_REQUIRED' instead of broadcasting \u2014 useful when the user is not around or you want to handle the approval out-of-band. maxPaymentAtomic is the user's pre-commitment to a maximum payment in the asset's smallest unit (e.g. \"10000\" = 0.01 USDC at 6 decimals). If the server demands more than this cap, the call returns code: 'PAYMENT_AMOUNT_EXCEEDED' \u2014 DO NOT retry with a higher cap without asking the user. BALANCE CHECK \u2014 Before calling this action, verify the wallet holds enough of the chosen asset on the chosen chain. Call `get_address(chain)` to find the wallet address, then `get_balance` for the asset (or `wallet_balance(chain)` for native coins). For Permit2 first-time use, also confirm the wallet holds some native chain gas (amount varies by chain). Surface insufficient balance to the user BEFORE calling this action \u2014 a failed payment mid-flow can leave the wallet in a half-state (e.g. Permit2 approval broadcast, then the transfer signature fails). The balance check should already have happened during the `x402_quote` flow that produced the menu; redo it here only if you skipped quote or the balance might have changed since. WALLET \u2014 Signs through whichever wallet mode is active for this session (local by default; switchable via `switch_wallet_mode`). If mode='local', signs in-process (no external prompt). If mode='walletconnect', signs over the WalletConnect relay \u2014 the user must approve each signature on their paired wallet. To retrieve the active wallet's address for a chain (e.g. to call `wallet_balance` / `get_balance`), call `get_address` with the chain key (e.g. \"bsc\", \"base\"), or `list_addresses` for all chains at once. HTTPS-only; non-HTTPS schemes and private IPs are rejected before any payment signing.",
|
|
45452
46168
|
schema: import_zod14.z.object({
|
|
45453
46169
|
url: import_zod14.z.string().describe(
|
|
45454
46170
|
"Target endpoint URL. Must be HTTPS. Resolves via DNS; private/local IPs are rejected before any payment."
|
|
@@ -45572,8 +46288,8 @@ var WalletModeActionProvider = class extends ActionProvider {
|
|
|
45572
46288
|
"WalletConnect is not configured. Set WALLETCONNECT_PROJECT_ID (env var or `twak init --wc-project-id <id>`) and restart the MCP server."
|
|
45573
46289
|
);
|
|
45574
46290
|
}
|
|
45575
|
-
const
|
|
45576
|
-
await this.walletContext.beginWalletConnect(
|
|
46291
|
+
const wc3 = this.walletConnectFactory();
|
|
46292
|
+
await this.walletContext.beginWalletConnect(wc3);
|
|
45577
46293
|
return this.snapshotResponse(this.walletContext.snapshot());
|
|
45578
46294
|
}
|
|
45579
46295
|
};
|
|
@@ -50224,7 +50940,6 @@ var StdioServerTransport = class {
|
|
|
50224
50940
|
init_dist();
|
|
50225
50941
|
var import_http = require("http");
|
|
50226
50942
|
var import_crypto7 = require("crypto");
|
|
50227
|
-
init_dist2();
|
|
50228
50943
|
init_dist();
|
|
50229
50944
|
var SERVER_INSTRUCTIONS = `twak signs blockchain operations through one of two wallet modes:
|
|
50230
50945
|
|
|
@@ -50309,263 +51024,18 @@ var TwakMcpServer = class {
|
|
|
50309
51024
|
await this.server.connect(transport);
|
|
50310
51025
|
}
|
|
50311
51026
|
};
|
|
50312
|
-
var CAIP2_TO_CHAIN = {
|
|
50313
|
-
"eip155:1": "ethereum",
|
|
50314
|
-
"eip155:56": "smartchain",
|
|
50315
|
-
"eip155:137": "polygon",
|
|
50316
|
-
"eip155:42161": "arbitrum",
|
|
50317
|
-
"eip155:8453": "base",
|
|
50318
|
-
"eip155:10": "optimism",
|
|
50319
|
-
"eip155:43114": "avalanche"
|
|
50320
|
-
};
|
|
50321
|
-
var CAIP2_TO_BC_CHAIN = {
|
|
50322
|
-
"eip155:1": "ethereum",
|
|
50323
|
-
"eip155:56": "bsc",
|
|
50324
|
-
"eip155:137": "polygon",
|
|
50325
|
-
"eip155:42161": "arbitrum",
|
|
50326
|
-
"eip155:8453": "base",
|
|
50327
|
-
"eip155:10": "optimism",
|
|
50328
|
-
"eip155:43114": "avalanche"
|
|
50329
|
-
};
|
|
50330
|
-
var RPC_REGISTRY = {
|
|
50331
|
-
"eip155:1": {
|
|
50332
|
-
rpc: "https://eth.llamarpc.com",
|
|
50333
|
-
native: "eth",
|
|
50334
|
-
nativeDecimals: 18,
|
|
50335
|
-
tokens: {
|
|
50336
|
-
usdc: { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", decimals: 6 },
|
|
50337
|
-
usdt: { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6 },
|
|
50338
|
-
dai: { address: "0x6B175474E89094C44Da98b954EedeAC495271d0F", decimals: 18 }
|
|
50339
|
-
}
|
|
50340
|
-
},
|
|
50341
|
-
"eip155:56": {
|
|
50342
|
-
rpc: "https://bsc-rpc.publicnode.com",
|
|
50343
|
-
native: "bnb",
|
|
50344
|
-
nativeDecimals: 18,
|
|
50345
|
-
tokens: {
|
|
50346
|
-
usdt: { address: "0x55d398326f99059fF775485246999027B3197955", decimals: 18 },
|
|
50347
|
-
usdc: { address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", decimals: 18 },
|
|
50348
|
-
twt: { address: "0x4B0F1812e5Df2A09796481Ff14017e6005508003", decimals: 18 }
|
|
50349
|
-
}
|
|
50350
|
-
},
|
|
50351
|
-
"eip155:137": {
|
|
50352
|
-
rpc: "https://rpc-mainnet.matic.quiknode.pro",
|
|
50353
|
-
native: "matic",
|
|
50354
|
-
nativeDecimals: 18,
|
|
50355
|
-
tokens: {
|
|
50356
|
-
usdc: { address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", decimals: 6 },
|
|
50357
|
-
usdt: { address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", decimals: 6 }
|
|
50358
|
-
}
|
|
50359
|
-
},
|
|
50360
|
-
"eip155:42161": {
|
|
50361
|
-
rpc: "https://arb1.arbitrum.io/rpc",
|
|
50362
|
-
native: "eth",
|
|
50363
|
-
nativeDecimals: 18,
|
|
50364
|
-
tokens: {
|
|
50365
|
-
usdc: { address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", decimals: 6 },
|
|
50366
|
-
usdt: { address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", decimals: 6 }
|
|
50367
|
-
}
|
|
50368
|
-
},
|
|
50369
|
-
"eip155:8453": {
|
|
50370
|
-
rpc: "https://mainnet.base.org",
|
|
50371
|
-
native: "eth",
|
|
50372
|
-
nativeDecimals: 18,
|
|
50373
|
-
tokens: {
|
|
50374
|
-
usdc: { address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", decimals: 6 },
|
|
50375
|
-
dai: { address: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb", decimals: 18 }
|
|
50376
|
-
}
|
|
50377
|
-
},
|
|
50378
|
-
"eip155:10": {
|
|
50379
|
-
rpc: "https://mainnet.optimism.io",
|
|
50380
|
-
native: "eth",
|
|
50381
|
-
nativeDecimals: 18,
|
|
50382
|
-
tokens: {
|
|
50383
|
-
usdc: { address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", decimals: 6 },
|
|
50384
|
-
usdt: { address: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", decimals: 6 }
|
|
50385
|
-
}
|
|
50386
|
-
},
|
|
50387
|
-
"eip155:43114": {
|
|
50388
|
-
rpc: "https://api.avax.network/ext/bc/C/rpc",
|
|
50389
|
-
native: "avax",
|
|
50390
|
-
nativeDecimals: 18,
|
|
50391
|
-
tokens: {
|
|
50392
|
-
usdc: { address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", decimals: 6 },
|
|
50393
|
-
usdt: { address: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", decimals: 6 }
|
|
50394
|
-
}
|
|
50395
|
-
}
|
|
50396
|
-
};
|
|
50397
|
-
var TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
50398
|
-
function parseTokenAmount(amount, decimals) {
|
|
50399
|
-
const [int, frac = ""] = amount.split(".");
|
|
50400
|
-
const fracNorm = frac.slice(0, decimals).padEnd(decimals, "0");
|
|
50401
|
-
return BigInt(int || "0") * 10n ** BigInt(decimals) + BigInt(fracNorm || "0");
|
|
50402
|
-
}
|
|
50403
|
-
async function jsonRpc(rpcUrl, method, params) {
|
|
50404
|
-
const res = await fetch(rpcUrl, {
|
|
50405
|
-
method: "POST",
|
|
50406
|
-
headers: { "Content-Type": "application/json" },
|
|
50407
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
|
|
50408
|
-
});
|
|
50409
|
-
if (!res.ok) throw new Error(`RPC HTTP ${res.status}`);
|
|
50410
|
-
const data = await res.json();
|
|
50411
|
-
if (data.error) throw new Error(data.error.message);
|
|
50412
|
-
return data.result;
|
|
50413
|
-
}
|
|
50414
|
-
async function verifyViaRPC(txHash, config, chainId) {
|
|
50415
|
-
const chain2 = RPC_REGISTRY[chainId];
|
|
50416
|
-
if (!chain2) return { valid: false, error: `No RPC available for chain ${chainId}` };
|
|
50417
|
-
const assetKey = config.asset.toLowerCase();
|
|
50418
|
-
const isNative = assetKey === chain2.native;
|
|
50419
|
-
const tokenInfo = isNative ? null : chain2.tokens[assetKey];
|
|
50420
|
-
if (!isNative && !tokenInfo) {
|
|
50421
|
-
return { valid: false, error: `Unsupported asset "${config.asset}" on chain ${chainId}` };
|
|
50422
|
-
}
|
|
50423
|
-
const decimals = isNative ? chain2.nativeDecimals : tokenInfo.decimals;
|
|
50424
|
-
const expectedAmount = parseTokenAmount(config.amount, decimals);
|
|
50425
|
-
const recipientLower = config.recipient.toLowerCase();
|
|
50426
|
-
let receipt;
|
|
50427
|
-
try {
|
|
50428
|
-
receipt = await jsonRpc(chain2.rpc, "eth_getTransactionReceipt", [txHash]);
|
|
50429
|
-
} catch (err) {
|
|
50430
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
50431
|
-
if (msg === "no response" || msg.toLowerCase().includes("not found")) {
|
|
50432
|
-
return { valid: false, error: "Transaction not found or not yet mined" };
|
|
50433
|
-
}
|
|
50434
|
-
return { valid: false, error: `RPC error: ${msg}` };
|
|
50435
|
-
}
|
|
50436
|
-
if (!receipt) return { valid: false, error: "Transaction not found or not yet mined" };
|
|
50437
|
-
if (receipt.status !== "0x1") return { valid: false, error: "Transaction reverted on-chain" };
|
|
50438
|
-
if (isNative) {
|
|
50439
|
-
const tx = await jsonRpc(chain2.rpc, "eth_getTransactionByHash", [txHash]);
|
|
50440
|
-
if (!tx) return { valid: false, error: "Transaction data not found" };
|
|
50441
|
-
if (config.recipient && tx.to?.toLowerCase() !== recipientLower) {
|
|
50442
|
-
return { valid: false, error: "Payment recipient mismatch" };
|
|
50443
|
-
}
|
|
50444
|
-
if (BigInt(tx.value || "0x0") < expectedAmount) {
|
|
50445
|
-
return { valid: false, error: "Insufficient native payment" };
|
|
50446
|
-
}
|
|
50447
|
-
return { valid: true, method: "rpc" };
|
|
50448
|
-
}
|
|
50449
|
-
const tokenAddr = tokenInfo.address.toLowerCase();
|
|
50450
|
-
const recipientPadded = recipientLower.replace("0x", "").padStart(64, "0");
|
|
50451
|
-
const transferLog = receipt.logs?.find(
|
|
50452
|
-
(log) => log.address.toLowerCase() === tokenAddr && log.topics[0] === TRANSFER_TOPIC && (!config.recipient || log.topics[2]?.toLowerCase() === `0x${recipientPadded}`)
|
|
50453
|
-
);
|
|
50454
|
-
if (!transferLog) return { valid: false, error: "No matching Transfer event found" };
|
|
50455
|
-
if (BigInt(transferLog.data) < expectedAmount) {
|
|
50456
|
-
return { valid: false, error: "Insufficient ERC-20 payment" };
|
|
50457
|
-
}
|
|
50458
|
-
return { valid: true, method: "rpc" };
|
|
50459
|
-
}
|
|
50460
|
-
async function verifyPayment(rawHeader, config, httpClient, seenHashes, blockchainClient) {
|
|
50461
|
-
const header = Array.isArray(rawHeader) ? rawHeader[0] : rawHeader;
|
|
50462
|
-
let proof;
|
|
50463
|
-
try {
|
|
50464
|
-
proof = JSON.parse(Buffer.from(header, "base64").toString("utf8"));
|
|
50465
|
-
} catch {
|
|
50466
|
-
return { valid: false, error: "Invalid payment header: expected base64-encoded JSON" };
|
|
50467
|
-
}
|
|
50468
|
-
if (!proof.txHash || typeof proof.txHash !== "string") {
|
|
50469
|
-
return { valid: false, error: "Missing txHash in payment proof" };
|
|
50470
|
-
}
|
|
50471
|
-
const txHash = proof.txHash;
|
|
50472
|
-
if (await seenHashes.hasSeen(txHash)) {
|
|
50473
|
-
return { valid: false, error: "Transaction hash already used (replay attack detected)" };
|
|
50474
|
-
}
|
|
50475
|
-
if (typeof proof.chainId === "string" && proof.chainId.toLowerCase() !== config.chainId.toLowerCase()) {
|
|
50476
|
-
return {
|
|
50477
|
-
valid: false,
|
|
50478
|
-
error: `Payment chain mismatch \u2014 merchant configured ${config.chainId}, proof claims ${proof.chainId}`
|
|
50479
|
-
};
|
|
50480
|
-
}
|
|
50481
|
-
const chainId = config.chainId.toLowerCase();
|
|
50482
|
-
const chainKey = CAIP2_TO_CHAIN[chainId];
|
|
50483
|
-
if (!chainKey) {
|
|
50484
|
-
return { valid: false, error: `Unsupported chain: ${chainId}` };
|
|
50485
|
-
}
|
|
50486
|
-
const bcChainKey = CAIP2_TO_BC_CHAIN[chainId];
|
|
50487
|
-
if (blockchainClient && bcChainKey) {
|
|
50488
|
-
try {
|
|
50489
|
-
const status = await blockchainClient.findTransactionStatus(
|
|
50490
|
-
bcChainKey,
|
|
50491
|
-
config.recipient || "0x0000000000000000000000000000000000000000",
|
|
50492
|
-
txHash
|
|
50493
|
-
);
|
|
50494
|
-
if (status.failed) {
|
|
50495
|
-
return {
|
|
50496
|
-
valid: false,
|
|
50497
|
-
error: `Transaction reverted on-chain${status.error ? `: ${status.error}` : ""}`
|
|
50498
|
-
};
|
|
50499
|
-
}
|
|
50500
|
-
if (status.confirmed) {
|
|
50501
|
-
const rpcResult2 = await verifyViaRPC(txHash, config, chainId);
|
|
50502
|
-
if (rpcResult2.valid) {
|
|
50503
|
-
await seenHashes.markSeen(txHash, chainId);
|
|
50504
|
-
}
|
|
50505
|
-
return rpcResult2;
|
|
50506
|
-
}
|
|
50507
|
-
} catch {
|
|
50508
|
-
}
|
|
50509
|
-
}
|
|
50510
|
-
const txService = new TransactionService(httpClient);
|
|
50511
|
-
try {
|
|
50512
|
-
const tx = await txService.getByHash(txHash, chainKey);
|
|
50513
|
-
if (!tx) return { valid: false, error: "Transaction not found" };
|
|
50514
|
-
if (tx.status === "pending" || tx.status === "failed") {
|
|
50515
|
-
return { valid: false, error: `Transaction is not confirmed (status: ${tx.status})` };
|
|
50516
|
-
}
|
|
50517
|
-
if (config.recipient && tx.to?.toLowerCase() !== config.recipient.toLowerCase()) {
|
|
50518
|
-
return { valid: false, error: "Payment recipient mismatch" };
|
|
50519
|
-
}
|
|
50520
|
-
const chain2 = RPC_REGISTRY[chainId];
|
|
50521
|
-
const assetKey = config.asset.toLowerCase();
|
|
50522
|
-
const isNative = chain2 && assetKey === chain2.native;
|
|
50523
|
-
const tokenInfo = chain2 && !isNative ? chain2.tokens[assetKey] : void 0;
|
|
50524
|
-
const decimals = isNative ? chain2.nativeDecimals : tokenInfo?.decimals ?? 18;
|
|
50525
|
-
const expectedAtomic = parseTokenAmount(config.amount, decimals);
|
|
50526
|
-
if (BigInt(tx.amount) < expectedAtomic) {
|
|
50527
|
-
return {
|
|
50528
|
-
valid: false,
|
|
50529
|
-
error: `Insufficient payment: received ${tx.amount}, expected ${config.amount}`
|
|
50530
|
-
};
|
|
50531
|
-
}
|
|
50532
|
-
await seenHashes.markSeen(txHash, chainId);
|
|
50533
|
-
return { valid: true, method: "txhub" };
|
|
50534
|
-
} catch (err) {
|
|
50535
|
-
const is403 = err instanceof Error && err.message.includes("403");
|
|
50536
|
-
if (!is403) {
|
|
50537
|
-
return {
|
|
50538
|
-
valid: false,
|
|
50539
|
-
error: `Failed to fetch transaction: ${err instanceof Error ? err.message : String(err)}`
|
|
50540
|
-
};
|
|
50541
|
-
}
|
|
50542
|
-
}
|
|
50543
|
-
const rpcResult = await verifyViaRPC(txHash, config, chainId);
|
|
50544
|
-
if (rpcResult.valid) {
|
|
50545
|
-
await seenHashes.markSeen(txHash, chainId);
|
|
50546
|
-
}
|
|
50547
|
-
return rpcResult;
|
|
50548
|
-
}
|
|
50549
51027
|
var TwakRestServer = class {
|
|
50550
51028
|
registry;
|
|
50551
51029
|
context;
|
|
50552
51030
|
config;
|
|
50553
|
-
seenHashes;
|
|
50554
51031
|
httpServer = null;
|
|
50555
51032
|
constructor(registry, context, config = {}) {
|
|
50556
51033
|
this.registry = registry;
|
|
50557
51034
|
this.context = context;
|
|
50558
|
-
this.seenHashes = new SeenHashStore();
|
|
50559
51035
|
this.config = {
|
|
50560
51036
|
port: config.port ?? 3e3,
|
|
50561
51037
|
host: config.host ?? "127.0.0.1",
|
|
50562
|
-
apiKey: config.apiKey
|
|
50563
|
-
paymentRequired: config.paymentRequired ?? false,
|
|
50564
|
-
paymentAsset: config.paymentAsset ?? "usdc",
|
|
50565
|
-
paymentAmount: config.paymentAmount ?? "0.01",
|
|
50566
|
-
paymentChainId: config.paymentChainId ?? "eip155:8453",
|
|
50567
|
-
paymentRecipient: config.paymentRecipient ?? "",
|
|
50568
|
-
blockchainClient: config.blockchainClient ?? void 0
|
|
51038
|
+
apiKey: config.apiKey
|
|
50569
51039
|
};
|
|
50570
51040
|
}
|
|
50571
51041
|
start() {
|
|
@@ -50589,11 +51059,6 @@ var TwakRestServer = class {
|
|
|
50589
51059
|
} else {
|
|
50590
51060
|
console.error("WARNING: No API key configured \u2014 REST server is unauthenticated");
|
|
50591
51061
|
}
|
|
50592
|
-
if (this.config.paymentRequired) {
|
|
50593
|
-
console.error(
|
|
50594
|
-
`x402 payment required: ${this.config.paymentAmount} ${this.config.paymentAsset} on ${this.config.paymentChainId}`
|
|
50595
|
-
);
|
|
50596
|
-
}
|
|
50597
51062
|
resolve2();
|
|
50598
51063
|
});
|
|
50599
51064
|
});
|
|
@@ -50623,7 +51088,7 @@ var TwakRestServer = class {
|
|
|
50623
51088
|
} catch {
|
|
50624
51089
|
}
|
|
50625
51090
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
50626
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type,
|
|
51091
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
50627
51092
|
if (req.method === "OPTIONS") {
|
|
50628
51093
|
res.writeHead(204);
|
|
50629
51094
|
res.end();
|
|
@@ -50662,52 +51127,6 @@ var TwakRestServer = class {
|
|
|
50662
51127
|
return;
|
|
50663
51128
|
}
|
|
50664
51129
|
const actionName = match[1];
|
|
50665
|
-
if (this.config.paymentRequired) {
|
|
50666
|
-
const paymentHeader = req.headers["x-payment"];
|
|
50667
|
-
if (!paymentHeader) {
|
|
50668
|
-
res.setHeader(
|
|
50669
|
-
"WWW-Authenticate",
|
|
50670
|
-
`x402 realm="twak", asset="${this.config.paymentAsset}", amount="${this.config.paymentAmount}", chain="${this.config.paymentChainId}"`
|
|
50671
|
-
);
|
|
50672
|
-
res.writeHead(402);
|
|
50673
|
-
res.end(
|
|
50674
|
-
JSON.stringify({
|
|
50675
|
-
error: "payment_required",
|
|
50676
|
-
message: `This endpoint requires payment of ${this.config.paymentAmount} ${this.config.paymentAsset}`,
|
|
50677
|
-
asset: this.config.paymentAsset,
|
|
50678
|
-
amount: this.config.paymentAmount,
|
|
50679
|
-
chainId: this.config.paymentChainId,
|
|
50680
|
-
recipient: this.config.paymentRecipient || void 0
|
|
50681
|
-
})
|
|
50682
|
-
);
|
|
50683
|
-
return;
|
|
50684
|
-
}
|
|
50685
|
-
if (this.config.paymentRecipient) {
|
|
50686
|
-
const result = await verifyPayment(
|
|
50687
|
-
paymentHeader,
|
|
50688
|
-
{
|
|
50689
|
-
asset: this.config.paymentAsset,
|
|
50690
|
-
amount: this.config.paymentAmount,
|
|
50691
|
-
chainId: this.config.paymentChainId,
|
|
50692
|
-
recipient: this.config.paymentRecipient
|
|
50693
|
-
},
|
|
50694
|
-
this.context.httpClient,
|
|
50695
|
-
this.seenHashes,
|
|
50696
|
-
this.config.blockchainClient
|
|
50697
|
-
);
|
|
50698
|
-
if (!result.valid) {
|
|
50699
|
-
res.writeHead(402);
|
|
50700
|
-
res.end(
|
|
50701
|
-
JSON.stringify({
|
|
50702
|
-
error: "payment_verification_failed",
|
|
50703
|
-
message: result.error ?? "Payment could not be verified"
|
|
50704
|
-
})
|
|
50705
|
-
);
|
|
50706
|
-
return;
|
|
50707
|
-
}
|
|
50708
|
-
}
|
|
50709
|
-
console.error(`[x402] Payment verified for ${actionName}`);
|
|
50710
|
-
}
|
|
50711
51130
|
const action = this.registry.getAction(actionName);
|
|
50712
51131
|
if (!action) {
|
|
50713
51132
|
res.writeHead(404);
|
|
@@ -50747,14 +51166,6 @@ function registerServeCommand(program3) {
|
|
|
50747
51166
|
program3.command("serve").description("Start MCP server (stdio) or REST API server for AI agent connection").option("--rest", "Start REST HTTP server instead of MCP stdio server").option("--port <n>", "Port for REST server", "3000").option(
|
|
50748
51167
|
"--host <host>",
|
|
50749
51168
|
"Host to bind REST server (default: 127.0.0.1; use 0.0.0.0 to expose externally)"
|
|
50750
|
-
).option("--x402", "Require x402 micropayment for REST endpoints").option("--payment-amount <n>", "Payment amount required per request", "0.01").option("--payment-asset <asset>", "Payment asset (usdc, eth, twt, bnb, matic, ...)", "usdc").option(
|
|
50751
|
-
"--payment-chain <chainId>",
|
|
50752
|
-
"Payment chain ID (e.g. eip155:56 for BSC/TWT, eip155:8453 for Base)",
|
|
50753
|
-
"eip155:8453"
|
|
50754
|
-
).option(
|
|
50755
|
-
"--payment-recipient <address>",
|
|
50756
|
-
"Address to receive payments (required for on-chain verification)",
|
|
50757
|
-
""
|
|
50758
51169
|
).option("--password <password>", "Wallet password (falls back to keychain)").option("--auto-lock <minutes>", "Auto-lock wallet after N minutes of inactivity").option("--wc-project-id <id>", "WalletConnect project ID (or set WALLETCONNECT_PROJECT_ID)").action(async (opts) => {
|
|
50759
51170
|
await ensureTrustSDK();
|
|
50760
51171
|
const client = await getClient();
|
|
@@ -50791,6 +51202,7 @@ function registerServeCommand(program3) {
|
|
|
50791
51202
|
const walletContext = new WalletContext();
|
|
50792
51203
|
if (agentWallet) {
|
|
50793
51204
|
await walletContext.bindLocal(agentWallet);
|
|
51205
|
+
blockchainClient.setSigner(agentWallet.getSigner());
|
|
50794
51206
|
}
|
|
50795
51207
|
const registry = new ActionRegistry();
|
|
50796
51208
|
registry.register(new BalanceActionProvider());
|
|
@@ -50800,12 +51212,16 @@ function registerServeCommand(program3) {
|
|
|
50800
51212
|
registry.register(new SwapActionProvider(swapClient, walletContext, blockchainClient));
|
|
50801
51213
|
registry.register(new SecurityActionProvider());
|
|
50802
51214
|
registry.register(new HistoryActionProvider());
|
|
50803
|
-
registry.register(
|
|
51215
|
+
registry.register(
|
|
51216
|
+
new TransferActionProvider(walletContext, swapClient, void 0, blockchainClient)
|
|
51217
|
+
);
|
|
50804
51218
|
registry.register(new AlertActionProvider(alertStore, swapClient));
|
|
50805
51219
|
registry.register(new AgentWalletActionProvider(blockchainClient, walletContext));
|
|
50806
51220
|
registry.register(new TokenActionProvider(blockchainClient, walletContext));
|
|
50807
51221
|
registry.register(new MarketActionProvider());
|
|
50808
|
-
registry.register(
|
|
51222
|
+
registry.register(
|
|
51223
|
+
new OnrampActionProvider(walletContext, swapClient, void 0, blockchainClient)
|
|
51224
|
+
);
|
|
50809
51225
|
registry.register(
|
|
50810
51226
|
new AutomateActionProvider(automationStore, swapClient, walletContext, blockchainClient)
|
|
50811
51227
|
);
|
|
@@ -50817,13 +51233,7 @@ function registerServeCommand(program3) {
|
|
|
50817
51233
|
const restServer = new TwakRestServer(registry, context, {
|
|
50818
51234
|
port: parseInt(opts.port),
|
|
50819
51235
|
host: opts.host,
|
|
50820
|
-
apiKey: client.hmacSecret
|
|
50821
|
-
paymentRequired: opts.x402,
|
|
50822
|
-
paymentAsset: opts.paymentAsset,
|
|
50823
|
-
paymentAmount: opts.paymentAmount,
|
|
50824
|
-
paymentChainId: opts.paymentChain,
|
|
50825
|
-
paymentRecipient: opts.paymentRecipient,
|
|
50826
|
-
blockchainClient
|
|
51236
|
+
apiKey: client.hmacSecret
|
|
50827
51237
|
});
|
|
50828
51238
|
registerShutdownHandlers([agentWallet], async () => {
|
|
50829
51239
|
await walletContext.reset();
|
|
@@ -51240,7 +51650,7 @@ ${url}`);
|
|
|
51240
51650
|
} catch (err) {
|
|
51241
51651
|
mapBroadcastError(err);
|
|
51242
51652
|
}
|
|
51243
|
-
const explorerUrl = getExplorerTxUrl(result.chainKey, result.
|
|
51653
|
+
const explorerUrl = getExplorerTxUrl(result.chainKey, result.txHash);
|
|
51244
51654
|
success("Off-ramp payout broadcast!");
|
|
51245
51655
|
if (!opts.json) {
|
|
51246
51656
|
info(
|
|
@@ -51249,7 +51659,7 @@ ${url}`);
|
|
|
51249
51659
|
}
|
|
51250
51660
|
output(
|
|
51251
51661
|
{
|
|
51252
|
-
hash: result.
|
|
51662
|
+
hash: result.txHash,
|
|
51253
51663
|
chain: result.chainKey,
|
|
51254
51664
|
from: address,
|
|
51255
51665
|
to: opts.to,
|
|
@@ -51751,7 +52161,7 @@ var crypto7 = __toESM(require("crypto"));
|
|
|
51751
52161
|
// package.json
|
|
51752
52162
|
var package_default = {
|
|
51753
52163
|
name: "@trustwallet/cli",
|
|
51754
|
-
version: "0.
|
|
52164
|
+
version: "0.15.0",
|
|
51755
52165
|
description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
|
|
51756
52166
|
main: "dist/index.js",
|
|
51757
52167
|
types: "dist/index.d.ts",
|
|
@@ -51786,7 +52196,7 @@ var package_default = {
|
|
|
51786
52196
|
},
|
|
51787
52197
|
devDependencies: {
|
|
51788
52198
|
"@twak/actions": "workspace:*",
|
|
51789
|
-
"@twak/
|
|
52199
|
+
"@twak/contracts": "workspace:*",
|
|
51790
52200
|
"@twak/core": "workspace:*",
|
|
51791
52201
|
"@twak/mcp": "workspace:*",
|
|
51792
52202
|
"@twak/security": "workspace:*",
|
|
@@ -51919,7 +52329,7 @@ function registerTelemetryCommand(program3) {
|
|
|
51919
52329
|
// src/commands/watch.ts
|
|
51920
52330
|
init_dist();
|
|
51921
52331
|
init_dist2();
|
|
51922
|
-
|
|
52332
|
+
init_dist3();
|
|
51923
52333
|
function ts3() {
|
|
51924
52334
|
return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
51925
52335
|
}
|