@trustwallet/cli 0.13.0 → 0.14.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/serve.d.ts.map +1 -1
- package/dist/commands/x402.d.ts.map +1 -1
- package/dist/index.js +855 -396
- package/package.json +13 -13
package/dist/index.js
CHANGED
|
@@ -3124,6 +3124,7 @@ __export(dist_exports, {
|
|
|
3124
3124
|
CommandError: () => CommandError,
|
|
3125
3125
|
ERC20_ERROR_CODES: () => ERC20_ERROR_CODES,
|
|
3126
3126
|
Erc20Error: () => Erc20Error,
|
|
3127
|
+
McpResponse: () => McpResponse,
|
|
3127
3128
|
NATIVE_TOKEN_ADDRESS: () => NATIVE_TOKEN_ADDRESS,
|
|
3128
3129
|
QUERY_ERROR_CODES: () => QUERY_ERROR_CODES,
|
|
3129
3130
|
QueryError: () => QueryError,
|
|
@@ -3613,7 +3614,7 @@ async function fetchBalance(httpClient, assetId, address, currency = "USD") {
|
|
|
3613
3614
|
throw new BalanceError("NETWORK_ERROR", "Could not fetch balance. Please try again later.");
|
|
3614
3615
|
}
|
|
3615
3616
|
}
|
|
3616
|
-
var import_crypto, NATIVE_TOKEN_ADDRESS, SOLANA_NATIVE_ADDRESS, CHAINS, COMMON_TOKENS, BSC_18_DECIMAL_TOKENS, NATIVE_SYMBOLS, TOKEN_DECIMALS, 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, DECIMAL_AMOUNT_RE, UNSAFE_KEYS, NAMING_API, DEFAULT_GATEWAY_URL, DEFAULT_MARKET_URL, DEFAULT_TIMEOUT, DEFAULT_USER_AGENT, DEFAULT_X_ORIGIN, TwakHttpClient, ALLOWED_OFFRAMP_PROVIDERS;
|
|
3617
|
+
var import_crypto, NATIVE_TOKEN_ADDRESS, SOLANA_NATIVE_ADDRESS, CHAINS, COMMON_TOKENS, BSC_18_DECIMAL_TOKENS, NATIVE_SYMBOLS, TOKEN_DECIMALS, 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, DECIMAL_AMOUNT_RE, UNSAFE_KEYS, NAMING_API, DEFAULT_GATEWAY_URL, DEFAULT_MARKET_URL, DEFAULT_TIMEOUT, DEFAULT_USER_AGENT, DEFAULT_X_ORIGIN, TwakHttpClient, ALLOWED_OFFRAMP_PROVIDERS, McpResponse;
|
|
3617
3618
|
var init_dist = __esm({
|
|
3618
3619
|
"../core/dist/index.mjs"() {
|
|
3619
3620
|
"use strict";
|
|
@@ -4182,7 +4183,11 @@ var init_dist = __esm({
|
|
|
4182
4183
|
// Raised by WalletConnect provider methods when the session isn't yet
|
|
4183
4184
|
// established (user hasn't scanned/approved) or has been dropped/expired.
|
|
4184
4185
|
// Actionable: agent prompts the user to complete pairing, then retry.
|
|
4185
|
-
WC_NOT_CONNECTED: "WC_NOT_CONNECTED"
|
|
4186
|
+
WC_NOT_CONNECTED: "WC_NOT_CONNECTED",
|
|
4187
|
+
// Raised by signing-path action providers when no wallet mode has been
|
|
4188
|
+
// selected yet for this server lifetime. Actionable: agent calls
|
|
4189
|
+
// switch_wallet_mode (local or walletconnect) and retries.
|
|
4190
|
+
WALLET_NOT_BOUND: "WALLET_NOT_BOUND"
|
|
4186
4191
|
};
|
|
4187
4192
|
WalletError = class extends CommandError {
|
|
4188
4193
|
constructor(code2, message) {
|
|
@@ -4373,6 +4378,11 @@ var init_dist = __esm({
|
|
|
4373
4378
|
}
|
|
4374
4379
|
};
|
|
4375
4380
|
ALLOWED_OFFRAMP_PROVIDERS = /* @__PURE__ */ new Set(["MoonPay"]);
|
|
4381
|
+
McpResponse = class {
|
|
4382
|
+
constructor(content) {
|
|
4383
|
+
this.content = content;
|
|
4384
|
+
}
|
|
4385
|
+
};
|
|
4376
4386
|
}
|
|
4377
4387
|
});
|
|
4378
4388
|
|
|
@@ -6880,7 +6890,7 @@ function registerShutdownHandlers(providers, onBeforeExit) {
|
|
|
6880
6890
|
process.removeListener("SIGTERM", handler);
|
|
6881
6891
|
};
|
|
6882
6892
|
}
|
|
6883
|
-
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, SHUTDOWN_TIMEOUT_MS;
|
|
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;
|
|
6884
6894
|
var init_dist3 = __esm({
|
|
6885
6895
|
"../wallet-core/dist/index.mjs"() {
|
|
6886
6896
|
"use strict";
|
|
@@ -6896,6 +6906,7 @@ var init_dist3 = __esm({
|
|
|
6896
6906
|
import_crypto2 = require("crypto");
|
|
6897
6907
|
init_dist();
|
|
6898
6908
|
init_dist2();
|
|
6909
|
+
init_dist();
|
|
6899
6910
|
__require = /* @__PURE__ */ ((x6) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x6, {
|
|
6900
6911
|
get: (a3, b3) => (typeof require !== "undefined" ? require : a3)[b3]
|
|
6901
6912
|
}) : x6)(function(x6) {
|
|
@@ -7916,10 +7927,33 @@ var init_dist3 = __esm({
|
|
|
7916
7927
|
}
|
|
7917
7928
|
}
|
|
7918
7929
|
}
|
|
7930
|
+
async signTypedData(chainKey, chainId, typedDataJson) {
|
|
7931
|
+
await this.ensureConnected();
|
|
7932
|
+
this.resetAutoLockTimer();
|
|
7933
|
+
return this.blockchainClient.signTypedData({
|
|
7934
|
+
chainKey,
|
|
7935
|
+
chainId,
|
|
7936
|
+
typedDataJson,
|
|
7937
|
+
hdWallet: this.signer.getHDWallet()
|
|
7938
|
+
});
|
|
7939
|
+
}
|
|
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
|
+
}
|
|
7919
7953
|
async sendTransaction(params) {
|
|
7920
7954
|
await this.ensureConnected();
|
|
7921
7955
|
this.resetAutoLockTimer();
|
|
7922
|
-
const { getChainByEvmId: getChainByEvmId2, chainKeyToCoinType:
|
|
7956
|
+
const { getChainByEvmId: getChainByEvmId2, chainKeyToCoinType: chainKeyToCoinType4 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
7923
7957
|
let assetId;
|
|
7924
7958
|
if (params.chainId.startsWith("eip155:")) {
|
|
7925
7959
|
const chain2 = getChainByEvmId2(params.chainId.replace("eip155:", ""));
|
|
@@ -7928,7 +7962,7 @@ var init_dist3 = __esm({
|
|
|
7928
7962
|
} else if (params.chainId.startsWith("solana:")) {
|
|
7929
7963
|
assetId = "c501";
|
|
7930
7964
|
} else {
|
|
7931
|
-
const coinId =
|
|
7965
|
+
const coinId = chainKeyToCoinType4(params.chainId);
|
|
7932
7966
|
if (coinId === void 0) throw new Error(`Unsupported chain: ${params.chainId}`);
|
|
7933
7967
|
assetId = `c${coinId}`;
|
|
7934
7968
|
}
|
|
@@ -7988,14 +8022,206 @@ var init_dist3 = __esm({
|
|
|
7988
8022
|
return this.connected;
|
|
7989
8023
|
}
|
|
7990
8024
|
getBlockchainClient() {
|
|
7991
|
-
if (!this.connected)
|
|
8025
|
+
if (!this.connected)
|
|
8026
|
+
throw new Error("Wallet is locked. Use an async method to trigger re-auth.");
|
|
7992
8027
|
return this.blockchainClient;
|
|
7993
8028
|
}
|
|
7994
8029
|
getSigner() {
|
|
7995
|
-
if (!this.connected)
|
|
8030
|
+
if (!this.connected)
|
|
8031
|
+
throw new Error("Wallet is locked. Use an async method to trigger re-auth.");
|
|
7996
8032
|
return this.signer;
|
|
7997
8033
|
}
|
|
7998
8034
|
};
|
|
8035
|
+
WalletContext = class {
|
|
8036
|
+
_state = "unbound";
|
|
8037
|
+
_wallet;
|
|
8038
|
+
/**
|
|
8039
|
+
* The WC instance we're currently pairing with. Used to ignore callbacks
|
|
8040
|
+
* from a previous pairing if the user re-calls `beginWalletConnect` while
|
|
8041
|
+
* already in `wc-pairing` (e.g. the first QR went stale and they want a
|
|
8042
|
+
* fresh one). Without this guard, the first wc's session-established
|
|
8043
|
+
* callback could land later and overwrite `_wallet` with the wrong instance.
|
|
8044
|
+
*/
|
|
8045
|
+
_pendingWc;
|
|
8046
|
+
_pairing;
|
|
8047
|
+
_peerName;
|
|
8048
|
+
_lastError;
|
|
8049
|
+
/**
|
|
8050
|
+
* Mode-switch serializer. Concurrent `bindLocal` / `beginWalletConnect` /
|
|
8051
|
+
* `reset` calls otherwise interleave their state writes — `_pendingWc`
|
|
8052
|
+
* identity guards prevent signing-against-wrong-wallet, but `_state` and
|
|
8053
|
+
* `_pairing` can land in an inconsistent shape. We queue each entry so the
|
|
8054
|
+
* second call waits for the first to settle. `.catch(()=>{})` on the field
|
|
8055
|
+
* itself keeps a rejected switch from poisoning the queue for later calls
|
|
8056
|
+
* (the caller still sees the original rejection via the awaited promise
|
|
8057
|
+
* we return from each entry method).
|
|
8058
|
+
*/
|
|
8059
|
+
_switchInflight = Promise.resolve();
|
|
8060
|
+
/**
|
|
8061
|
+
* Disconnect any in-flight or established WC session. Used internally when
|
|
8062
|
+
* switching modes and externally by `reset()`. Best-effort — disconnect
|
|
8063
|
+
* failures are swallowed (state still progresses to a clean slot).
|
|
8064
|
+
*
|
|
8065
|
+
* Covers both `wc-connected` (via `_wallet`) and `wc-pairing` (via
|
|
8066
|
+
* `_pendingWc`). Without the pending-wc disconnect, switching away from
|
|
8067
|
+
* `wc-pairing` would leave the relay client open until expiry.
|
|
8068
|
+
*/
|
|
8069
|
+
async disconnectWcIfAny() {
|
|
8070
|
+
const targets = [];
|
|
8071
|
+
if (this._wallet && this._wallet.name === "walletconnect") {
|
|
8072
|
+
targets.push(this._wallet);
|
|
8073
|
+
}
|
|
8074
|
+
if (this._pendingWc && this._pendingWc !== this._wallet) {
|
|
8075
|
+
targets.push(this._pendingWc);
|
|
8076
|
+
}
|
|
8077
|
+
for (const t of targets) {
|
|
8078
|
+
try {
|
|
8079
|
+
await t.disconnect();
|
|
8080
|
+
} catch {
|
|
8081
|
+
}
|
|
8082
|
+
}
|
|
8083
|
+
}
|
|
8084
|
+
get state() {
|
|
8085
|
+
return this._state;
|
|
8086
|
+
}
|
|
8087
|
+
/**
|
|
8088
|
+
* Returns the bound wallet, or throws WALLET_NOT_BOUND. All signing-path
|
|
8089
|
+
* action providers (transfer/swap/x402/etc.) should call this at the top
|
|
8090
|
+
* of each action method.
|
|
8091
|
+
*/
|
|
8092
|
+
activeWallet() {
|
|
8093
|
+
if (this._state !== "local" && this._state !== "wc-connected") {
|
|
8094
|
+
throw new WalletError(
|
|
8095
|
+
"WALLET_NOT_BOUND",
|
|
8096
|
+
"No wallet mode selected. Ask the user whether to use the local agent wallet or pair via WalletConnect, then call switch_wallet_mode."
|
|
8097
|
+
);
|
|
8098
|
+
}
|
|
8099
|
+
return this._wallet;
|
|
8100
|
+
}
|
|
8101
|
+
/** Snapshot for get_wallet_status MCP action and CLI introspection. */
|
|
8102
|
+
snapshot() {
|
|
8103
|
+
const base3 = {
|
|
8104
|
+
state: this._state,
|
|
8105
|
+
// Spread-copy so consumers can't mutate our internal _lastError object.
|
|
8106
|
+
lastError: this._lastError ? { ...this._lastError } : void 0
|
|
8107
|
+
};
|
|
8108
|
+
if (this._wallet) {
|
|
8109
|
+
base3.walletType = this._wallet.name === "walletconnect" ? "walletconnect" : "agent";
|
|
8110
|
+
}
|
|
8111
|
+
if (this._pairing) {
|
|
8112
|
+
base3.pairing = {
|
|
8113
|
+
uri: this._pairing.uri,
|
|
8114
|
+
deepLink: this._pairing.deepLinks.universal,
|
|
8115
|
+
qrCodeImageUrl: this._pairing.qrCode.imageUrl,
|
|
8116
|
+
expiresAt: this._pairing.expiresAt
|
|
8117
|
+
};
|
|
8118
|
+
}
|
|
8119
|
+
if (this._peerName) base3.peerName = this._peerName;
|
|
8120
|
+
return base3;
|
|
8121
|
+
}
|
|
8122
|
+
/**
|
|
8123
|
+
* Bind to the local agent wallet. Idempotent for the same provider; when
|
|
8124
|
+
* called while bound to WC (`wc-connected` or `wc-pairing`), the WC session
|
|
8125
|
+
* is disconnected first and any in-flight pairing is invalidated so its
|
|
8126
|
+
* later session-established callback can't reclaim state.
|
|
8127
|
+
*/
|
|
8128
|
+
async bindLocal(agent) {
|
|
8129
|
+
return this.enqueue(() => this.bindLocalImpl(agent));
|
|
8130
|
+
}
|
|
8131
|
+
async bindLocalImpl(agent) {
|
|
8132
|
+
await this.disconnectWcIfAny();
|
|
8133
|
+
this._pendingWc = void 0;
|
|
8134
|
+
this._wallet = agent;
|
|
8135
|
+
this._pairing = void 0;
|
|
8136
|
+
this._peerName = void 0;
|
|
8137
|
+
this._state = "local";
|
|
8138
|
+
this._lastError = void 0;
|
|
8139
|
+
}
|
|
8140
|
+
/**
|
|
8141
|
+
* Run `task` after any in-flight mode switch finishes. The returned promise
|
|
8142
|
+
* settles with `task`'s outcome (success or failure). The internal queue
|
|
8143
|
+
* promise always resolves so a failing switch can't block subsequent
|
|
8144
|
+
* switches.
|
|
8145
|
+
*/
|
|
8146
|
+
enqueue(task) {
|
|
8147
|
+
const next = this._switchInflight.then(task);
|
|
8148
|
+
this._switchInflight = next.catch(() => void 0);
|
|
8149
|
+
return next;
|
|
8150
|
+
}
|
|
8151
|
+
/**
|
|
8152
|
+
* Start a WalletConnect pairing. Returns the pairing payload immediately;
|
|
8153
|
+
* the caller surfaces it (QR code, deep link). When any compatible client
|
|
8154
|
+
* approves, the provider's session_established event fires and the context
|
|
8155
|
+
* transitions to `wc-connected`.
|
|
8156
|
+
*
|
|
8157
|
+
* V1 does not restrict the peer wallet — any WC v2 client can pair. The
|
|
8158
|
+
* user's choice to scan the QR is the consent moment. A peer-identity gate
|
|
8159
|
+
* (e.g. Trust-Wallet-only) is deferred until we have a real pairing log to
|
|
8160
|
+
* write it against.
|
|
8161
|
+
*
|
|
8162
|
+
* `wc` is opaque here — we don't import the WalletConnect package directly
|
|
8163
|
+
* to avoid forcing wallet-core to depend on it. The caller (typically the
|
|
8164
|
+
* MCP action provider) constructs the WC provider and passes it in.
|
|
8165
|
+
*/
|
|
8166
|
+
async beginWalletConnect(wc2, chains) {
|
|
8167
|
+
return this.enqueue(() => this.beginWalletConnectImpl(wc2, chains));
|
|
8168
|
+
}
|
|
8169
|
+
async beginWalletConnectImpl(wc2, chains) {
|
|
8170
|
+
await this.disconnectWcIfAny();
|
|
8171
|
+
try {
|
|
8172
|
+
this._pairing = await wc2.connect(chains);
|
|
8173
|
+
} catch (err) {
|
|
8174
|
+
this._wallet = void 0;
|
|
8175
|
+
this._pendingWc = void 0;
|
|
8176
|
+
this._pairing = void 0;
|
|
8177
|
+
this._peerName = void 0;
|
|
8178
|
+
this._state = "unbound";
|
|
8179
|
+
this._lastError = {
|
|
8180
|
+
code: "WC_PAIRING_FAILED",
|
|
8181
|
+
message: `WalletConnect pairing failed: ${err instanceof Error ? err.message : String(err)}`
|
|
8182
|
+
};
|
|
8183
|
+
throw err;
|
|
8184
|
+
}
|
|
8185
|
+
this._wallet = void 0;
|
|
8186
|
+
this._pendingWc = wc2;
|
|
8187
|
+
this._state = "wc-pairing";
|
|
8188
|
+
this._lastError = void 0;
|
|
8189
|
+
this._peerName = void 0;
|
|
8190
|
+
wc2.onSessionEstablished((session) => {
|
|
8191
|
+
if (this._pendingWc !== wc2) return;
|
|
8192
|
+
this._wallet = wc2;
|
|
8193
|
+
this._peerName = session.peerName;
|
|
8194
|
+
this._state = "wc-connected";
|
|
8195
|
+
this._pendingWc = void 0;
|
|
8196
|
+
this._pairing = void 0;
|
|
8197
|
+
});
|
|
8198
|
+
wc2.onSessionEnded((reason) => {
|
|
8199
|
+
if (this._wallet !== wc2) return;
|
|
8200
|
+
this._wallet = void 0;
|
|
8201
|
+
this._pairing = void 0;
|
|
8202
|
+
this._peerName = void 0;
|
|
8203
|
+
this._state = "unbound";
|
|
8204
|
+
this._lastError = {
|
|
8205
|
+
code: "WC_NOT_CONNECTED",
|
|
8206
|
+
message: `WalletConnect session ended (${reason}). Call switch_wallet_mode to re-pair or switch to local.`
|
|
8207
|
+
};
|
|
8208
|
+
});
|
|
8209
|
+
return this.snapshot().pairing;
|
|
8210
|
+
}
|
|
8211
|
+
/** Disconnect the active wallet (best-effort) and return to `unbound`. */
|
|
8212
|
+
async reset() {
|
|
8213
|
+
return this.enqueue(() => this.resetImpl());
|
|
8214
|
+
}
|
|
8215
|
+
async resetImpl() {
|
|
8216
|
+
await this.disconnectWcIfAny();
|
|
8217
|
+
this._wallet = void 0;
|
|
8218
|
+
this._pendingWc = void 0;
|
|
8219
|
+
this._pairing = void 0;
|
|
8220
|
+
this._peerName = void 0;
|
|
8221
|
+
this._state = "unbound";
|
|
8222
|
+
this._lastError = void 0;
|
|
8223
|
+
}
|
|
8224
|
+
};
|
|
7999
8225
|
SHUTDOWN_TIMEOUT_MS = 3e3;
|
|
8000
8226
|
}
|
|
8001
8227
|
});
|
|
@@ -38734,10 +38960,10 @@ async function renderPngDataUrl(uri) {
|
|
|
38734
38960
|
var DEFAULT_METADATA = {
|
|
38735
38961
|
name: "TWAK Agent",
|
|
38736
38962
|
description: "AI Agent for Trust Wallet operations",
|
|
38737
|
-
url: "https://
|
|
38963
|
+
url: "https://portal.trustwallet.com",
|
|
38738
38964
|
icons: ["https://avatars.githubusercontent.com/u/32179365"]
|
|
38739
38965
|
};
|
|
38740
|
-
var WC_NOT_CONNECTED_MSG =
|
|
38966
|
+
var WC_NOT_CONNECTED_MSG = 'WalletConnect session not established. If pairing is in progress, complete the approval in Trust Wallet mobile and retry; otherwise call switch_wallet_mode({mode: "walletconnect"}) to start pairing.';
|
|
38741
38967
|
var WalletConnectProvider = class extends WalletProvider {
|
|
38742
38968
|
name = "walletconnect";
|
|
38743
38969
|
client = null;
|
|
@@ -38745,6 +38971,7 @@ var WalletConnectProvider = class extends WalletProvider {
|
|
|
38745
38971
|
currentSession = null;
|
|
38746
38972
|
config;
|
|
38747
38973
|
sessionCallback = null;
|
|
38974
|
+
sessionEndedCallback = null;
|
|
38748
38975
|
pairingEventCallback = null;
|
|
38749
38976
|
constructor(config) {
|
|
38750
38977
|
super();
|
|
@@ -38769,12 +38996,13 @@ var WalletConnectProvider = class extends WalletProvider {
|
|
|
38769
38996
|
const emit = (event, data) => {
|
|
38770
38997
|
if (this.pairingEventCallback) this.pairingEventCallback(event, data);
|
|
38771
38998
|
};
|
|
38772
|
-
|
|
38773
|
-
if (this.currentSession?.topic
|
|
38774
|
-
|
|
38775
|
-
|
|
38776
|
-
|
|
38777
|
-
});
|
|
38999
|
+
const handleSessionEnded = (topic, reason) => {
|
|
39000
|
+
if (this.currentSession?.topic !== topic) return;
|
|
39001
|
+
this.currentSession = null;
|
|
39002
|
+
if (this.sessionEndedCallback) this.sessionEndedCallback(reason);
|
|
39003
|
+
};
|
|
39004
|
+
client.on("session_delete", ({ topic }) => handleSessionEnded(topic, "deleted"));
|
|
39005
|
+
client.on("session_expire", ({ topic }) => handleSessionEnded(topic, "expired"));
|
|
38778
39006
|
client.on("session_update", () => {
|
|
38779
39007
|
if (this.sessionCallback) {
|
|
38780
39008
|
const session = this.getActiveSession();
|
|
@@ -38792,6 +39020,15 @@ var WalletConnectProvider = class extends WalletProvider {
|
|
|
38792
39020
|
onSessionEstablished(callback) {
|
|
38793
39021
|
this.sessionCallback = callback;
|
|
38794
39022
|
}
|
|
39023
|
+
/**
|
|
39024
|
+
* Register a callback for when the WC session ends (peer wallet revoked,
|
|
39025
|
+
* relay-side expiry, etc.). WalletContext uses this to flip back to
|
|
39026
|
+
* `unbound` so subsequent signing surfaces `WALLET_NOT_BOUND` rather than
|
|
39027
|
+
* a deep WC-RPC failure.
|
|
39028
|
+
*/
|
|
39029
|
+
onSessionEnded(callback) {
|
|
39030
|
+
this.sessionEndedCallback = callback;
|
|
39031
|
+
}
|
|
38795
39032
|
/**
|
|
38796
39033
|
* Returns the WC account to use as the `from` field for a signing request
|
|
38797
39034
|
* on `chainId`. EVM-only: throws `CHAIN_UNSUPPORTED` if `chainId` isn't in
|
|
@@ -38846,6 +39083,36 @@ var WalletConnectProvider = class extends WalletProvider {
|
|
|
38846
39083
|
}
|
|
38847
39084
|
});
|
|
38848
39085
|
}
|
|
39086
|
+
async signTypedData(chainKey, chainId, typedDataJson) {
|
|
39087
|
+
const session = this.getActiveSession();
|
|
39088
|
+
if (!session) throw new WalletError("WC_NOT_CONNECTED", WC_NOT_CONNECTED_MSG);
|
|
39089
|
+
const client = await this.ensureClient();
|
|
39090
|
+
const caipChainId = chainKey.includes(":") ? chainKey : `eip155:${chainId}`;
|
|
39091
|
+
return client.request({
|
|
39092
|
+
topic: session.topic,
|
|
39093
|
+
chainId: caipChainId,
|
|
39094
|
+
request: {
|
|
39095
|
+
method: "eth_signTypedData_v4",
|
|
39096
|
+
params: [this.resolveFromAddress(caipChainId), typedDataJson]
|
|
39097
|
+
}
|
|
39098
|
+
});
|
|
39099
|
+
}
|
|
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
|
+
}
|
|
38849
39116
|
async sendTransaction(params) {
|
|
38850
39117
|
const session = this.getActiveSession();
|
|
38851
39118
|
if (!session) throw new WalletError("WC_NOT_CONNECTED", WC_NOT_CONNECTED_MSG);
|
|
@@ -42665,29 +42932,30 @@ function registerQueryCommand(program3) {
|
|
|
42665
42932
|
}
|
|
42666
42933
|
|
|
42667
42934
|
// ../actions/dist/index.mjs
|
|
42935
|
+
init_dist();
|
|
42668
42936
|
var import_zod3 = require("zod");
|
|
42669
42937
|
init_dist();
|
|
42670
42938
|
var import_zod4 = require("zod");
|
|
42939
|
+
init_dist();
|
|
42671
42940
|
var import_zod5 = require("zod");
|
|
42672
42941
|
init_dist();
|
|
42673
42942
|
var import_zod6 = require("zod");
|
|
42674
|
-
init_dist();
|
|
42675
42943
|
var import_zod7 = require("zod");
|
|
42944
|
+
init_dist();
|
|
42676
42945
|
var import_zod8 = require("zod");
|
|
42677
42946
|
init_dist();
|
|
42678
42947
|
var import_zod9 = require("zod");
|
|
42679
42948
|
init_dist();
|
|
42680
42949
|
var import_zod10 = require("zod");
|
|
42950
|
+
init_dist3();
|
|
42681
42951
|
init_dist();
|
|
42682
42952
|
var import_zod11 = require("zod");
|
|
42683
|
-
init_dist();
|
|
42684
42953
|
var import_zod12 = require("zod");
|
|
42954
|
+
init_dist();
|
|
42685
42955
|
var import_zod13 = require("zod");
|
|
42686
42956
|
init_dist();
|
|
42687
42957
|
var import_zod14 = require("zod");
|
|
42688
42958
|
init_dist();
|
|
42689
|
-
var import_zod15 = require("zod");
|
|
42690
|
-
init_dist();
|
|
42691
42959
|
|
|
42692
42960
|
// ../x402/dist/index.mjs
|
|
42693
42961
|
init_dist();
|
|
@@ -42911,12 +43179,11 @@ async function signTransferWithAuthorization(p3) {
|
|
|
42911
43179
|
domain: p3.token,
|
|
42912
43180
|
message: authorization
|
|
42913
43181
|
});
|
|
42914
|
-
const rawSignature = await p3.
|
|
42915
|
-
|
|
42916
|
-
|
|
42917
|
-
|
|
42918
|
-
|
|
42919
|
-
});
|
|
43182
|
+
const rawSignature = await p3.walletProvider.signTypedData(
|
|
43183
|
+
p3.chainKey,
|
|
43184
|
+
p3.token.chainId,
|
|
43185
|
+
JSON.stringify(typedData)
|
|
43186
|
+
);
|
|
42920
43187
|
const signature = normalizeEip712Signature(rawSignature);
|
|
42921
43188
|
return { signature, authorization };
|
|
42922
43189
|
}
|
|
@@ -42981,12 +43248,11 @@ async function signPermit2Authorization(p3) {
|
|
|
42981
43248
|
witnessTo: p3.payTo,
|
|
42982
43249
|
validAfter
|
|
42983
43250
|
});
|
|
42984
|
-
const rawSignature = await p3.
|
|
42985
|
-
|
|
42986
|
-
|
|
42987
|
-
|
|
42988
|
-
|
|
42989
|
-
});
|
|
43251
|
+
const rawSignature = await p3.walletProvider.signTypedData(
|
|
43252
|
+
p3.chainKey,
|
|
43253
|
+
p3.chainId,
|
|
43254
|
+
JSON.stringify(typedData)
|
|
43255
|
+
);
|
|
42990
43256
|
const signature = normalizeEip712Signature(rawSignature);
|
|
42991
43257
|
return {
|
|
42992
43258
|
signature,
|
|
@@ -43082,17 +43348,16 @@ async function payAndFetch(args) {
|
|
|
43082
43348
|
if (!confirmed) {
|
|
43083
43349
|
throw new X402Error("PAYMENT_CANCELLED", "Payment cancelled by user");
|
|
43084
43350
|
}
|
|
43085
|
-
const
|
|
43086
|
-
const
|
|
43087
|
-
const bc2 = new BlockchainClient(signer);
|
|
43088
|
-
const fromAddress = signer.getAddress(chainKey);
|
|
43351
|
+
const bc2 = new BlockchainClient();
|
|
43352
|
+
const fromAddress = await args.provider.getAddress(chainKey);
|
|
43089
43353
|
const tokenAddress = getAsset(kind);
|
|
43090
43354
|
const amount = getAmount(kind);
|
|
43091
43355
|
let xPayment;
|
|
43092
43356
|
let nonce;
|
|
43093
43357
|
if (method === "eip3009") {
|
|
43094
43358
|
const { signature, authorization } = await signTransferWithAuthorization({
|
|
43095
|
-
|
|
43359
|
+
walletProvider: args.provider,
|
|
43360
|
+
chainKey,
|
|
43096
43361
|
token: {
|
|
43097
43362
|
name: rawName,
|
|
43098
43363
|
version: rawVersion,
|
|
@@ -43114,6 +43379,7 @@ async function payAndFetch(args) {
|
|
|
43114
43379
|
nonce = authorization.nonce;
|
|
43115
43380
|
} else {
|
|
43116
43381
|
approval = await ensurePermit2Allowance({
|
|
43382
|
+
walletProvider: args.provider,
|
|
43117
43383
|
blockchainClient: bc2,
|
|
43118
43384
|
chainKey,
|
|
43119
43385
|
chainId: evmChainId,
|
|
@@ -43121,13 +43387,13 @@ async function payAndFetch(args) {
|
|
|
43121
43387
|
owner: fromAddress,
|
|
43122
43388
|
tokenAddress,
|
|
43123
43389
|
requiredAmount: BigInt(amount),
|
|
43124
|
-
confirmApproval: args.confirmApproval
|
|
43125
|
-
hdWallet
|
|
43390
|
+
confirmApproval: args.confirmApproval
|
|
43126
43391
|
}) ?? void 0;
|
|
43127
43392
|
const rawExtra2 = getExtra(kind);
|
|
43128
43393
|
const dynamicSpender = typeof rawExtra2?.spenderAddress === "string" ? rawExtra2.spenderAddress : void 0;
|
|
43129
43394
|
const { signature, permit2Authorization } = await signPermit2Authorization({
|
|
43130
|
-
|
|
43395
|
+
walletProvider: args.provider,
|
|
43396
|
+
chainKey,
|
|
43131
43397
|
chainId: evmChainId,
|
|
43132
43398
|
tokenAddress,
|
|
43133
43399
|
amount,
|
|
@@ -43188,14 +43454,14 @@ async function ensurePermit2Allowance(args) {
|
|
|
43188
43454
|
`Permit2 needs a one-time approval for ${args.tokenAddress} on ${args.chainKey}. Run: twak erc20 approve --asset ${assetId} --spender ${PERMIT2_ADDRESS} --amount unlimited`
|
|
43189
43455
|
);
|
|
43190
43456
|
}
|
|
43191
|
-
const
|
|
43192
|
-
|
|
43193
|
-
|
|
43194
|
-
|
|
43195
|
-
|
|
43196
|
-
|
|
43197
|
-
await waitForTxConfirmation(args.blockchainClient, args.chainKey, args.owner,
|
|
43198
|
-
return { token: args.tokenAddress, chain: args.chainKey, txHash
|
|
43457
|
+
const { txHash } = await args.walletProvider.approveErc20(
|
|
43458
|
+
args.chainKey,
|
|
43459
|
+
args.tokenAddress,
|
|
43460
|
+
PERMIT2_ADDRESS,
|
|
43461
|
+
MAX_UINT256_DECIMAL
|
|
43462
|
+
);
|
|
43463
|
+
await waitForTxConfirmation(args.blockchainClient, args.chainKey, args.owner, txHash);
|
|
43464
|
+
return { token: args.tokenAddress, chain: args.chainKey, txHash };
|
|
43199
43465
|
}
|
|
43200
43466
|
var APPROVE_TIMEOUT_MS = 6e4;
|
|
43201
43467
|
var APPROVE_POLL_MS = 2e3;
|
|
@@ -43412,6 +43678,17 @@ async function readChallenge(resp) {
|
|
|
43412
43678
|
);
|
|
43413
43679
|
}
|
|
43414
43680
|
}
|
|
43681
|
+
function isPayableNetwork(network) {
|
|
43682
|
+
try {
|
|
43683
|
+
parseNetwork(network);
|
|
43684
|
+
return true;
|
|
43685
|
+
} catch {
|
|
43686
|
+
return false;
|
|
43687
|
+
}
|
|
43688
|
+
}
|
|
43689
|
+
function safeText(s2) {
|
|
43690
|
+
return s2.replace(/[\x00-\x1f\x7f]/g, "");
|
|
43691
|
+
}
|
|
43415
43692
|
async function quoteX402(args) {
|
|
43416
43693
|
try {
|
|
43417
43694
|
await validateX402Url(args.url);
|
|
@@ -43458,7 +43735,10 @@ async function quoteX402(args) {
|
|
|
43458
43735
|
}
|
|
43459
43736
|
let preferredKind = null;
|
|
43460
43737
|
try {
|
|
43461
|
-
|
|
43738
|
+
const picked = pickSupportedKind(challenge.accepts);
|
|
43739
|
+
if (isPayableNetwork(getNetwork(picked))) {
|
|
43740
|
+
preferredKind = picked;
|
|
43741
|
+
}
|
|
43462
43742
|
} catch {
|
|
43463
43743
|
}
|
|
43464
43744
|
const accepts = challenge.accepts.map((kind) => {
|
|
@@ -43482,22 +43762,32 @@ async function quoteX402(args) {
|
|
|
43482
43762
|
maxTimeoutSeconds: getMaxTimeoutSeconds(kind),
|
|
43483
43763
|
preferred: preferredKind === kind,
|
|
43484
43764
|
requiresApproval,
|
|
43485
|
-
description
|
|
43765
|
+
// `description` is a human-readable string consumers may render to a
|
|
43766
|
+
// terminal (CLI, LLM chat UI). `tokenName` and `transferMethod` are
|
|
43767
|
+
// seller-controlled (`extra.name`, `extra.assetTransferMethod`) so we
|
|
43768
|
+
// strip control chars to defend against terminal-escape injection.
|
|
43769
|
+
// Raw values stay on the structured `tokenName` / `transferMethod`
|
|
43770
|
+
// fields so consumers can apply their own escaping policy.
|
|
43771
|
+
description: `${amount} of ${safeText(tokenLabel)} on ${network} via ${safeText(transferMethod)}${approvalTag}`
|
|
43486
43772
|
};
|
|
43487
43773
|
});
|
|
43488
43774
|
const chosen = accepts.find((a3) => a3.preferred);
|
|
43489
|
-
const
|
|
43775
|
+
const safeTokenLabel = chosen ? safeText(chosen.tokenName) || chosen.asset : "";
|
|
43776
|
+
const safeMethod = chosen ? safeText(chosen.transferMethod) : "";
|
|
43777
|
+
const summary = chosen ? `Endpoint offers ${accepts.length} payment option(s). Default route: ${chosen.amount} of ${safeTokenLabel} on ${chosen.network} via ${safeMethod}${chosen.requiresApproval ? " (requires one-time Permit2 approval)" : " (gasless)"}.` : `Endpoint offers ${accepts.length} payment option(s), but none are supported by this client.`;
|
|
43490
43778
|
const requestShapeHint = supportsBody ? args.body ? ` with method "${httpMethod}" and the same request body` : ` with method "${httpMethod}"` : "";
|
|
43491
43779
|
return {
|
|
43492
43780
|
success: true,
|
|
43493
43781
|
resource: challenge.version === 2 ? challenge.resource : void 0,
|
|
43494
43782
|
accepts,
|
|
43495
43783
|
summary,
|
|
43496
|
-
nextAction: chosen ? `
|
|
43784
|
+
nextAction: chosen ? `Show these payment options to the user and let them pick one. Then call x402_request with this url${requestShapeHint}, maxPaymentAtomic set to the picked option's amount, and preferAsset set to its asset contract.` : "No supported route. The endpoint offered no payment method this client can sign."
|
|
43497
43785
|
};
|
|
43498
43786
|
}
|
|
43499
43787
|
|
|
43500
43788
|
// ../actions/dist/index.mjs
|
|
43789
|
+
var import_zod15 = require("zod");
|
|
43790
|
+
init_dist();
|
|
43501
43791
|
var ActionProvider = class {
|
|
43502
43792
|
supportsChain(_chainId) {
|
|
43503
43793
|
return true;
|
|
@@ -43614,88 +43904,6 @@ var BalanceActionProvider = class extends ActionProvider {
|
|
|
43614
43904
|
};
|
|
43615
43905
|
}
|
|
43616
43906
|
};
|
|
43617
|
-
var WalletActionProvider = class extends ActionProvider {
|
|
43618
|
-
constructor(walletProvider, agentWallet) {
|
|
43619
|
-
super();
|
|
43620
|
-
this.walletProvider = walletProvider;
|
|
43621
|
-
this.agentWallet = agentWallet;
|
|
43622
|
-
}
|
|
43623
|
-
name = "wallet";
|
|
43624
|
-
description = "Wallet connection and address management";
|
|
43625
|
-
getActions() {
|
|
43626
|
-
if (!this.walletProvider) return [];
|
|
43627
|
-
return [this.getWalletAddress(), this.getWalletStatus(), this.connectWallet()];
|
|
43628
|
-
}
|
|
43629
|
-
getWalletAddress() {
|
|
43630
|
-
return {
|
|
43631
|
-
name: "get_wallet_address",
|
|
43632
|
-
description: "Get the connected wallet address",
|
|
43633
|
-
schema: import_zod4.z.object({
|
|
43634
|
-
chainId: import_zod4.z.string().optional().default("eip155:1").describe("Chain ID")
|
|
43635
|
-
}),
|
|
43636
|
-
invoke: async (_ctx, input2) => {
|
|
43637
|
-
const provider = this.agentWallet ?? this.walletProvider;
|
|
43638
|
-
if (!provider) {
|
|
43639
|
-
return {
|
|
43640
|
-
connected: false,
|
|
43641
|
-
code: "WALLET_NOT_FOUND",
|
|
43642
|
-
message: "No wallet provider configured"
|
|
43643
|
-
};
|
|
43644
|
-
}
|
|
43645
|
-
try {
|
|
43646
|
-
const address = await provider.getAddress(input2.chainId);
|
|
43647
|
-
return { connected: true, address, chainId: input2.chainId, provider: provider.name };
|
|
43648
|
-
} catch {
|
|
43649
|
-
return { connected: false };
|
|
43650
|
-
}
|
|
43651
|
-
}
|
|
43652
|
-
};
|
|
43653
|
-
}
|
|
43654
|
-
connectWallet() {
|
|
43655
|
-
return {
|
|
43656
|
-
name: "connect_wallet",
|
|
43657
|
-
description: "Connect a wallet via WalletConnect (returns QR code / deep link)",
|
|
43658
|
-
schema: import_zod4.z.object({
|
|
43659
|
-
chains: import_zod4.z.array(import_zod4.z.string()).optional().default(["eip155:1"]).describe("Chains to connect")
|
|
43660
|
-
}),
|
|
43661
|
-
invoke: async (_ctx, input2) => {
|
|
43662
|
-
if (!this.walletProvider) {
|
|
43663
|
-
return {
|
|
43664
|
-
success: false,
|
|
43665
|
-
code: "WALLET_NOT_FOUND",
|
|
43666
|
-
message: "No wallet provider configured"
|
|
43667
|
-
};
|
|
43668
|
-
}
|
|
43669
|
-
const result = await this.walletProvider.connect(input2.chains);
|
|
43670
|
-
return { success: true, ...result };
|
|
43671
|
-
}
|
|
43672
|
-
};
|
|
43673
|
-
}
|
|
43674
|
-
getWalletStatus() {
|
|
43675
|
-
return {
|
|
43676
|
-
name: "get_wallet_status",
|
|
43677
|
-
description: "Check wallet connection status",
|
|
43678
|
-
schema: import_zod4.z.object({}),
|
|
43679
|
-
invoke: async (_ctx) => {
|
|
43680
|
-
const status = {};
|
|
43681
|
-
if (this.agentWallet) {
|
|
43682
|
-
status.agent = {
|
|
43683
|
-
connected: this.agentWallet.isConnected(),
|
|
43684
|
-
addresses: await this.agentWallet.getAddresses()
|
|
43685
|
-
};
|
|
43686
|
-
}
|
|
43687
|
-
if (this.walletProvider) {
|
|
43688
|
-
status.walletConnect = {
|
|
43689
|
-
connected: this.walletProvider.isConnected(),
|
|
43690
|
-
addresses: this.walletProvider.isConnected() ? await this.walletProvider.getAddresses() : []
|
|
43691
|
-
};
|
|
43692
|
-
}
|
|
43693
|
-
const hasAny = this.agentWallet || this.walletProvider;
|
|
43694
|
-
return hasAny ? status : { connected: false, provider: "none" };
|
|
43695
|
-
}
|
|
43696
|
-
};
|
|
43697
|
-
}
|
|
43698
|
-
};
|
|
43699
43907
|
var ERC20_TRANSFER_SIG = "0xa9059cbb";
|
|
43700
43908
|
var NATIVE_TOKEN_PLACEHOLDER = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
43701
43909
|
var DEFAULT_MAX_TRANSFER_USD2 = 1e4;
|
|
@@ -43731,9 +43939,9 @@ function toCaip2(chain2) {
|
|
|
43731
43939
|
return caip2;
|
|
43732
43940
|
}
|
|
43733
43941
|
var TransferActionProvider = class extends ActionProvider {
|
|
43734
|
-
constructor(
|
|
43942
|
+
constructor(walletContext, priceLookup, maxTransferUsd = DEFAULT_MAX_TRANSFER_USD2) {
|
|
43735
43943
|
super();
|
|
43736
|
-
this.
|
|
43944
|
+
this.walletContext = walletContext;
|
|
43737
43945
|
this.priceLookup = priceLookup;
|
|
43738
43946
|
this.maxTransferUsd = maxTransferUsd;
|
|
43739
43947
|
}
|
|
@@ -43746,20 +43954,17 @@ var TransferActionProvider = class extends ActionProvider {
|
|
|
43746
43954
|
return {
|
|
43747
43955
|
name: "transfer",
|
|
43748
43956
|
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.',
|
|
43749
|
-
schema:
|
|
43750
|
-
chain:
|
|
43957
|
+
schema: import_zod4.z.object({
|
|
43958
|
+
chain: import_zod4.z.string().describe(
|
|
43751
43959
|
'Chain name (e.g., ethereum, bsc, polygon, arbitrum, base). CAIP-2 ids like "eip155:1" also accepted.'
|
|
43752
43960
|
),
|
|
43753
|
-
to:
|
|
43754
|
-
amount:
|
|
43755
|
-
token:
|
|
43756
|
-
decimals:
|
|
43961
|
+
to: import_zod4.z.string().describe("Destination address or ENS name (e.g., vitalik.eth)"),
|
|
43962
|
+
amount: import_zod4.z.string().describe('Amount in human-readable format (e.g., "1.5", "0.01")'),
|
|
43963
|
+
token: import_zod4.z.string().optional().describe("ERC-20 token contract address (omit for native token)"),
|
|
43964
|
+
decimals: import_zod4.z.coerce.number().optional().default(18).describe("Token decimals (default 18, use 6 for USDC/USDT)")
|
|
43757
43965
|
}),
|
|
43758
43966
|
invoke: async (_ctx, input2) => {
|
|
43759
|
-
const signer = this.
|
|
43760
|
-
if (!signer) {
|
|
43761
|
-
throw new TransferError("WALLET_NOT_FOUND", "No wallet provider configured");
|
|
43762
|
-
}
|
|
43967
|
+
const signer = this.walletContext.activeWallet();
|
|
43763
43968
|
const caip2 = toCaip2(input2.chain);
|
|
43764
43969
|
if (this.priceLookup) {
|
|
43765
43970
|
try {
|
|
@@ -43816,10 +44021,10 @@ var TransferActionProvider = class extends ActionProvider {
|
|
|
43816
44021
|
}
|
|
43817
44022
|
};
|
|
43818
44023
|
var SwapActionProvider = class extends ActionProvider {
|
|
43819
|
-
constructor(swapClient,
|
|
44024
|
+
constructor(swapClient, walletContext, blockchainClient) {
|
|
43820
44025
|
super();
|
|
43821
44026
|
this.swapClient = swapClient;
|
|
43822
|
-
this.
|
|
44027
|
+
this.walletContext = walletContext;
|
|
43823
44028
|
this.blockchainClient = blockchainClient;
|
|
43824
44029
|
}
|
|
43825
44030
|
name = "swap";
|
|
@@ -43871,19 +44076,19 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
43871
44076
|
return {
|
|
43872
44077
|
name: "swap",
|
|
43873
44078
|
description: "Execute a token swap via Trust Wallet (supports all chains including cross-chain)",
|
|
43874
|
-
schema:
|
|
43875
|
-
fromChain:
|
|
43876
|
-
fromToken:
|
|
43877
|
-
toChain:
|
|
43878
|
-
toToken:
|
|
43879
|
-
amount:
|
|
43880
|
-
slippage:
|
|
44079
|
+
schema: import_zod5.z.object({
|
|
44080
|
+
fromChain: import_zod5.z.string().describe("Source chain (e.g., ethereum, polygon, solana)"),
|
|
44081
|
+
fromToken: import_zod5.z.string().describe("Source token symbol or address"),
|
|
44082
|
+
toChain: import_zod5.z.string().describe("Destination chain"),
|
|
44083
|
+
toToken: import_zod5.z.string().describe("Destination token symbol or address"),
|
|
44084
|
+
amount: import_zod5.z.string().describe("Human-readable amount to swap"),
|
|
44085
|
+
slippage: import_zod5.z.string().optional().default("1").describe("Slippage tolerance % (max 50)")
|
|
43881
44086
|
}),
|
|
43882
44087
|
invoke: async (_ctx, input2) => {
|
|
43883
|
-
|
|
43884
|
-
|
|
43885
|
-
throw new SwapError("WALLET_NOT_FOUND", "Wallet and swap client required");
|
|
44088
|
+
if (!this.swapClient) {
|
|
44089
|
+
throw new SwapError("WALLET_NOT_FOUND", "Swap client not configured");
|
|
43886
44090
|
}
|
|
44091
|
+
const signer = this.walletContext.activeWallet();
|
|
43887
44092
|
const slippage = parseFloat(input2.slippage ?? "1");
|
|
43888
44093
|
if (isNaN(slippage) || slippage <= 0 || slippage > 50) {
|
|
43889
44094
|
return {
|
|
@@ -43958,12 +44163,12 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
43958
44163
|
return {
|
|
43959
44164
|
name: "get_swap_quote",
|
|
43960
44165
|
description: "Get a swap quote without executing",
|
|
43961
|
-
schema:
|
|
43962
|
-
fromChain:
|
|
43963
|
-
fromToken:
|
|
43964
|
-
toChain:
|
|
43965
|
-
toToken:
|
|
43966
|
-
amount:
|
|
44166
|
+
schema: import_zod5.z.object({
|
|
44167
|
+
fromChain: import_zod5.z.string().describe("Source chain"),
|
|
44168
|
+
fromToken: import_zod5.z.string().describe("Source token symbol or address"),
|
|
44169
|
+
toChain: import_zod5.z.string().describe("Destination chain"),
|
|
44170
|
+
toToken: import_zod5.z.string().describe("Destination token symbol or address"),
|
|
44171
|
+
amount: import_zod5.z.string().describe("Human-readable amount")
|
|
43967
44172
|
}),
|
|
43968
44173
|
invoke: async (_ctx, input2) => {
|
|
43969
44174
|
if (!this.swapClient) {
|
|
@@ -44016,9 +44221,9 @@ var SwapActionProvider = class extends ActionProvider {
|
|
|
44016
44221
|
return {
|
|
44017
44222
|
name: "get_token_price",
|
|
44018
44223
|
description: "Get current USD price for a token",
|
|
44019
|
-
schema:
|
|
44020
|
-
chain:
|
|
44021
|
-
token:
|
|
44224
|
+
schema: import_zod5.z.object({
|
|
44225
|
+
chain: import_zod5.z.string().describe("Chain name (e.g., ethereum)"),
|
|
44226
|
+
token: import_zod5.z.string().describe("Token symbol or address")
|
|
44022
44227
|
}),
|
|
44023
44228
|
invoke: async (_ctx, input2) => {
|
|
44024
44229
|
if (!this.swapClient) {
|
|
@@ -44067,10 +44272,10 @@ var SecurityActionProvider = class extends ActionProvider {
|
|
|
44067
44272
|
return {
|
|
44068
44273
|
name: "validate_transaction",
|
|
44069
44274
|
description: "Validate an address or transaction for security",
|
|
44070
|
-
schema:
|
|
44071
|
-
address:
|
|
44072
|
-
assetId:
|
|
44073
|
-
type:
|
|
44275
|
+
schema: import_zod6.z.object({
|
|
44276
|
+
address: import_zod6.z.string().describe("Address to validate"),
|
|
44277
|
+
assetId: import_zod6.z.string().optional().describe("Asset ID"),
|
|
44278
|
+
type: import_zod6.z.string().optional().describe("Validation type")
|
|
44074
44279
|
}),
|
|
44075
44280
|
invoke: async (ctx, input2) => {
|
|
44076
44281
|
return this.getService(ctx).validate(input2);
|
|
@@ -44081,8 +44286,8 @@ var SecurityActionProvider = class extends ActionProvider {
|
|
|
44081
44286
|
return {
|
|
44082
44287
|
name: "get_asset_info",
|
|
44083
44288
|
description: "Get detailed information about an asset",
|
|
44084
|
-
schema:
|
|
44085
|
-
assetId:
|
|
44289
|
+
schema: import_zod6.z.object({
|
|
44290
|
+
assetId: import_zod6.z.string().describe("Asset ID (e.g., c60_t0xA0b8...)")
|
|
44086
44291
|
}),
|
|
44087
44292
|
invoke: async (ctx, input2) => {
|
|
44088
44293
|
return this.getService(ctx).getAssetInfo(input2.assetId);
|
|
@@ -44107,12 +44312,12 @@ var HistoryActionProvider = class extends ActionProvider {
|
|
|
44107
44312
|
return {
|
|
44108
44313
|
name: "get_transaction_history",
|
|
44109
44314
|
description: "Get transaction history for an address",
|
|
44110
|
-
schema:
|
|
44111
|
-
address:
|
|
44112
|
-
chain:
|
|
44113
|
-
limit:
|
|
44114
|
-
from:
|
|
44115
|
-
to:
|
|
44315
|
+
schema: import_zod7.z.object({
|
|
44316
|
+
address: import_zod7.z.string().describe("Wallet address"),
|
|
44317
|
+
chain: import_zod7.z.string().optional().describe("Chain name"),
|
|
44318
|
+
limit: import_zod7.z.coerce.number().optional().default(20).describe("Max results"),
|
|
44319
|
+
from: import_zod7.z.string().optional().describe("Start date (ISO)"),
|
|
44320
|
+
to: import_zod7.z.string().optional().describe("End date (ISO)")
|
|
44116
44321
|
}),
|
|
44117
44322
|
invoke: async (ctx, input2) => {
|
|
44118
44323
|
return this.getService(ctx).getHistory(input2);
|
|
@@ -44123,9 +44328,9 @@ var HistoryActionProvider = class extends ActionProvider {
|
|
|
44123
44328
|
return {
|
|
44124
44329
|
name: "get_transaction_details",
|
|
44125
44330
|
description: "Get details of a specific transaction by hash",
|
|
44126
|
-
schema:
|
|
44127
|
-
hash:
|
|
44128
|
-
chain:
|
|
44331
|
+
schema: import_zod7.z.object({
|
|
44332
|
+
hash: import_zod7.z.string().describe("Transaction hash"),
|
|
44333
|
+
chain: import_zod7.z.string().describe("Chain name")
|
|
44129
44334
|
}),
|
|
44130
44335
|
invoke: async (ctx, input2) => {
|
|
44131
44336
|
const details = await this.getService(ctx).getByHash(input2.hash, input2.chain);
|
|
@@ -44138,10 +44343,10 @@ var HistoryActionProvider = class extends ActionProvider {
|
|
|
44138
44343
|
return {
|
|
44139
44344
|
name: "get_swap_history",
|
|
44140
44345
|
description: "Get swap transaction history",
|
|
44141
|
-
schema:
|
|
44142
|
-
address:
|
|
44143
|
-
chain:
|
|
44144
|
-
limit:
|
|
44346
|
+
schema: import_zod7.z.object({
|
|
44347
|
+
address: import_zod7.z.string().describe("Wallet address"),
|
|
44348
|
+
chain: import_zod7.z.string().optional().describe("Chain name"),
|
|
44349
|
+
limit: import_zod7.z.coerce.number().optional().default(20).describe("Max results")
|
|
44145
44350
|
}),
|
|
44146
44351
|
invoke: async (ctx, input2) => {
|
|
44147
44352
|
return this.getService(ctx).getSwapHistory(input2);
|
|
@@ -44171,11 +44376,11 @@ var AlertActionProvider = class extends ActionProvider {
|
|
|
44171
44376
|
return {
|
|
44172
44377
|
name: "create_alert",
|
|
44173
44378
|
description: "Create a price alert for a token. Triggers when price goes above or below a target.",
|
|
44174
|
-
schema:
|
|
44175
|
-
tokenId:
|
|
44176
|
-
chainKey:
|
|
44177
|
-
condition:
|
|
44178
|
-
targetPrice:
|
|
44379
|
+
schema: import_zod8.z.object({
|
|
44380
|
+
tokenId: import_zod8.z.string().describe('Token ID (e.g., "c60" for ETH, "c20000714_t0x55d3..." for BSC USDT)'),
|
|
44381
|
+
chainKey: import_zod8.z.string().describe('Chain key (e.g., "ethereum", "bsc")'),
|
|
44382
|
+
condition: import_zod8.z.enum(["above", "below"]).describe("Alert when price goes above or below target"),
|
|
44383
|
+
targetPrice: import_zod8.z.coerce.number().positive().describe("Target price in USD")
|
|
44179
44384
|
}),
|
|
44180
44385
|
invoke: async (_ctx, input2) => {
|
|
44181
44386
|
try {
|
|
@@ -44200,8 +44405,8 @@ var AlertActionProvider = class extends ActionProvider {
|
|
|
44200
44405
|
return {
|
|
44201
44406
|
name: "list_alerts",
|
|
44202
44407
|
description: "List all price alerts",
|
|
44203
|
-
schema:
|
|
44204
|
-
activeOnly:
|
|
44408
|
+
schema: import_zod8.z.object({
|
|
44409
|
+
activeOnly: import_zod8.z.boolean().optional().default(false).describe("Only show active alerts")
|
|
44205
44410
|
}),
|
|
44206
44411
|
invoke: async (_ctx, input2) => {
|
|
44207
44412
|
const alerts = input2.activeOnly ? await this.alertStore.getActive() : await this.alertStore.list();
|
|
@@ -44213,7 +44418,7 @@ var AlertActionProvider = class extends ActionProvider {
|
|
|
44213
44418
|
return {
|
|
44214
44419
|
name: "check_alerts",
|
|
44215
44420
|
description: "Check active alerts against current prices and trigger any that match",
|
|
44216
|
-
schema:
|
|
44421
|
+
schema: import_zod8.z.object({}),
|
|
44217
44422
|
invoke: async (_ctx, _input) => {
|
|
44218
44423
|
const activeAlerts = await this.alertStore.getActive();
|
|
44219
44424
|
const triggered = [];
|
|
@@ -44246,8 +44451,8 @@ var AlertActionProvider = class extends ActionProvider {
|
|
|
44246
44451
|
return {
|
|
44247
44452
|
name: "delete_alert",
|
|
44248
44453
|
description: "Delete a price alert by ID",
|
|
44249
|
-
schema:
|
|
44250
|
-
id:
|
|
44454
|
+
schema: import_zod8.z.object({
|
|
44455
|
+
id: import_zod8.z.string().describe("Alert ID to delete")
|
|
44251
44456
|
}),
|
|
44252
44457
|
invoke: async (_ctx, input2) => {
|
|
44253
44458
|
const deleted = await this.alertStore.delete(input2.id);
|
|
@@ -44260,31 +44465,25 @@ var AlertActionProvider = class extends ActionProvider {
|
|
|
44260
44465
|
}
|
|
44261
44466
|
};
|
|
44262
44467
|
var AgentWalletActionProvider = class extends ActionProvider {
|
|
44263
|
-
constructor(blockchainClient,
|
|
44468
|
+
constructor(blockchainClient, walletContext) {
|
|
44264
44469
|
super();
|
|
44265
44470
|
this.blockchainClient = blockchainClient;
|
|
44266
|
-
this.
|
|
44471
|
+
this.walletContext = walletContext;
|
|
44267
44472
|
}
|
|
44268
44473
|
name = "agent-wallet";
|
|
44269
|
-
description = "Derive addresses and read native balances for the
|
|
44474
|
+
description = "Derive addresses and read native balances for the active wallet (local HD or WalletConnect-paired). Returns the address that will actually sign \u2014 call these to surface the user-facing wallet identity.";
|
|
44270
44475
|
getActions() {
|
|
44271
44476
|
return [this.getAddress(), this.listAddresses(), this.walletBalance()];
|
|
44272
44477
|
}
|
|
44273
44478
|
requireWallet() {
|
|
44274
|
-
|
|
44275
|
-
throw new WalletError(
|
|
44276
|
-
"WALLET_NOT_FOUND",
|
|
44277
|
-
"Agent wallet not loaded. Create one with `twak init` (CLI) and restart the MCP server so it can unlock the wallet at startup."
|
|
44278
|
-
);
|
|
44279
|
-
}
|
|
44280
|
-
return this.agentWallet;
|
|
44479
|
+
return this.walletContext.activeWallet();
|
|
44281
44480
|
}
|
|
44282
44481
|
getAddress() {
|
|
44283
44482
|
return {
|
|
44284
44483
|
name: "get_address",
|
|
44285
|
-
description: "
|
|
44286
|
-
schema:
|
|
44287
|
-
chainKey:
|
|
44484
|
+
description: "Get the active wallet's address for a specific chain.",
|
|
44485
|
+
schema: import_zod9.z.object({
|
|
44486
|
+
chainKey: import_zod9.z.string().describe('Chain key (e.g., "ethereum", "bsc", "solana")')
|
|
44288
44487
|
}),
|
|
44289
44488
|
invoke: async (_ctx, input2) => {
|
|
44290
44489
|
const wallet = this.requireWallet();
|
|
@@ -44296,8 +44495,8 @@ var AgentWalletActionProvider = class extends ActionProvider {
|
|
|
44296
44495
|
listAddresses() {
|
|
44297
44496
|
return {
|
|
44298
44497
|
name: "list_addresses",
|
|
44299
|
-
description: "
|
|
44300
|
-
schema:
|
|
44498
|
+
description: "Get the active wallet's addresses for every supported chain. Returns one address per chain.",
|
|
44499
|
+
schema: import_zod9.z.object({}),
|
|
44301
44500
|
invoke: async () => {
|
|
44302
44501
|
const wallet = this.requireWallet();
|
|
44303
44502
|
const addresses = await wallet.getAddresses();
|
|
@@ -44308,9 +44507,9 @@ var AgentWalletActionProvider = class extends ActionProvider {
|
|
|
44308
44507
|
walletBalance() {
|
|
44309
44508
|
return {
|
|
44310
44509
|
name: "wallet_balance",
|
|
44311
|
-
description: "Read the native-coin balance (BNB on BSC, ETH on Ethereum / Base /
|
|
44312
|
-
schema:
|
|
44313
|
-
chainKey:
|
|
44510
|
+
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
|
+
schema: import_zod9.z.object({
|
|
44512
|
+
chainKey: import_zod9.z.string().describe('Chain key (e.g., "ethereum", "bsc")')
|
|
44314
44513
|
}),
|
|
44315
44514
|
invoke: async (_ctx, input2) => {
|
|
44316
44515
|
const wallet = this.requireWallet();
|
|
@@ -44330,10 +44529,10 @@ var AgentWalletActionProvider = class extends ActionProvider {
|
|
|
44330
44529
|
}
|
|
44331
44530
|
};
|
|
44332
44531
|
var TokenActionProvider = class extends ActionProvider {
|
|
44333
|
-
constructor(blockchainClient,
|
|
44532
|
+
constructor(blockchainClient, walletContext) {
|
|
44334
44533
|
super();
|
|
44335
44534
|
this.blockchainClient = blockchainClient;
|
|
44336
|
-
this.
|
|
44535
|
+
this.walletContext = walletContext;
|
|
44337
44536
|
}
|
|
44338
44537
|
name = "token";
|
|
44339
44538
|
description = "Chain-agnostic token operations \u2014 balances, approvals, and transfers";
|
|
@@ -44350,9 +44549,9 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44350
44549
|
return {
|
|
44351
44550
|
name: "token_balance",
|
|
44352
44551
|
description: "Get balance for a specific token or native asset by asset ID",
|
|
44353
|
-
schema:
|
|
44354
|
-
assetId:
|
|
44355
|
-
address:
|
|
44552
|
+
schema: import_zod10.z.object({
|
|
44553
|
+
assetId: import_zod10.z.string().describe('Token ID (e.g., "c60" for ETH, "c20000714_t0x55d3..." for BSC USDT)'),
|
|
44554
|
+
address: import_zod10.z.string().describe("Wallet address to check balance for")
|
|
44356
44555
|
}),
|
|
44357
44556
|
invoke: async (_ctx, input2) => {
|
|
44358
44557
|
if (!this.blockchainClient) {
|
|
@@ -44366,29 +44565,64 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44366
44565
|
transferToken() {
|
|
44367
44566
|
return {
|
|
44368
44567
|
name: "transfer_token",
|
|
44369
|
-
description: "Transfer a token or native asset to a destination address",
|
|
44370
|
-
schema:
|
|
44371
|
-
assetId:
|
|
44372
|
-
to:
|
|
44373
|
-
amount:
|
|
44374
|
-
memo:
|
|
44568
|
+
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
|
+
schema: import_zod10.z.object({
|
|
44570
|
+
assetId: import_zod10.z.string().describe("Token ID to transfer"),
|
|
44571
|
+
to: import_zod10.z.string().describe("Destination address"),
|
|
44572
|
+
amount: import_zod10.z.string().describe("Amount to transfer (in smallest unit)"),
|
|
44573
|
+
memo: import_zod10.z.string().optional().describe("Optional memo/note")
|
|
44375
44574
|
}),
|
|
44376
44575
|
invoke: async (_ctx, input2) => {
|
|
44377
|
-
|
|
44378
|
-
|
|
44576
|
+
const wallet = this.walletContext.activeWallet();
|
|
44577
|
+
if (wallet instanceof AgentWalletProvider) {
|
|
44578
|
+
if (!this.blockchainClient) {
|
|
44579
|
+
throw new WalletError("WALLET_NOT_FOUND", "BlockchainClient not available");
|
|
44580
|
+
}
|
|
44581
|
+
const hdWallet = wallet.getSigner().getHDWallet();
|
|
44582
|
+
const result = await this.blockchainClient.transfer({
|
|
44583
|
+
assetId: input2.assetId,
|
|
44584
|
+
to: input2.to,
|
|
44585
|
+
amount: input2.amount,
|
|
44586
|
+
memo: input2.memo,
|
|
44587
|
+
hdWallet
|
|
44588
|
+
});
|
|
44589
|
+
return { hash: result.hash, chainKey: result.chainKey };
|
|
44379
44590
|
}
|
|
44380
|
-
|
|
44381
|
-
|
|
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}`);
|
|
44382
44595
|
}
|
|
44383
|
-
const
|
|
44384
|
-
|
|
44385
|
-
|
|
44386
|
-
|
|
44387
|
-
|
|
44388
|
-
|
|
44389
|
-
|
|
44390
|
-
}
|
|
44391
|
-
|
|
44596
|
+
const chain2 = getChainById(chainKey);
|
|
44597
|
+
if (!chain2 || chain2.namespace !== "eip155") {
|
|
44598
|
+
throw new WalletError(
|
|
44599
|
+
"CHAIN_UNSUPPORTED",
|
|
44600
|
+
`transfer_token on ${chainKey} is not supported in WalletConnect mode (EVM-only). Switch to local wallet mode for ${chainKey} transfers.`
|
|
44601
|
+
);
|
|
44602
|
+
}
|
|
44603
|
+
const caipChainId = `${chain2.namespace}:${chain2.chainId}`;
|
|
44604
|
+
let txHash;
|
|
44605
|
+
if (contractAddress) {
|
|
44606
|
+
const selector = "0xa9059cbb";
|
|
44607
|
+
const paddedTo = input2.to.replace(/^0x/, "").toLowerCase().padStart(64, "0");
|
|
44608
|
+
const paddedAmount = BigInt(input2.amount).toString(16).padStart(64, "0");
|
|
44609
|
+
const data = selector + paddedTo + paddedAmount;
|
|
44610
|
+
txHash = await wallet.sendTransaction({
|
|
44611
|
+
chainId: caipChainId,
|
|
44612
|
+
to: contractAddress,
|
|
44613
|
+
data,
|
|
44614
|
+
value: "0x0"
|
|
44615
|
+
});
|
|
44616
|
+
} else {
|
|
44617
|
+
const valueHex = "0x" + BigInt(input2.amount).toString(16);
|
|
44618
|
+
txHash = await wallet.sendTransaction({
|
|
44619
|
+
chainId: caipChainId,
|
|
44620
|
+
to: input2.to,
|
|
44621
|
+
data: "0x",
|
|
44622
|
+
value: valueHex
|
|
44623
|
+
});
|
|
44624
|
+
}
|
|
44625
|
+
return { hash: txHash, chainKey };
|
|
44392
44626
|
}
|
|
44393
44627
|
};
|
|
44394
44628
|
}
|
|
@@ -44396,27 +44630,31 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44396
44630
|
return {
|
|
44397
44631
|
name: "approve_token",
|
|
44398
44632
|
description: `Approve a spender to use tokens on your behalf (ERC-20 approve). 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.`,
|
|
44399
|
-
schema:
|
|
44400
|
-
assetId:
|
|
44401
|
-
spender:
|
|
44402
|
-
amount:
|
|
44633
|
+
schema: import_zod10.z.object({
|
|
44634
|
+
assetId: import_zod10.z.string().describe("Token ID to approve"),
|
|
44635
|
+
spender: import_zod10.z.string().describe("Spender address to approve"),
|
|
44636
|
+
amount: import_zod10.z.string().describe('Amount to approve (in smallest unit, or "unlimited")')
|
|
44403
44637
|
}),
|
|
44404
44638
|
invoke: async (_ctx, input2) => {
|
|
44405
|
-
|
|
44406
|
-
|
|
44639
|
+
const wallet = this.walletContext.activeWallet();
|
|
44640
|
+
const { coinId, contractAddress } = parseTokenId(input2.assetId);
|
|
44641
|
+
if (!contractAddress) {
|
|
44642
|
+
throw new WalletError("VALIDATION_ERROR", "Cannot approve native token (no contract).");
|
|
44407
44643
|
}
|
|
44408
|
-
|
|
44409
|
-
|
|
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") {
|
|
44650
|
+
throw new WalletError(
|
|
44651
|
+
"CHAIN_UNSUPPORTED",
|
|
44652
|
+
`approve_token on ${chainKey} is not supported \u2014 ERC-20 approvals are EVM-only.`
|
|
44653
|
+
);
|
|
44410
44654
|
}
|
|
44411
|
-
const hdWallet = this.agentWallet.getSigner().getHDWallet();
|
|
44412
44655
|
const amount = input2.amount === "unlimited" ? "115792089237316195423570985008687907853269984665640564039457584007913129639935" : input2.amount;
|
|
44413
|
-
const result = await
|
|
44414
|
-
|
|
44415
|
-
spender: input2.spender,
|
|
44416
|
-
amount,
|
|
44417
|
-
hdWallet
|
|
44418
|
-
});
|
|
44419
|
-
return { hash: result.hash, chainKey: result.chainKey };
|
|
44656
|
+
const result = await wallet.approveErc20(chainKey, contractAddress, input2.spender, amount);
|
|
44657
|
+
return { hash: result.txHash, chainKey };
|
|
44420
44658
|
}
|
|
44421
44659
|
};
|
|
44422
44660
|
}
|
|
@@ -44424,27 +44662,31 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44424
44662
|
return {
|
|
44425
44663
|
name: "revoke_token_approval",
|
|
44426
44664
|
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. Applies only to ERC-20 tokens (asset IDs of form "c{coinId}_t{contract}"); native tokens have no approvals to revoke. This is the inverse of approve_token.',
|
|
44427
|
-
schema:
|
|
44428
|
-
assetId:
|
|
44665
|
+
schema: import_zod10.z.object({
|
|
44666
|
+
assetId: import_zod10.z.string().describe(
|
|
44429
44667
|
'ERC-20 token ID to revoke approval for (e.g., "c60_t0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" for USDC on Ethereum)'
|
|
44430
44668
|
),
|
|
44431
|
-
spender:
|
|
44669
|
+
spender: import_zod10.z.string().describe("Spender address whose allowance should be set to 0")
|
|
44432
44670
|
}),
|
|
44433
44671
|
invoke: async (_ctx, input2) => {
|
|
44434
|
-
|
|
44435
|
-
|
|
44672
|
+
const wallet = this.walletContext.activeWallet();
|
|
44673
|
+
const { coinId, contractAddress } = parseTokenId(input2.assetId);
|
|
44674
|
+
if (!contractAddress) {
|
|
44675
|
+
throw new WalletError("VALIDATION_ERROR", "Native tokens have no approval to revoke.");
|
|
44436
44676
|
}
|
|
44437
|
-
|
|
44438
|
-
|
|
44677
|
+
const chainKey = coinTypeToChainKey(coinId);
|
|
44678
|
+
if (!chainKey) {
|
|
44679
|
+
throw new WalletError("CHAIN_UNSUPPORTED", `Unsupported coin type: ${coinId}`);
|
|
44439
44680
|
}
|
|
44440
|
-
const
|
|
44441
|
-
|
|
44442
|
-
|
|
44443
|
-
|
|
44444
|
-
|
|
44445
|
-
|
|
44446
|
-
}
|
|
44447
|
-
|
|
44681
|
+
const chain2 = getChainById(chainKey);
|
|
44682
|
+
if (!chain2 || chain2.namespace !== "eip155") {
|
|
44683
|
+
throw new WalletError(
|
|
44684
|
+
"CHAIN_UNSUPPORTED",
|
|
44685
|
+
`revoke_token_approval on ${chainKey} is not supported \u2014 ERC-20 approvals are EVM-only.`
|
|
44686
|
+
);
|
|
44687
|
+
}
|
|
44688
|
+
const result = await wallet.approveErc20(chainKey, contractAddress, input2.spender, "0");
|
|
44689
|
+
return { hash: result.txHash, chainKey };
|
|
44448
44690
|
}
|
|
44449
44691
|
};
|
|
44450
44692
|
}
|
|
@@ -44452,10 +44694,10 @@ var TokenActionProvider = class extends ActionProvider {
|
|
|
44452
44694
|
return {
|
|
44453
44695
|
name: "check_allowance",
|
|
44454
44696
|
description: "Check the token allowance for a spender address",
|
|
44455
|
-
schema:
|
|
44456
|
-
assetId:
|
|
44457
|
-
owner:
|
|
44458
|
-
spender:
|
|
44697
|
+
schema: import_zod10.z.object({
|
|
44698
|
+
assetId: import_zod10.z.string().describe("Token ID to check allowance for"),
|
|
44699
|
+
owner: import_zod10.z.string().describe("Token owner address"),
|
|
44700
|
+
spender: import_zod10.z.string().describe("Spender address to check")
|
|
44459
44701
|
}),
|
|
44460
44702
|
invoke: async (_ctx, input2) => {
|
|
44461
44703
|
if (!this.blockchainClient) {
|
|
@@ -44493,8 +44735,8 @@ var MarketActionProvider = class extends ActionProvider {
|
|
|
44493
44735
|
return {
|
|
44494
44736
|
name: "get_trending_tokens",
|
|
44495
44737
|
description: "Get a list of trending tokens by market activity",
|
|
44496
|
-
schema:
|
|
44497
|
-
limit:
|
|
44738
|
+
schema: import_zod11.z.object({
|
|
44739
|
+
limit: import_zod11.z.coerce.number().optional().default(20).describe("Number of tokens to return (default 20)")
|
|
44498
44740
|
}),
|
|
44499
44741
|
invoke: async (ctx, input2) => {
|
|
44500
44742
|
const tokens = await this.getService(ctx).getAssetListings({ limit: input2.limit });
|
|
@@ -44506,8 +44748,8 @@ var MarketActionProvider = class extends ActionProvider {
|
|
|
44506
44748
|
return {
|
|
44507
44749
|
name: "check_token_risk",
|
|
44508
44750
|
description: "Check security and risk information for a token (honeypot detection, audit status, freeze authority)",
|
|
44509
|
-
schema:
|
|
44510
|
-
assetId:
|
|
44751
|
+
schema: import_zod11.z.object({
|
|
44752
|
+
assetId: import_zod11.z.string().describe("Asset ID (e.g., c60_t0xA0b8... for ERC-20, c60 for native)")
|
|
44511
44753
|
}),
|
|
44512
44754
|
invoke: async (ctx, input2) => {
|
|
44513
44755
|
const status = await this.getService(ctx).checkCoinStatus(input2.assetId);
|
|
@@ -44613,9 +44855,9 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44613
44855
|
// TransferActionProvider for the off-ramp payout path (confirm_offramp_transfer).
|
|
44614
44856
|
// Off-ramp sends crypto to a third-party deposit address, so the cap is
|
|
44615
44857
|
// strictly more important here than for peer-to-peer transfers.
|
|
44616
|
-
constructor(
|
|
44858
|
+
constructor(walletContext, priceLookup, maxTransferUsd = DEFAULT_MAX_OFFRAMP_USD) {
|
|
44617
44859
|
super();
|
|
44618
|
-
this.
|
|
44860
|
+
this.walletContext = walletContext;
|
|
44619
44861
|
this.priceLookup = priceLookup;
|
|
44620
44862
|
this.maxTransferUsd = maxTransferUsd;
|
|
44621
44863
|
}
|
|
@@ -44632,12 +44874,6 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44632
44874
|
}
|
|
44633
44875
|
async resolveAddress(providedAddress, cryptoAsset) {
|
|
44634
44876
|
if (providedAddress) return providedAddress;
|
|
44635
|
-
if (!this.walletProvider) {
|
|
44636
|
-
throw new TransferError(
|
|
44637
|
-
"WALLET_NOT_FOUND",
|
|
44638
|
-
"walletAddress not provided and no wallet provider is configured. Pass walletAddress explicitly or configure an agent wallet."
|
|
44639
|
-
);
|
|
44640
|
-
}
|
|
44641
44877
|
if (!cryptoAsset) {
|
|
44642
44878
|
throw new TransferError(
|
|
44643
44879
|
"VALIDATION_ERROR",
|
|
@@ -44651,19 +44887,19 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44651
44887
|
`Cannot determine chain for asset ${cryptoAsset}. Pass walletAddress explicitly.`
|
|
44652
44888
|
);
|
|
44653
44889
|
}
|
|
44654
|
-
return this.
|
|
44890
|
+
return this.walletContext.activeWallet().getAddress(chainKey);
|
|
44655
44891
|
}
|
|
44656
44892
|
getOnrampQuote() {
|
|
44657
44893
|
return {
|
|
44658
44894
|
name: "get_onramp_quote",
|
|
44659
44895
|
description: "Step 1 of buying crypto with fiat: get quotes from available payment providers (MoonPay, Transak, etc.) for a given fiat amount. Returns an array of quotes each with a quoteId \u2014 pass one to get_onramp_url to generate the checkout redirect.",
|
|
44660
|
-
schema:
|
|
44661
|
-
fiatAmount:
|
|
44662
|
-
fiatCurrency:
|
|
44663
|
-
cryptoAsset:
|
|
44896
|
+
schema: import_zod12.z.object({
|
|
44897
|
+
fiatAmount: import_zod12.z.coerce.number().positive().describe("Amount of fiat currency to spend (e.g., 100 for $100)."),
|
|
44898
|
+
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
|
+
cryptoAsset: import_zod12.z.string().describe(
|
|
44664
44900
|
"Target crypto asset ID. Format: c<coinId> for native coins (e.g., c60 for ETH, c0 for BTC, c501 for SOL) or c<coinId>_t<contract> for tokens (e.g., c60_t0xA0b8... for USDC on Ethereum)."
|
|
44665
44901
|
),
|
|
44666
|
-
walletAddress:
|
|
44902
|
+
walletAddress: import_zod12.z.string().optional().describe(
|
|
44667
44903
|
"Destination wallet address where the purchased crypto will be sent. OMIT to auto-derive from the configured wallet using cryptoAsset's 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."
|
|
44668
44904
|
)
|
|
44669
44905
|
}),
|
|
@@ -44697,12 +44933,12 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44697
44933
|
return {
|
|
44698
44934
|
name: "get_onramp_url",
|
|
44699
44935
|
description: "Step 2 of buying crypto with fiat: exchange a quoteId from get_onramp_quote for a browser redirect URL. The user opens that URL to complete KYC and payment with the provider; on success, the provider sends the purchased crypto to the destination address. After this tool returns a URL, poll the balance tool named in the `followUp` field to observe the incoming funds \u2014 there is no background polling, webhook, or push notification.",
|
|
44700
|
-
schema:
|
|
44701
|
-
quoteId:
|
|
44702
|
-
walletAddress:
|
|
44936
|
+
schema: import_zod12.z.object({
|
|
44937
|
+
quoteId: import_zod12.z.string().describe("Quote ID returned by get_onramp_quote."),
|
|
44938
|
+
walletAddress: import_zod12.z.string().optional().describe(
|
|
44703
44939
|
"Destination wallet address. OMIT to auto-derive from the configured wallet \u2014 requires cryptoAsset so the chain can be determined. Only supply this when operating on behalf of a different user."
|
|
44704
44940
|
),
|
|
44705
|
-
cryptoAsset:
|
|
44941
|
+
cryptoAsset: import_zod12.z.string().optional().describe(
|
|
44706
44942
|
"Crypto asset ID that was used for the quote (e.g., c60 for ETH). Required when walletAddress is omitted so the chain can be derived for address lookup."
|
|
44707
44943
|
)
|
|
44708
44944
|
}),
|
|
@@ -44745,16 +44981,16 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44745
44981
|
return {
|
|
44746
44982
|
name: "get_offramp_quote",
|
|
44747
44983
|
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.",
|
|
44748
|
-
schema:
|
|
44749
|
-
cryptoAmount:
|
|
44750
|
-
cryptoAsset:
|
|
44984
|
+
schema: import_zod12.z.object({
|
|
44985
|
+
cryptoAmount: import_zod12.z.coerce.number().positive().describe("Amount of crypto to sell (e.g., 0.1 for 0.1 ETH)."),
|
|
44986
|
+
cryptoAsset: import_zod12.z.string().describe(
|
|
44751
44987
|
"Crypto asset ID to sell. Format: c<coinId> for native coins (e.g., c60 for ETH, c0 for BTC) or c<coinId>_t<contract> for tokens."
|
|
44752
44988
|
),
|
|
44753
|
-
fiatCurrency:
|
|
44754
|
-
paymentMethod:
|
|
44989
|
+
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
|
+
paymentMethod: import_zod12.z.enum(["ANY", "bank_transfer", "card"]).optional().default("ANY").describe(
|
|
44755
44991
|
'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.'
|
|
44756
44992
|
),
|
|
44757
|
-
walletAddress:
|
|
44993
|
+
walletAddress: import_zod12.z.string().optional().describe(
|
|
44758
44994
|
"Source wallet address holding the crypto to be sold. OMIT to auto-derive from the configured wallet using cryptoAsset's chain \u2014 this is the safe default. Only supply this when operating on behalf of a different user."
|
|
44759
44995
|
)
|
|
44760
44996
|
}),
|
|
@@ -44797,12 +45033,12 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44797
45033
|
return {
|
|
44798
45034
|
name: "get_offramp_url",
|
|
44799
45035
|
description: "Step 2 of selling crypto for fiat: exchange a quoteId from get_offramp_quote for a browser checkout URL. The user opens that URL to complete KYC and order creation with the provider; the provider then displays a deposit address (plus a memo/tag on chains that require one) and the exact amount to send. The agent must then call confirm_offramp_transfer with those values to sign and broadcast the payout natively \u2014 the browser has no access to wallet keys and cannot sign the transfer itself. After the payout broadcasts, poll the balance tool named in the `followUp` field to observe the outgoing tx. Fiat payout arrives separately via the provider.",
|
|
44800
|
-
schema:
|
|
44801
|
-
quoteId:
|
|
44802
|
-
walletAddress:
|
|
45036
|
+
schema: import_zod12.z.object({
|
|
45037
|
+
quoteId: import_zod12.z.string().describe("Quote ID returned by get_offramp_quote."),
|
|
45038
|
+
walletAddress: import_zod12.z.string().optional().describe(
|
|
44803
45039
|
"Source wallet address. OMIT to auto-derive from the configured wallet \u2014 requires cryptoAsset so the chain can be determined. Only supply this when operating on behalf of a different user."
|
|
44804
45040
|
),
|
|
44805
|
-
cryptoAsset:
|
|
45041
|
+
cryptoAsset: import_zod12.z.string().optional().describe(
|
|
44806
45042
|
"Crypto asset ID that was used for the quote (e.g., c60 for ETH). Required when walletAddress is omitted so the chain can be derived for address lookup."
|
|
44807
45043
|
)
|
|
44808
45044
|
}),
|
|
@@ -44846,31 +45082,28 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44846
45082
|
return {
|
|
44847
45083
|
name: "confirm_offramp_transfer",
|
|
44848
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 for BTC / Solana / Cosmos off-ramps use the `twak onramp sell-confirm` CLI command instead. Returns a tx hash on success; fiat payout from the provider then arrives separately (no on-chain signal for that step).",
|
|
44849
|
-
schema:
|
|
44850
|
-
depositAddress:
|
|
45085
|
+
schema: import_zod12.z.object({
|
|
45086
|
+
depositAddress: import_zod12.z.string().describe(
|
|
44851
45087
|
"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."
|
|
44852
45088
|
),
|
|
44853
|
-
cryptoAmount:
|
|
45089
|
+
cryptoAmount: import_zod12.z.string().describe(
|
|
44854
45090
|
'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.'
|
|
44855
45091
|
),
|
|
44856
|
-
cryptoAsset:
|
|
45092
|
+
cryptoAsset: import_zod12.z.string().describe(
|
|
44857
45093
|
"Crypto asset being sold. Format: c<coinId> for native coins (e.g., c60 for ETH) or c<coinId>_t<contract> for ERC-20 tokens."
|
|
44858
45094
|
),
|
|
44859
|
-
decimals:
|
|
45095
|
+
decimals: import_zod12.z.coerce.number().optional().describe(
|
|
44860
45096
|
"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."
|
|
44861
45097
|
),
|
|
44862
|
-
memo:
|
|
45098
|
+
memo: import_zod12.z.string().optional().describe(
|
|
44863
45099
|
"Memo / destination tag. Not used on EVM chains, so supplying one on EVM is a no-op warning. Exists in the schema for forward-compat with non-EVM chains."
|
|
44864
45100
|
),
|
|
44865
|
-
quoteId:
|
|
45101
|
+
quoteId: import_zod12.z.string().optional().describe(
|
|
44866
45102
|
"Quote ID from get_offramp_quote. Optional \u2014 included only for traceability in the response; tx details come from depositAddress / cryptoAmount."
|
|
44867
45103
|
)
|
|
44868
45104
|
}),
|
|
44869
45105
|
invoke: async (_ctx, input2) => {
|
|
44870
|
-
const signer = this.
|
|
44871
|
-
if (!signer) {
|
|
44872
|
-
throw new TransferError("WALLET_NOT_FOUND", "No wallet provider configured");
|
|
44873
|
-
}
|
|
45106
|
+
const signer = this.walletContext.activeWallet();
|
|
44874
45107
|
let parsed;
|
|
44875
45108
|
try {
|
|
44876
45109
|
parsed = parseTokenId(input2.cryptoAsset);
|
|
@@ -44973,11 +45206,11 @@ var OnrampActionProvider = class extends ActionProvider {
|
|
|
44973
45206
|
}
|
|
44974
45207
|
};
|
|
44975
45208
|
var AutomateActionProvider = class extends ActionProvider {
|
|
44976
|
-
constructor(automationStore, swapClient,
|
|
45209
|
+
constructor(automationStore, swapClient, walletContext, blockchainClient) {
|
|
44977
45210
|
super();
|
|
44978
45211
|
this.automationStore = automationStore;
|
|
44979
45212
|
this.swapClient = swapClient;
|
|
44980
|
-
this.
|
|
45213
|
+
this.walletContext = walletContext;
|
|
44981
45214
|
this.blockchainClient = blockchainClient;
|
|
44982
45215
|
}
|
|
44983
45216
|
name = "automate";
|
|
@@ -44994,15 +45227,15 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
44994
45227
|
return {
|
|
44995
45228
|
name: "create_automation",
|
|
44996
45229
|
description: "Create a DCA (recurring buy) or limit order (buy/sell when price condition is met)",
|
|
44997
|
-
schema:
|
|
44998
|
-
type:
|
|
44999
|
-
fromToken:
|
|
45000
|
-
toToken:
|
|
45001
|
-
chain:
|
|
45002
|
-
amount:
|
|
45003
|
-
intervalMs:
|
|
45004
|
-
targetPrice:
|
|
45005
|
-
condition:
|
|
45230
|
+
schema: import_zod13.z.object({
|
|
45231
|
+
type: import_zod13.z.enum(["dca", "limit"]).describe("Automation type: dca (recurring) or limit (price trigger)"),
|
|
45232
|
+
fromToken: import_zod13.z.string().describe("Source token symbol or address"),
|
|
45233
|
+
toToken: import_zod13.z.string().describe("Destination token symbol or address"),
|
|
45234
|
+
chain: import_zod13.z.string().default("ethereum").describe("Chain name"),
|
|
45235
|
+
amount: import_zod13.z.string().describe("Amount of fromToken per execution"),
|
|
45236
|
+
intervalMs: import_zod13.z.coerce.number().optional().describe("Interval in milliseconds for DCA (e.g., 86400000 for daily)"),
|
|
45237
|
+
targetPrice: import_zod13.z.coerce.number().optional().describe("Target price in USD for limit orders"),
|
|
45238
|
+
condition: import_zod13.z.enum(["above", "below"]).optional().describe("Price condition for limit orders")
|
|
45006
45239
|
}),
|
|
45007
45240
|
invoke: async (_ctx, input2) => {
|
|
45008
45241
|
if (input2.type === "dca" && !input2.intervalMs) {
|
|
@@ -45038,8 +45271,8 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45038
45271
|
return {
|
|
45039
45272
|
name: "list_automations",
|
|
45040
45273
|
description: "List all active automation rules (DCA and limit orders)",
|
|
45041
|
-
schema:
|
|
45042
|
-
activeOnly:
|
|
45274
|
+
schema: import_zod13.z.object({
|
|
45275
|
+
activeOnly: import_zod13.z.boolean().optional().default(true).describe("Only show active automations")
|
|
45043
45276
|
}),
|
|
45044
45277
|
invoke: async (_ctx, input2) => {
|
|
45045
45278
|
const all = await this.automationStore.list();
|
|
@@ -45052,8 +45285,8 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45052
45285
|
return {
|
|
45053
45286
|
name: "delete_automation",
|
|
45054
45287
|
description: "Delete (deactivate) an automation rule",
|
|
45055
|
-
schema:
|
|
45056
|
-
id:
|
|
45288
|
+
schema: import_zod13.z.object({
|
|
45289
|
+
id: import_zod13.z.string().describe("Automation ID to delete")
|
|
45057
45290
|
}),
|
|
45058
45291
|
invoke: async (_ctx, input2) => {
|
|
45059
45292
|
const deleted = await this.automationStore.delete(input2.id);
|
|
@@ -45109,16 +45342,17 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45109
45342
|
return {
|
|
45110
45343
|
name: "run_automation_now",
|
|
45111
45344
|
description: "Manually execute an automation immediately (runs the swap now regardless of schedule)",
|
|
45112
|
-
schema:
|
|
45113
|
-
id:
|
|
45345
|
+
schema: import_zod13.z.object({
|
|
45346
|
+
id: import_zod13.z.string().describe("Automation ID to run")
|
|
45114
45347
|
}),
|
|
45115
45348
|
invoke: async (_ctx, input2) => {
|
|
45116
|
-
if (!this.
|
|
45349
|
+
if (!this.swapClient) {
|
|
45117
45350
|
throw new AutomationError(
|
|
45118
|
-
"
|
|
45119
|
-
"
|
|
45351
|
+
"VALIDATION_ERROR",
|
|
45352
|
+
"Swap client required to execute automation"
|
|
45120
45353
|
);
|
|
45121
45354
|
}
|
|
45355
|
+
const walletProvider = this.walletContext.activeWallet();
|
|
45122
45356
|
const record2 = await this.automationStore.getById(input2.id);
|
|
45123
45357
|
if (!record2) {
|
|
45124
45358
|
return {
|
|
@@ -45151,7 +45385,7 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45151
45385
|
};
|
|
45152
45386
|
}
|
|
45153
45387
|
const parsedAmount = parseAmount(record2.amount, fromTokenInfo.decimals);
|
|
45154
|
-
const address = await
|
|
45388
|
+
const address = await walletProvider.getAddress(record2.chain);
|
|
45155
45389
|
const quote = await this.swapClient.getQuote({
|
|
45156
45390
|
fromChain: record2.chain,
|
|
45157
45391
|
fromToken: fromTokenInfo.address,
|
|
@@ -45168,7 +45402,7 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45168
45402
|
const transactions = await this.swapClient.getSwapTransactions(route);
|
|
45169
45403
|
let txHash = "";
|
|
45170
45404
|
for (const txData of transactions) {
|
|
45171
|
-
txHash = await this.sendSwapTx(txData, chainConfig,
|
|
45405
|
+
txHash = await this.sendSwapTx(txData, chainConfig, walletProvider);
|
|
45172
45406
|
const isApproval = txData.transaction.type === "approval" || txData.transaction.type === "revokeApproval";
|
|
45173
45407
|
await this.pollConfirmation(record2.chain, address, txHash, isApproval ? 6e4 : 12e4);
|
|
45174
45408
|
}
|
|
@@ -45186,9 +45420,9 @@ var AutomateActionProvider = class extends ActionProvider {
|
|
|
45186
45420
|
}
|
|
45187
45421
|
};
|
|
45188
45422
|
var X402ActionProvider = class extends ActionProvider {
|
|
45189
|
-
constructor(
|
|
45423
|
+
constructor(walletContext) {
|
|
45190
45424
|
super();
|
|
45191
|
-
this.
|
|
45425
|
+
this.walletContext = walletContext;
|
|
45192
45426
|
}
|
|
45193
45427
|
name = "x402";
|
|
45194
45428
|
description = "HTTP requests to x402 payment-gated endpoints \u2014 pay and fetch in one call";
|
|
@@ -45198,13 +45432,13 @@ var X402ActionProvider = class extends ActionProvider {
|
|
|
45198
45432
|
x402Quote() {
|
|
45199
45433
|
return {
|
|
45200
45434
|
name: "x402_quote",
|
|
45201
|
-
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). 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.
|
|
45202
|
-
schema:
|
|
45203
|
-
url:
|
|
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(chainKey)` to find the wallet address on that chain, then `get_balance` for the entry\'s asset (or `wallet_balance(chainKey)` 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
|
+
schema: import_zod14.z.object({
|
|
45437
|
+
url: import_zod14.z.string().describe(
|
|
45204
45438
|
"Target endpoint URL. Must be HTTPS. Resolves via DNS; private/local IPs are rejected."
|
|
45205
45439
|
),
|
|
45206
|
-
method:
|
|
45207
|
-
body:
|
|
45440
|
+
method: import_zod14.z.string().optional().default("GET").describe("HTTP verb (GET, POST, PUT, ...). Case-insensitive."),
|
|
45441
|
+
body: import_zod14.z.string().optional().describe(
|
|
45208
45442
|
"Pre-serialized request body. For JSON payloads, pass JSON.stringify(...) on the caller side."
|
|
45209
45443
|
)
|
|
45210
45444
|
}),
|
|
@@ -45214,35 +45448,33 @@ var X402ActionProvider = class extends ActionProvider {
|
|
|
45214
45448
|
x402Request() {
|
|
45215
45449
|
return {
|
|
45216
45450
|
name: "x402_request",
|
|
45217
|
-
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
|
|
45218
|
-
schema:
|
|
45219
|
-
url:
|
|
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(chainKey)` to find the wallet address, then `get_balance` for the asset (or `wallet_balance(chainKey)` 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
|
+
schema: import_zod14.z.object({
|
|
45453
|
+
url: import_zod14.z.string().describe(
|
|
45220
45454
|
"Target endpoint URL. Must be HTTPS. Resolves via DNS; private/local IPs are rejected before any payment."
|
|
45221
45455
|
),
|
|
45222
|
-
method:
|
|
45223
|
-
body:
|
|
45456
|
+
method: import_zod14.z.string().optional().default("GET").describe("HTTP verb (GET, POST, PUT, ...). Case-insensitive."),
|
|
45457
|
+
body: import_zod14.z.string().optional().describe(
|
|
45224
45458
|
"Pre-serialized request body. For JSON payloads, pass JSON.stringify(...) on the caller side."
|
|
45225
45459
|
),
|
|
45226
|
-
maxPaymentAtomic:
|
|
45460
|
+
maxPaymentAtomic: import_zod14.z.string().describe(
|
|
45227
45461
|
`Hard cap on payment amount in the asset's smallest atomic unit (integer string, e.g. "10000" = 0.01 USDC at 6 decimals). If the server demands more than this cap, the call returns code: 'PAYMENT_AMOUNT_EXCEEDED' rather than overpaying. The user has pre-committed to this cap \u2014 do NOT raise it without asking the user.`
|
|
45228
45462
|
),
|
|
45229
|
-
autoApprove:
|
|
45463
|
+
autoApprove: import_zod14.z.boolean().optional().default(true).describe(
|
|
45230
45464
|
"When true (default), silently grants an UNLIMITED Permit2 allowance if the endpoint requires it \u2014 DANGEROUS, see SAFETY in the action description. Defaults true because MCP agents cannot interact with a prompt, but you MUST consult the user before relying on this on any endpoint you have not paid before. Set to false to surface code: 'PERMIT2_APPROVAL_REQUIRED' instead and route approval out-of-band."
|
|
45231
45465
|
),
|
|
45232
|
-
preferNetwork:
|
|
45466
|
+
preferNetwork: import_zod14.z.string().optional().describe(
|
|
45233
45467
|
`Optional override: restrict payment to this network when the endpoint offers multiple. Accepts either CAIP-2 ("eip155:8453") or a chain key ("base"). Case-insensitive. Use this when the user has a balance on a specific chain \u2014 without it, the picker chooses based on transfer-method preference (EIP-3009 over Permit2), which can land on a chain where the wallet has no funds. If no offered entry matches, the call returns code: 'NO_MATCHING_ROUTE' with the list of offered networks so you can re-ask the user.`
|
|
45234
45468
|
),
|
|
45235
|
-
preferMethod:
|
|
45469
|
+
preferMethod: import_zod14.z.string().optional().describe(
|
|
45236
45470
|
`Optional override: restrict payment to this transfer method ("eip3009", "permit2", or "permit2-exact"). Case-insensitive. Use this to force the gasless route (eip3009) or to insist on a specific Permit2 variant. If no offered entry matches, the call returns code: 'NO_MATCHING_ROUTE'.`
|
|
45237
45471
|
),
|
|
45238
|
-
preferAsset:
|
|
45472
|
+
preferAsset: import_zod14.z.string().optional().describe(
|
|
45239
45473
|
"Optional override: restrict payment to entries matching this asset. Matches the entry's `asset` field (contract address, exact, case-insensitive) or its `tokenName` field (substring, case-insensitive). Pass the exact value from the quote's output. Returns code: 'NO_MATCHING_ROUTE' when nothing matches."
|
|
45240
45474
|
)
|
|
45241
45475
|
}),
|
|
45242
45476
|
invoke: async (_ctx, input2) => {
|
|
45243
|
-
|
|
45244
|
-
throw new WalletError("WALLET_NOT_FOUND", "Agent wallet required for x402 payments");
|
|
45245
|
-
}
|
|
45477
|
+
const provider = this.walletContext.activeWallet();
|
|
45246
45478
|
try {
|
|
45247
45479
|
const result = await payAndFetch({
|
|
45248
45480
|
url: input2.url,
|
|
@@ -45252,8 +45484,13 @@ var X402ActionProvider = class extends ActionProvider {
|
|
|
45252
45484
|
preferNetwork: input2.preferNetwork,
|
|
45253
45485
|
preferMethod: input2.preferMethod,
|
|
45254
45486
|
preferAsset: input2.preferAsset,
|
|
45255
|
-
provider
|
|
45256
|
-
// maxPaymentAtomic is the cap; no per-call prompt for MCP.
|
|
45487
|
+
provider,
|
|
45488
|
+
// maxPaymentAtomic is the cap; no per-call prompt for MCP. The
|
|
45489
|
+
// expectation is the LLM ran x402_quote first, presented the
|
|
45490
|
+
// menu to the user, and is now calling x402_request with the
|
|
45491
|
+
// user-picked (chain, asset). Token-substitution risk is
|
|
45492
|
+
// accepted at the MCP layer — the CLI path has a readline
|
|
45493
|
+
// confirm that surfaces the contract address as the safety net.
|
|
45257
45494
|
confirm: async () => true,
|
|
45258
45495
|
// Auto-true when autoApprove=true; omit (rejects) otherwise.
|
|
45259
45496
|
confirmApproval: input2.autoApprove ? async () => true : void 0
|
|
@@ -45276,6 +45513,80 @@ var X402ActionProvider = class extends ActionProvider {
|
|
|
45276
45513
|
};
|
|
45277
45514
|
}
|
|
45278
45515
|
};
|
|
45516
|
+
var WalletModeActionProvider = class extends ActionProvider {
|
|
45517
|
+
name = "wallet-mode";
|
|
45518
|
+
description = "Switch wallet modes mid-session. The local agent wallet is auto-bound at server startup when configured, so signing actions work out of the box without calling this. Only call switch_wallet_mode when the user explicitly asks to use WalletConnect (or to return to the local wallet afterwards).";
|
|
45519
|
+
walletContext;
|
|
45520
|
+
agentWallet;
|
|
45521
|
+
walletConnectFactory;
|
|
45522
|
+
constructor(options) {
|
|
45523
|
+
super();
|
|
45524
|
+
this.walletContext = options.walletContext;
|
|
45525
|
+
this.agentWallet = options.agentWallet;
|
|
45526
|
+
this.walletConnectFactory = options.walletConnectFactory;
|
|
45527
|
+
}
|
|
45528
|
+
getActions() {
|
|
45529
|
+
return [this.switchWalletMode(), this.getWalletStatus()];
|
|
45530
|
+
}
|
|
45531
|
+
/**
|
|
45532
|
+
* Builds the MCP response for a snapshot. When state is `wc-pairing` and
|
|
45533
|
+
* a QR code is available, emits a text block + an image block so MCP
|
|
45534
|
+
* clients (Claude Desktop etc.) render the QR inline. For other states,
|
|
45535
|
+
* emits a single text block with the JSON snapshot.
|
|
45536
|
+
*/
|
|
45537
|
+
snapshotResponse(snapshot) {
|
|
45538
|
+
const textPayload = snapshot.pairing ? { ...snapshot, pairing: { ...snapshot.pairing, qrCodeImageUrl: void 0 } } : snapshot;
|
|
45539
|
+
const content = [{ type: "text", text: JSON.stringify(textPayload, null, 2) }];
|
|
45540
|
+
if (snapshot.pairing?.qrCodeImageUrl) {
|
|
45541
|
+
const match = snapshot.pairing.qrCodeImageUrl.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
45542
|
+
if (match) {
|
|
45543
|
+
const [, mimeType, base642] = match;
|
|
45544
|
+
content.push({ type: "image", data: base642, mimeType });
|
|
45545
|
+
}
|
|
45546
|
+
}
|
|
45547
|
+
return new McpResponse(content);
|
|
45548
|
+
}
|
|
45549
|
+
switchWalletMode() {
|
|
45550
|
+
return {
|
|
45551
|
+
name: "switch_wallet_mode",
|
|
45552
|
+
description: 'Switch the active wallet mode for this MCP server session. IMPORTANT: the LOCAL agent wallet is auto-bound at server startup when one is configured, so most users do NOT need to call this \u2014 signing actions just work. Call this ONLY when the user explicitly asks to switch. Two flows: (a) user wants to sign on their phone via Trust Wallet (or any WC-compatible wallet) \u2192 mode="walletconnect"; (b) user wants to switch back from WalletConnect to the local wallet \u2192 mode="local". \n\nDISCOVERABILITY: most users do not know WalletConnect is an option. When the user first asks about wallet operations or signing in a fresh conversation, briefly mention that they can optionally switch to signing via Trust Wallet on their phone (more secure: keys never leave their device) by saying so \u2014 then call switch_wallet_mode if they opt in. Do not push it on every signing call; just surface it once early on or when the user expresses security concerns.\n\nFor mode="walletconnect": returns a pairing payload (URI, mobile deep link, inline QR image). Surface the QR to the user, then poll get_wallet_status until state="wc-connected" before retrying any signing action. Do NOT call switch_wallet_mode while a signing action is in flight: switching disconnects the prior WC relay session, which can break the in-flight signature; wait for the current action to settle before changing modes.',
|
|
45553
|
+
schema: import_zod15.z.object({
|
|
45554
|
+
mode: import_zod15.z.enum(["local", "walletconnect"]).describe(
|
|
45555
|
+
'"local" for the on-disk agent wallet, "walletconnect" to pair a remote wallet'
|
|
45556
|
+
)
|
|
45557
|
+
}),
|
|
45558
|
+
invoke: async (_ctx, input2) => {
|
|
45559
|
+
if (input2.mode === "local") {
|
|
45560
|
+
if (!this.agentWallet) {
|
|
45561
|
+
throw new WalletError(
|
|
45562
|
+
"WALLET_NOT_FOUND",
|
|
45563
|
+
"No local agent wallet found. Create one with `twak init` (CLI), then restart the MCP client (e.g. Claude Desktop)."
|
|
45564
|
+
);
|
|
45565
|
+
}
|
|
45566
|
+
await this.walletContext.bindLocal(this.agentWallet);
|
|
45567
|
+
return this.snapshotResponse(this.walletContext.snapshot());
|
|
45568
|
+
}
|
|
45569
|
+
if (!this.walletConnectFactory) {
|
|
45570
|
+
throw new WalletError(
|
|
45571
|
+
"VALIDATION_ERROR",
|
|
45572
|
+
"WalletConnect is not configured. Set WALLETCONNECT_PROJECT_ID (env var or `twak init --wc-project-id <id>`) and restart the MCP server."
|
|
45573
|
+
);
|
|
45574
|
+
}
|
|
45575
|
+
const wc2 = this.walletConnectFactory();
|
|
45576
|
+
await this.walletContext.beginWalletConnect(wc2);
|
|
45577
|
+
return this.snapshotResponse(this.walletContext.snapshot());
|
|
45578
|
+
}
|
|
45579
|
+
};
|
|
45580
|
+
}
|
|
45581
|
+
getWalletStatus() {
|
|
45582
|
+
return {
|
|
45583
|
+
name: "get_wallet_status",
|
|
45584
|
+
description: 'Returns the current wallet binding for this MCP server session: state ("unbound" | "local" | "wc-pairing" | "wc-connected"), wallet type (when bound), peer name (when wc-connected), and pairing data (URI / deep link / QR code, when state="wc-pairing"). When state="wc-pairing", the response includes an inline QR-code image (MCP image content), a mobile deep link (`pairing.deepLink`) for users on their phone, and the raw `pairing.uri` as a copyable string for advanced users. Poll this after switch_wallet_mode({mode: "walletconnect"}) until state transitions to "wc-connected"; that\'s when signing actions become usable. If the user asks to see the QR code again (e.g. "show me the QR", "I can\'t see it"), call this action again \u2014 every call while state="wc-pairing" re-emits the inline QR image. Do NOT try to render the QR yourself from the URI or the response JSON.',
|
|
45585
|
+
schema: import_zod15.z.object({}),
|
|
45586
|
+
invoke: async () => this.snapshotResponse(this.walletContext.snapshot())
|
|
45587
|
+
};
|
|
45588
|
+
}
|
|
45589
|
+
};
|
|
45279
45590
|
|
|
45280
45591
|
// src/commands/serve.ts
|
|
45281
45592
|
init_dist2();
|
|
@@ -49915,6 +50226,15 @@ var import_http = require("http");
|
|
|
49915
50226
|
var import_crypto7 = require("crypto");
|
|
49916
50227
|
init_dist2();
|
|
49917
50228
|
init_dist();
|
|
50229
|
+
var SERVER_INSTRUCTIONS = `twak signs blockchain operations through one of two wallet modes:
|
|
50230
|
+
|
|
50231
|
+
LOCAL (default): an HD wallet auto-bound at server startup. In-process signing, no user-side prompts.
|
|
50232
|
+
|
|
50233
|
+
WALLETCONNECT: a remote wallet (e.g. Trust Wallet on phone) paired via WC v2. Each signing action prompts the user on their device. More secure: the private key never leaves the user's device.
|
|
50234
|
+
|
|
50235
|
+
When the user first engages with signing operations in a fresh conversation, briefly mention that they can optionally switch to phone-based signing via \`switch_wallet_mode({mode: "walletconnect"})\` \u2014 many users don't know this is an option. Mention once; don't repeat on every signing call.
|
|
50236
|
+
|
|
50237
|
+
To switch mid-session, call \`switch_wallet_mode\`. The new mode applies to subsequent calls; in-flight signing keeps using its captured wallet. Mode persists for the MCP server's lifetime; restarting the MCP client (e.g. Claude Desktop) resets to local.`;
|
|
49918
50238
|
var TwakMcpServer = class {
|
|
49919
50239
|
server;
|
|
49920
50240
|
registry;
|
|
@@ -49922,7 +50242,10 @@ var TwakMcpServer = class {
|
|
|
49922
50242
|
constructor(registry, context) {
|
|
49923
50243
|
this.registry = registry;
|
|
49924
50244
|
this.context = context;
|
|
49925
|
-
this.server = new Server(
|
|
50245
|
+
this.server = new Server(
|
|
50246
|
+
{ name: "twak", version: "0.1.0" },
|
|
50247
|
+
{ capabilities: { tools: {} }, instructions: SERVER_INSTRUCTIONS }
|
|
50248
|
+
);
|
|
49926
50249
|
this.setupHandlers();
|
|
49927
50250
|
}
|
|
49928
50251
|
setupHandlers() {
|
|
@@ -49956,6 +50279,9 @@ var TwakMcpServer = class {
|
|
|
49956
50279
|
try {
|
|
49957
50280
|
const parsed = action.schema.parse(args);
|
|
49958
50281
|
const result = await action.invoke(this.context, parsed);
|
|
50282
|
+
if (result instanceof McpResponse) {
|
|
50283
|
+
return { content: result.content };
|
|
50284
|
+
}
|
|
49959
50285
|
return {
|
|
49960
50286
|
content: [
|
|
49961
50287
|
{
|
|
@@ -49999,7 +50325,7 @@ var CAIP2_TO_BC_CHAIN = {
|
|
|
49999
50325
|
"eip155:42161": "arbitrum",
|
|
50000
50326
|
"eip155:8453": "base",
|
|
50001
50327
|
"eip155:10": "optimism",
|
|
50002
|
-
"eip155:43114": "
|
|
50328
|
+
"eip155:43114": "avalanche"
|
|
50003
50329
|
};
|
|
50004
50330
|
var RPC_REGISTRY = {
|
|
50005
50331
|
"eip155:1": {
|
|
@@ -50146,7 +50472,13 @@ async function verifyPayment(rawHeader, config, httpClient, seenHashes, blockcha
|
|
|
50146
50472
|
if (await seenHashes.hasSeen(txHash)) {
|
|
50147
50473
|
return { valid: false, error: "Transaction hash already used (replay attack detected)" };
|
|
50148
50474
|
}
|
|
50149
|
-
|
|
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();
|
|
50150
50482
|
const chainKey = CAIP2_TO_CHAIN[chainId];
|
|
50151
50483
|
if (!chainKey) {
|
|
50152
50484
|
return { valid: false, error: `Unsupported chain: ${chainId}` };
|
|
@@ -50160,7 +50492,10 @@ async function verifyPayment(rawHeader, config, httpClient, seenHashes, blockcha
|
|
|
50160
50492
|
txHash
|
|
50161
50493
|
);
|
|
50162
50494
|
if (status.failed) {
|
|
50163
|
-
return {
|
|
50495
|
+
return {
|
|
50496
|
+
valid: false,
|
|
50497
|
+
error: `Transaction reverted on-chain${status.error ? `: ${status.error}` : ""}`
|
|
50498
|
+
};
|
|
50164
50499
|
}
|
|
50165
50500
|
if (status.confirmed) {
|
|
50166
50501
|
const rpcResult2 = await verifyViaRPC(txHash, config, chainId);
|
|
@@ -50189,14 +50524,20 @@ async function verifyPayment(rawHeader, config, httpClient, seenHashes, blockcha
|
|
|
50189
50524
|
const decimals = isNative ? chain2.nativeDecimals : tokenInfo?.decimals ?? 18;
|
|
50190
50525
|
const expectedAtomic = parseTokenAmount(config.amount, decimals);
|
|
50191
50526
|
if (BigInt(tx.amount) < expectedAtomic) {
|
|
50192
|
-
return {
|
|
50527
|
+
return {
|
|
50528
|
+
valid: false,
|
|
50529
|
+
error: `Insufficient payment: received ${tx.amount}, expected ${config.amount}`
|
|
50530
|
+
};
|
|
50193
50531
|
}
|
|
50194
50532
|
await seenHashes.markSeen(txHash, chainId);
|
|
50195
50533
|
return { valid: true, method: "txhub" };
|
|
50196
50534
|
} catch (err) {
|
|
50197
50535
|
const is403 = err instanceof Error && err.message.includes("403");
|
|
50198
50536
|
if (!is403) {
|
|
50199
|
-
return {
|
|
50537
|
+
return {
|
|
50538
|
+
valid: false,
|
|
50539
|
+
error: `Failed to fetch transaction: ${err instanceof Error ? err.message : String(err)}`
|
|
50540
|
+
};
|
|
50200
50541
|
}
|
|
50201
50542
|
}
|
|
50202
50543
|
const rpcResult = await verifyViaRPC(txHash, config, chainId);
|
|
@@ -50414,7 +50755,7 @@ function registerServeCommand(program3) {
|
|
|
50414
50755
|
"--payment-recipient <address>",
|
|
50415
50756
|
"Address to receive payments (required for on-chain verification)",
|
|
50416
50757
|
""
|
|
50417
|
-
).option("--password <password>", "Wallet password (falls back to keychain)").option("--auto-lock <minutes>", "Auto-lock wallet after N minutes of inactivity").option("--
|
|
50758
|
+
).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) => {
|
|
50418
50759
|
await ensureTrustSDK();
|
|
50419
50760
|
const client = await getClient();
|
|
50420
50761
|
const swapClient = new SwapClient(client);
|
|
@@ -50427,15 +50768,6 @@ function registerServeCommand(program3) {
|
|
|
50427
50768
|
if (password3) agentWallet = await AgentWalletProvider.fromStorage(password3);
|
|
50428
50769
|
} catch {
|
|
50429
50770
|
}
|
|
50430
|
-
let wallet;
|
|
50431
|
-
if (opts.walletconnect) {
|
|
50432
|
-
const projectId = opts.wcProjectId || process.env.WALLETCONNECT_PROJECT_ID;
|
|
50433
|
-
if (!projectId) {
|
|
50434
|
-
error("--walletconnect requires --wc-project-id or WALLETCONNECT_PROJECT_ID");
|
|
50435
|
-
process.exit(1);
|
|
50436
|
-
}
|
|
50437
|
-
wallet = new WalletConnectProvider({ projectId });
|
|
50438
|
-
}
|
|
50439
50771
|
if (opts.autoLock && agentWallet) {
|
|
50440
50772
|
const minutes = parseInt(opts.autoLock, 10);
|
|
50441
50773
|
if (isNaN(minutes) || minutes <= 0) {
|
|
@@ -50447,33 +50779,37 @@ function registerServeCommand(program3) {
|
|
|
50447
50779
|
passwordResolver: () => resolvePassword(opts)
|
|
50448
50780
|
});
|
|
50449
50781
|
}
|
|
50450
|
-
|
|
50451
|
-
|
|
50452
|
-
|
|
50453
|
-
|
|
50454
|
-
|
|
50455
|
-
|
|
50782
|
+
let wcProjectId = opts.wcProjectId || process.env.WALLETCONNECT_PROJECT_ID;
|
|
50783
|
+
if (!wcProjectId) {
|
|
50784
|
+
try {
|
|
50785
|
+
const creds = await new CredentialManager().load();
|
|
50786
|
+
wcProjectId = creds?.walletConnectProjectId;
|
|
50787
|
+
} catch {
|
|
50456
50788
|
}
|
|
50457
|
-
|
|
50458
|
-
}
|
|
50459
|
-
|
|
50789
|
+
}
|
|
50790
|
+
const walletConnectFactory = wcProjectId ? () => new WalletConnectProvider({ projectId: wcProjectId }) : void 0;
|
|
50791
|
+
const walletContext = new WalletContext();
|
|
50792
|
+
if (agentWallet) {
|
|
50793
|
+
await walletContext.bindLocal(agentWallet);
|
|
50460
50794
|
}
|
|
50461
50795
|
const registry = new ActionRegistry();
|
|
50462
50796
|
registry.register(new BalanceActionProvider());
|
|
50463
|
-
registry.register(
|
|
50464
|
-
|
|
50797
|
+
registry.register(
|
|
50798
|
+
new WalletModeActionProvider({ walletContext, agentWallet, walletConnectFactory })
|
|
50799
|
+
);
|
|
50800
|
+
registry.register(new SwapActionProvider(swapClient, walletContext, blockchainClient));
|
|
50465
50801
|
registry.register(new SecurityActionProvider());
|
|
50466
50802
|
registry.register(new HistoryActionProvider());
|
|
50467
|
-
registry.register(new TransferActionProvider(
|
|
50803
|
+
registry.register(new TransferActionProvider(walletContext, swapClient));
|
|
50468
50804
|
registry.register(new AlertActionProvider(alertStore, swapClient));
|
|
50469
|
-
registry.register(new AgentWalletActionProvider(blockchainClient,
|
|
50470
|
-
registry.register(new TokenActionProvider(blockchainClient,
|
|
50805
|
+
registry.register(new AgentWalletActionProvider(blockchainClient, walletContext));
|
|
50806
|
+
registry.register(new TokenActionProvider(blockchainClient, walletContext));
|
|
50471
50807
|
registry.register(new MarketActionProvider());
|
|
50472
|
-
registry.register(new OnrampActionProvider(
|
|
50808
|
+
registry.register(new OnrampActionProvider(walletContext, swapClient));
|
|
50473
50809
|
registry.register(
|
|
50474
|
-
new AutomateActionProvider(automationStore, swapClient,
|
|
50810
|
+
new AutomateActionProvider(automationStore, swapClient, walletContext, blockchainClient)
|
|
50475
50811
|
);
|
|
50476
|
-
registry.register(new X402ActionProvider(
|
|
50812
|
+
registry.register(new X402ActionProvider(walletContext));
|
|
50477
50813
|
const context = {
|
|
50478
50814
|
httpClient: client
|
|
50479
50815
|
};
|
|
@@ -50489,7 +50825,10 @@ function registerServeCommand(program3) {
|
|
|
50489
50825
|
paymentRecipient: opts.paymentRecipient,
|
|
50490
50826
|
blockchainClient
|
|
50491
50827
|
});
|
|
50492
|
-
registerShutdownHandlers([agentWallet
|
|
50828
|
+
registerShutdownHandlers([agentWallet], async () => {
|
|
50829
|
+
await walletContext.reset();
|
|
50830
|
+
restServer.stop();
|
|
50831
|
+
});
|
|
50493
50832
|
try {
|
|
50494
50833
|
await restServer.start();
|
|
50495
50834
|
} catch (err) {
|
|
@@ -50497,7 +50836,7 @@ function registerServeCommand(program3) {
|
|
|
50497
50836
|
process.exit(1);
|
|
50498
50837
|
}
|
|
50499
50838
|
} else {
|
|
50500
|
-
registerShutdownHandlers([agentWallet,
|
|
50839
|
+
registerShutdownHandlers([agentWallet], () => walletContext.reset());
|
|
50501
50840
|
const mcpServer = new TwakMcpServer(registry, context);
|
|
50502
50841
|
console.error("TW Agent MCP server starting...");
|
|
50503
50842
|
await mcpServer.start();
|
|
@@ -51149,6 +51488,7 @@ function registerAutomateCommand(program3) {
|
|
|
51149
51488
|
}
|
|
51150
51489
|
|
|
51151
51490
|
// src/commands/x402.ts
|
|
51491
|
+
init_dist();
|
|
51152
51492
|
function makeReadlineConfirm() {
|
|
51153
51493
|
return async (k4) => {
|
|
51154
51494
|
const readline = await import("readline");
|
|
@@ -51205,6 +51545,54 @@ async function autoApproveConfirm(c3) {
|
|
|
51205
51545
|
warn(`x402: auto-approving Permit2 unlimited allowance for ${c3.token} on ${c3.chain}`);
|
|
51206
51546
|
return true;
|
|
51207
51547
|
}
|
|
51548
|
+
function supportedChainName(network) {
|
|
51549
|
+
if (!network.startsWith("eip155:")) return null;
|
|
51550
|
+
const chain2 = getChainByEvmId(network.slice("eip155:".length));
|
|
51551
|
+
return chain2 ? chain2.name : null;
|
|
51552
|
+
}
|
|
51553
|
+
function filterAndSortAccepts(accepts) {
|
|
51554
|
+
const supported = accepts.flatMap((a3) => {
|
|
51555
|
+
const chainName = supportedChainName(a3.network);
|
|
51556
|
+
return chainName ? [{ ...a3, chainName }] : [];
|
|
51557
|
+
});
|
|
51558
|
+
return supported.sort((a3, b3) => Number(b3.preferred) - Number(a3.preferred));
|
|
51559
|
+
}
|
|
51560
|
+
function safeTokenName(s2) {
|
|
51561
|
+
return s2.replace(/[\x00-\x1f\x7f]/g, "");
|
|
51562
|
+
}
|
|
51563
|
+
function renderAcceptsTable(accepts) {
|
|
51564
|
+
const rows = accepts.map((a3, i2) => ({
|
|
51565
|
+
idx: String(i2 + 1),
|
|
51566
|
+
token: safeTokenName(a3.tokenName) || a3.asset,
|
|
51567
|
+
chain: a3.chainName,
|
|
51568
|
+
amount: a3.amount,
|
|
51569
|
+
method: a3.transferMethod + (a3.requiresApproval ? " (Permit2)" : " (gasless)"),
|
|
51570
|
+
asset: a3.asset
|
|
51571
|
+
}));
|
|
51572
|
+
const headers = {
|
|
51573
|
+
idx: "#",
|
|
51574
|
+
token: "Token",
|
|
51575
|
+
chain: "Chain",
|
|
51576
|
+
amount: "Amount",
|
|
51577
|
+
method: "Method",
|
|
51578
|
+
asset: "Asset"
|
|
51579
|
+
};
|
|
51580
|
+
const widths = {
|
|
51581
|
+
idx: Math.max(headers.idx.length, ...rows.map((r3) => r3.idx.length)),
|
|
51582
|
+
token: Math.max(headers.token.length, ...rows.map((r3) => r3.token.length)),
|
|
51583
|
+
chain: Math.max(headers.chain.length, ...rows.map((r3) => r3.chain.length)),
|
|
51584
|
+
amount: Math.max(headers.amount.length, ...rows.map((r3) => r3.amount.length)),
|
|
51585
|
+
method: Math.max(headers.method.length, ...rows.map((r3) => r3.method.length)),
|
|
51586
|
+
asset: Math.max(headers.asset.length, ...rows.map((r3) => r3.asset.length))
|
|
51587
|
+
};
|
|
51588
|
+
const pad3 = (s2, w4) => s2 + " ".repeat(Math.max(0, w4 - s2.length));
|
|
51589
|
+
const line = (r3) => `${pad3(r3.idx, widths.idx)} ${pad3(r3.token, widths.token)} ${pad3(r3.chain, widths.chain)} ${pad3(r3.amount, widths.amount)} ${pad3(r3.method, widths.method)} ${pad3(r3.asset, widths.asset)}`;
|
|
51590
|
+
const totalWidth = Object.values(widths).reduce((a3, b3) => a3 + b3, 0) + (Object.keys(widths).length - 1) * 2;
|
|
51591
|
+
console.log(line(headers));
|
|
51592
|
+
console.log("\u2500".repeat(totalWidth));
|
|
51593
|
+
rows.forEach((r3) => console.log(line(r3)));
|
|
51594
|
+
console.log("\u2500".repeat(totalWidth));
|
|
51595
|
+
}
|
|
51208
51596
|
function registerX402Command(program3) {
|
|
51209
51597
|
const x402 = program3.command("x402").description("x402 micropayment protocol \u2014 pay HTTP endpoints that require on-chain payment");
|
|
51210
51598
|
x402.command("request <url>").description(
|
|
@@ -51258,13 +51646,84 @@ function registerX402Command(program3) {
|
|
|
51258
51646
|
handleCommandError(err, opts.json ?? false, "x402 request failed");
|
|
51259
51647
|
}
|
|
51260
51648
|
});
|
|
51649
|
+
x402.command("quote <url>").description(
|
|
51650
|
+
"Preview an x402 endpoint's payment options without signing. Read-only; no wallet needed."
|
|
51651
|
+
).option("--method <method>", "HTTP method", "GET").option("--body <json>", "Request body (JSON string)").option("--json", "JSON output").action(async (url, opts) => {
|
|
51652
|
+
try {
|
|
51653
|
+
const result = await quoteX402({ url, method: opts.method, body: opts.body });
|
|
51654
|
+
if (!result.success) {
|
|
51655
|
+
if (opts.json) {
|
|
51656
|
+
output({ ...result, error: result.message, errorCode: result.code }, true);
|
|
51657
|
+
} else {
|
|
51658
|
+
error(result.message);
|
|
51659
|
+
}
|
|
51660
|
+
process.exitCode = 1;
|
|
51661
|
+
return;
|
|
51662
|
+
}
|
|
51663
|
+
const sorted = filterAndSortAccepts(result.accepts);
|
|
51664
|
+
const shQuote = (s2) => `'${s2.replace(/'/g, `'\\''`)}'`;
|
|
51665
|
+
const cliCommand = (amount) => {
|
|
51666
|
+
const parts = ["twak x402 request", shQuote(url)];
|
|
51667
|
+
if (opts.method && opts.method.toUpperCase() !== "GET") {
|
|
51668
|
+
parts.push(`--method ${shQuote(opts.method)}`);
|
|
51669
|
+
}
|
|
51670
|
+
if (opts.body) parts.push(`--body ${shQuote(opts.body)}`);
|
|
51671
|
+
parts.push(`--max-payment ${amount}`);
|
|
51672
|
+
return parts.join(" ");
|
|
51673
|
+
};
|
|
51674
|
+
if (opts.json) {
|
|
51675
|
+
if (sorted.length === 0) {
|
|
51676
|
+
output(
|
|
51677
|
+
{
|
|
51678
|
+
...result,
|
|
51679
|
+
nextAction: `Endpoint offered ${result.accepts.length} option(s) but none on chains this client supports.`
|
|
51680
|
+
},
|
|
51681
|
+
true
|
|
51682
|
+
);
|
|
51683
|
+
process.exitCode = 1;
|
|
51684
|
+
return;
|
|
51685
|
+
}
|
|
51686
|
+
output(
|
|
51687
|
+
{
|
|
51688
|
+
...result,
|
|
51689
|
+
nextAction: `Show these payment options to the user. Default route is ${sorted[0].amount} ${safeTokenName(sorted[0].tokenName) || sorted[0].asset} on ${sorted[0].chainName}. Pick one and run: ${cliCommand(sorted[0].amount)} --prefer-asset <picked asset>. Omit --prefer-asset to use the default.`
|
|
51690
|
+
},
|
|
51691
|
+
true
|
|
51692
|
+
);
|
|
51693
|
+
return;
|
|
51694
|
+
}
|
|
51695
|
+
if (result.resource?.url) {
|
|
51696
|
+
info(`Resource: ${result.resource.url}`);
|
|
51697
|
+
}
|
|
51698
|
+
info(result.summary);
|
|
51699
|
+
const dropped = result.accepts.length - sorted.length;
|
|
51700
|
+
if (dropped > 0) {
|
|
51701
|
+
warn(`Filtered ${dropped} route(s) on chains this client does not support.`);
|
|
51702
|
+
}
|
|
51703
|
+
console.log("");
|
|
51704
|
+
if (sorted.length === 0) {
|
|
51705
|
+
error("No supported route from this endpoint.");
|
|
51706
|
+
process.exitCode = 1;
|
|
51707
|
+
return;
|
|
51708
|
+
}
|
|
51709
|
+
renderAcceptsTable(sorted);
|
|
51710
|
+
console.log(
|
|
51711
|
+
"If you don't pass --prefer-asset/--prefer-method/--prefer-network, the first row is used."
|
|
51712
|
+
);
|
|
51713
|
+
console.log("");
|
|
51714
|
+
info(`Next: ${cliCommand(sorted[0].amount)}`);
|
|
51715
|
+
} catch (err) {
|
|
51716
|
+
handleCommandError(err, opts.json ?? false, "x402 quote failed");
|
|
51717
|
+
}
|
|
51718
|
+
});
|
|
51261
51719
|
x402.command("info").description("Show x402 client information and usage").action(() => {
|
|
51262
51720
|
info("twak x402 client \u2014 speaks canonical x402 (V1 + V2) with EIP-3009 + Permit2 signing");
|
|
51263
51721
|
console.log("");
|
|
51264
51722
|
console.log("Usage:");
|
|
51723
|
+
console.log(" twak x402 quote <url> Preview payment options (read-only)");
|
|
51265
51724
|
console.log(" twak x402 request <url> Pay an x402-gated endpoint");
|
|
51266
51725
|
console.log("");
|
|
51267
|
-
console.log("Options:");
|
|
51726
|
+
console.log("Options (request):");
|
|
51268
51727
|
console.log(" --method <method> HTTP method (default: GET)");
|
|
51269
51728
|
console.log(" --body <json> Request body");
|
|
51270
51729
|
console.log(" --max-payment <atomic> Cap auto-approval; integer atomic units");
|
|
@@ -51292,7 +51751,7 @@ var crypto7 = __toESM(require("crypto"));
|
|
|
51292
51751
|
// package.json
|
|
51293
51752
|
var package_default = {
|
|
51294
51753
|
name: "@trustwallet/cli",
|
|
51295
|
-
version: "0.
|
|
51754
|
+
version: "0.14.0",
|
|
51296
51755
|
description: "TWAK CLI \u2014 command-line interface for Trust Wallet Agent Kit",
|
|
51297
51756
|
main: "dist/index.js",
|
|
51298
51757
|
types: "dist/index.d.ts",
|