@zeroxyz/cli 1.25.0 → 1.26.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.
Files changed (2) hide show
  1. package/dist/index.js +804 -276
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8785,7 +8785,7 @@ var init_BlockOverrides = __esm({
8785
8785
  });
8786
8786
 
8787
8787
  // ../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/constants/abis.js
8788
- var multicall3Abi, batchGatewayAbi, universalResolverErrors, universalResolverResolveAbi, universalResolverReverseAbi, textResolverAbi, addressResolverAbi, erc1271Abi, erc6492SignatureValidatorAbi;
8788
+ var multicall3Abi, batchGatewayAbi, universalResolverErrors, universalResolverResolveAbi, universalResolverReverseAbi, textResolverAbi, addressResolverAbi, erc1271Abi, erc6492SignatureValidatorAbi, erc20Abi;
8789
8789
  var init_abis = __esm({
8790
8790
  "../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/constants/abis.js"() {
8791
8791
  init_esm_shims();
@@ -9140,6 +9140,194 @@ var init_abis = __esm({
9140
9140
  name: "isValidSig"
9141
9141
  }
9142
9142
  ];
9143
+ erc20Abi = [
9144
+ {
9145
+ type: "event",
9146
+ name: "Approval",
9147
+ inputs: [
9148
+ {
9149
+ indexed: true,
9150
+ name: "owner",
9151
+ type: "address"
9152
+ },
9153
+ {
9154
+ indexed: true,
9155
+ name: "spender",
9156
+ type: "address"
9157
+ },
9158
+ {
9159
+ indexed: false,
9160
+ name: "value",
9161
+ type: "uint256"
9162
+ }
9163
+ ]
9164
+ },
9165
+ {
9166
+ type: "event",
9167
+ name: "Transfer",
9168
+ inputs: [
9169
+ {
9170
+ indexed: true,
9171
+ name: "from",
9172
+ type: "address"
9173
+ },
9174
+ {
9175
+ indexed: true,
9176
+ name: "to",
9177
+ type: "address"
9178
+ },
9179
+ {
9180
+ indexed: false,
9181
+ name: "value",
9182
+ type: "uint256"
9183
+ }
9184
+ ]
9185
+ },
9186
+ {
9187
+ type: "function",
9188
+ name: "allowance",
9189
+ stateMutability: "view",
9190
+ inputs: [
9191
+ {
9192
+ name: "owner",
9193
+ type: "address"
9194
+ },
9195
+ {
9196
+ name: "spender",
9197
+ type: "address"
9198
+ }
9199
+ ],
9200
+ outputs: [
9201
+ {
9202
+ type: "uint256"
9203
+ }
9204
+ ]
9205
+ },
9206
+ {
9207
+ type: "function",
9208
+ name: "approve",
9209
+ stateMutability: "nonpayable",
9210
+ inputs: [
9211
+ {
9212
+ name: "spender",
9213
+ type: "address"
9214
+ },
9215
+ {
9216
+ name: "amount",
9217
+ type: "uint256"
9218
+ }
9219
+ ],
9220
+ outputs: [
9221
+ {
9222
+ type: "bool"
9223
+ }
9224
+ ]
9225
+ },
9226
+ {
9227
+ type: "function",
9228
+ name: "balanceOf",
9229
+ stateMutability: "view",
9230
+ inputs: [
9231
+ {
9232
+ name: "account",
9233
+ type: "address"
9234
+ }
9235
+ ],
9236
+ outputs: [
9237
+ {
9238
+ type: "uint256"
9239
+ }
9240
+ ]
9241
+ },
9242
+ {
9243
+ type: "function",
9244
+ name: "decimals",
9245
+ stateMutability: "view",
9246
+ inputs: [],
9247
+ outputs: [
9248
+ {
9249
+ type: "uint8"
9250
+ }
9251
+ ]
9252
+ },
9253
+ {
9254
+ type: "function",
9255
+ name: "name",
9256
+ stateMutability: "view",
9257
+ inputs: [],
9258
+ outputs: [
9259
+ {
9260
+ type: "string"
9261
+ }
9262
+ ]
9263
+ },
9264
+ {
9265
+ type: "function",
9266
+ name: "symbol",
9267
+ stateMutability: "view",
9268
+ inputs: [],
9269
+ outputs: [
9270
+ {
9271
+ type: "string"
9272
+ }
9273
+ ]
9274
+ },
9275
+ {
9276
+ type: "function",
9277
+ name: "totalSupply",
9278
+ stateMutability: "view",
9279
+ inputs: [],
9280
+ outputs: [
9281
+ {
9282
+ type: "uint256"
9283
+ }
9284
+ ]
9285
+ },
9286
+ {
9287
+ type: "function",
9288
+ name: "transfer",
9289
+ stateMutability: "nonpayable",
9290
+ inputs: [
9291
+ {
9292
+ name: "recipient",
9293
+ type: "address"
9294
+ },
9295
+ {
9296
+ name: "amount",
9297
+ type: "uint256"
9298
+ }
9299
+ ],
9300
+ outputs: [
9301
+ {
9302
+ type: "bool"
9303
+ }
9304
+ ]
9305
+ },
9306
+ {
9307
+ type: "function",
9308
+ name: "transferFrom",
9309
+ stateMutability: "nonpayable",
9310
+ inputs: [
9311
+ {
9312
+ name: "sender",
9313
+ type: "address"
9314
+ },
9315
+ {
9316
+ name: "recipient",
9317
+ type: "address"
9318
+ },
9319
+ {
9320
+ name: "amount",
9321
+ type: "uint256"
9322
+ }
9323
+ ],
9324
+ outputs: [
9325
+ {
9326
+ type: "bool"
9327
+ }
9328
+ ]
9329
+ }
9330
+ ];
9143
9331
  }
9144
9332
  });
9145
9333
 
@@ -78480,7 +78668,7 @@ init_esm_shims();
78480
78668
  // ../sdk/dist/index.js
78481
78669
  init_esm_shims();
78482
78670
 
78483
- // ../sdk/dist/chunk-UWPQEIGZ.js
78671
+ // ../sdk/dist/chunk-YRHWENJ7.js
78484
78672
  init_esm_shims();
78485
78673
 
78486
78674
  // ../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/index.js
@@ -88445,6 +88633,8 @@ function http(url4, config3 = {}) {
88445
88633
  }
88446
88634
 
88447
88635
  // ../../node_modules/.pnpm/viem@2.52.2_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@6.0.6_zod@4.3.6/node_modules/viem/_esm/index.js
88636
+ init_abis();
88637
+ init_number();
88448
88638
  init_encodeAbiParameters();
88449
88639
  init_encodeFunctionData();
88450
88640
  init_getAbiItem();
@@ -163979,8 +164169,6 @@ config2(en_default3());
163979
164169
 
163980
164170
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/index.js
163981
164171
  var zod_default = external_exports3;
163982
-
163983
- // ../sdk/dist/chunk-UWPQEIGZ.js
163984
164172
  var createManagedAccount = (client, address5, opts = {}) => {
163985
164173
  const signOptions = {
163986
164174
  // expectedAddress pins verification to this adapter's frozen address.
@@ -164196,43 +164384,50 @@ var normalizeTransportEnvelopeRequest = (url4, opts) => {
164196
164384
  body: hasInnerBody ? JSON.stringify(innerBody) : void 0
164197
164385
  };
164198
164386
  };
164199
- var KNOWN_EIP712_DOMAINS = {
164200
- // Base mainnet USDC. On-chain `name()` is "USD Coin" (NOT "USDC")
164201
- // — verified via the contract's name() / version().
164202
- "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": {
164203
- name: "USD Coin",
164204
- version: "2"
164205
- },
164206
- // Base Sepolia USDC. Testnet's on-chain `name()` is "USDC",
164207
- // differs from mainnet's "USD Coin".
164208
- "0x036cbd53842c5426634e7929541ec2318f3dcf7e": {
164209
- name: "USDC",
164210
- version: "2"
164211
- }
164212
- };
164213
- var trimmedOrUndefined = (v) => {
164214
- if (typeof v !== "string") return void 0;
164215
- const t = v.trim();
164216
- return t.length > 0 ? t : void 0;
164387
+ var package_default = {
164388
+ version: "0.19.0"
164217
164389
  };
164218
- var trimStringFields = (extra) => {
164219
- if (!extra) return extra;
164220
- const out = {};
164221
- for (const [k, v] of Object.entries(extra)) {
164222
- out[k] = typeof v === "string" ? v.trim() : v;
164390
+ var SDK_VERSION2 = package_default.version;
164391
+ var refreshResponseSchema = external_exports3.object({
164392
+ accessToken: external_exports3.string(),
164393
+ refreshToken: external_exports3.string()
164394
+ });
164395
+ var tryRefreshSession = async (client, callerSignal) => {
164396
+ const credentials = client.getCredentials();
164397
+ if (credentials.kind !== "session") return false;
164398
+ if (!credentials.refreshToken) return false;
164399
+ const url4 = `${client.baseUrl.replace(/\/$/, "")}/v1/auth/refresh`;
164400
+ const timeoutSignal = AbortSignal.timeout(client.timeout);
164401
+ const signal = callerSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : timeoutSignal;
164402
+ let response;
164403
+ try {
164404
+ response = await client.fetchImpl(url4, {
164405
+ method: "POST",
164406
+ headers: { "content-type": "application/json" },
164407
+ body: JSON.stringify({ refreshToken: credentials.refreshToken }),
164408
+ signal
164409
+ });
164410
+ } catch {
164411
+ return false;
164223
164412
  }
164224
- return out;
164225
- };
164226
- var withKnownEip712Domain = (asset, extra) => {
164227
- const trimmedExtra = trimStringFields(extra);
164228
- const known = KNOWN_EIP712_DOMAINS[(asset ?? "").toLowerCase()];
164229
- if (!known) return trimmedExtra;
164230
- return {
164231
- ...trimmedExtra,
164232
- name: trimmedOrUndefined(trimmedExtra?.name) ?? known.name,
164233
- version: trimmedOrUndefined(trimmedExtra?.version) ?? known.version
164234
- };
164413
+ if (!response.ok) return false;
164414
+ let body;
164415
+ try {
164416
+ body = await response.json();
164417
+ } catch {
164418
+ return false;
164419
+ }
164420
+ const parsed = refreshResponseSchema.safeParse(body);
164421
+ if (!parsed.success) return false;
164422
+ await client.applyRefreshedTokens({
164423
+ accessToken: parsed.data.accessToken,
164424
+ refreshToken: parsed.data.refreshToken
164425
+ });
164426
+ return true;
164235
164427
  };
164428
+ var buildSessionAuthHeaders = (accessToken) => ({
164429
+ authorization: `Bearer ${accessToken}`
164430
+ });
164236
164431
  var ABORT_SIGNAL_ANY_AVAILABLE = typeof AbortSignal !== "undefined" && typeof AbortSignal.any === "function";
164237
164432
  var headersToRecord = (h) => {
164238
164433
  const result = {};
@@ -164359,6 +164554,383 @@ var buildConfiguredFetch = (client, overrides2) => {
164359
164554
  };
164360
164555
  return impl;
164361
164556
  };
164557
+ var buildCanonicalMessage = (method, pathWithQuery, body, timestamp, nonce2) => {
164558
+ const bodyHash = createHash("sha256").update(body ?? "").digest("hex");
164559
+ return `${method}:${pathWithQuery}:${bodyHash}:${timestamp}:${nonce2}`;
164560
+ };
164561
+ var buildAccountAuthHeaders = async (account, method, pathWithQuery, body) => {
164562
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
164563
+ const nonce2 = crypto.randomUUID();
164564
+ const message2 = buildCanonicalMessage(
164565
+ method,
164566
+ pathWithQuery,
164567
+ body,
164568
+ timestamp,
164569
+ nonce2
164570
+ );
164571
+ const signature2 = await account.signMessage({ message: message2 });
164572
+ return {
164573
+ "x-zero-address": account.address,
164574
+ "x-zero-timestamp": timestamp,
164575
+ "x-zero-nonce": nonce2,
164576
+ "x-zero-signature": signature2
164577
+ };
164578
+ };
164579
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 502, 503, 504]);
164580
+ var sleep = (ms, signal) => new Promise((resolve7, reject) => {
164581
+ if (signal?.aborted) {
164582
+ reject(new DOMException("Aborted", "AbortError"));
164583
+ return;
164584
+ }
164585
+ const timer = setTimeout(() => {
164586
+ signal?.removeEventListener("abort", onAbort);
164587
+ resolve7();
164588
+ }, ms);
164589
+ const onAbort = () => {
164590
+ clearTimeout(timer);
164591
+ reject(new DOMException("Aborted", "AbortError"));
164592
+ };
164593
+ signal?.addEventListener("abort", onAbort, { once: true });
164594
+ });
164595
+ var buildPathWithQuery = (path3, query) => {
164596
+ const normalizedPath = path3.startsWith("/") ? path3 : `/${path3}`;
164597
+ if (!query) return normalizedPath;
164598
+ const params = new URLSearchParams();
164599
+ for (const [key, value] of Object.entries(query)) {
164600
+ if (value !== void 0) params.set(key, String(value));
164601
+ }
164602
+ const qs = params.toString();
164603
+ return qs ? `${normalizedPath}?${qs}` : normalizedPath;
164604
+ };
164605
+ var joinUrl = (baseUrl, pathWithQuery) => {
164606
+ const base4 = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
164607
+ return `${base4}${pathWithQuery}`;
164608
+ };
164609
+ var buildHeaders = async (client, method, pathWithQuery, hasBody, bodyStr) => {
164610
+ const headers = { ...client.defaultHeaders };
164611
+ if (hasBody) headers["content-type"] = "application/json";
164612
+ headers["x-zero-sdk-version"] = SDK_VERSION2;
164613
+ if (client.apiVersion) headers["x-zero-api-version"] = client.apiVersion;
164614
+ const credentials = client.getCredentials();
164615
+ if (credentials.kind === "session") {
164616
+ Object.assign(headers, buildSessionAuthHeaders(credentials.accessToken));
164617
+ } else if (credentials.kind === "account") {
164618
+ const signed = await buildAccountAuthHeaders(
164619
+ credentials.account,
164620
+ method,
164621
+ pathWithQuery,
164622
+ bodyStr
164623
+ );
164624
+ Object.assign(headers, signed);
164625
+ }
164626
+ return headers;
164627
+ };
164628
+ var parseErrorBody = async (response) => {
164629
+ try {
164630
+ return await response.clone().json();
164631
+ } catch {
164632
+ try {
164633
+ return await response.text();
164634
+ } catch {
164635
+ return void 0;
164636
+ }
164637
+ }
164638
+ };
164639
+ var errorMessageFromBody = (body, status) => {
164640
+ if (body !== null && typeof body === "object" && "error" in body && typeof body.error === "string") {
164641
+ return body.error;
164642
+ }
164643
+ return `HTTP ${status}`;
164644
+ };
164645
+ var request2 = async (client, opts, schema) => {
164646
+ const pathWithQuery = buildPathWithQuery(opts.path, opts.query);
164647
+ const url4 = joinUrl(client.baseUrl, pathWithQuery);
164648
+ const hasBody = opts.body !== void 0;
164649
+ const bodyStr = hasBody ? JSON.stringify(
164650
+ opts.body,
164651
+ (_k, v) => typeof v === "bigint" ? v.toString() : v
164652
+ ) : void 0;
164653
+ const configuredFetch = buildConfiguredFetch(client);
164654
+ const performOnce = async () => configuredFetch(url4, {
164655
+ method: opts.method,
164656
+ headers: await buildHeaders(
164657
+ client,
164658
+ opts.method,
164659
+ pathWithQuery,
164660
+ hasBody,
164661
+ bodyStr
164662
+ ),
164663
+ body: bodyStr,
164664
+ signal: opts.signal
164665
+ });
164666
+ let refreshAttempted = false;
164667
+ const executeWithRefresh = async () => {
164668
+ const response = await performOnce();
164669
+ if (response.status !== 401 || refreshAttempted) return response;
164670
+ const credentials = client.getCredentials();
164671
+ if (credentials.kind !== "session") return response;
164672
+ refreshAttempted = true;
164673
+ const refreshed = await client.refreshSessionOnce(opts.signal);
164674
+ if (!refreshed) return response;
164675
+ return await performOnce();
164676
+ };
164677
+ let lastNetworkError;
164678
+ for (let attempt = 0; attempt <= client.maxRetries; attempt++) {
164679
+ let response;
164680
+ try {
164681
+ response = await executeWithRefresh();
164682
+ } catch (e) {
164683
+ if (e instanceof ZeroError) throw e;
164684
+ lastNetworkError = e;
164685
+ if (attempt < client.maxRetries) {
164686
+ try {
164687
+ await sleep(2 ** attempt * 200, opts.signal);
164688
+ } catch (sleepErr) {
164689
+ throw new ZeroApiError("Request aborted by caller", {
164690
+ status: 0,
164691
+ cause: sleepErr
164692
+ });
164693
+ }
164694
+ continue;
164695
+ }
164696
+ throw new ZeroApiError("Network request failed", {
164697
+ status: 0,
164698
+ cause: e
164699
+ });
164700
+ }
164701
+ if (response.ok) {
164702
+ let text;
164703
+ try {
164704
+ text = await response.text();
164705
+ } catch (e) {
164706
+ throw new ZeroApiError("Response body read failed", {
164707
+ status: response.status,
164708
+ cause: e
164709
+ });
164710
+ }
164711
+ let json3;
164712
+ try {
164713
+ json3 = JSON.parse(text);
164714
+ } catch {
164715
+ }
164716
+ const parsed = schema.safeParse(json3);
164717
+ if (!parsed.success) {
164718
+ throw new ZeroValidationError(
164719
+ `Response did not match expected schema for ${opts.method} ${opts.path}`,
164720
+ { cause: parsed.error, body: text }
164721
+ );
164722
+ }
164723
+ return parsed.data;
164724
+ }
164725
+ if (RETRYABLE_STATUS.has(response.status) && attempt < client.maxRetries) {
164726
+ try {
164727
+ await sleep(2 ** attempt * 200, opts.signal);
164728
+ } catch (sleepErr) {
164729
+ throw new ZeroApiError("Request aborted by caller", {
164730
+ status: 0,
164731
+ cause: sleepErr
164732
+ });
164733
+ }
164734
+ continue;
164735
+ }
164736
+ const errorBody = await parseErrorBody(response);
164737
+ const requestId = response.headers.get("x-request-id") ?? void 0;
164738
+ throw new ZeroApiError(errorMessageFromBody(errorBody, response.status), {
164739
+ status: response.status,
164740
+ requestId,
164741
+ body: errorBody
164742
+ });
164743
+ }
164744
+ throw new ZeroApiError("Exhausted retries", {
164745
+ status: 0,
164746
+ cause: lastNetworkError
164747
+ });
164748
+ };
164749
+ var TEMPO_SWEEP_FEE_HEADROOM = 5000n;
164750
+ var bridgeResponseSchema = external_exports3.object({
164751
+ sourceChain: external_exports3.enum(["base", "tempo"]),
164752
+ destinationChain: external_exports3.enum(["base", "tempo"]),
164753
+ sourceAmountUsdc: external_exports3.string(),
164754
+ transferId: external_exports3.string().optional(),
164755
+ txHash: external_exports3.string().optional(),
164756
+ status: external_exports3.literal("pending")
164757
+ });
164758
+ var callBridgeEndpoint = (client, input) => request2(
164759
+ client,
164760
+ {
164761
+ method: "POST",
164762
+ path: "/v1/users/me/wallets/bridge",
164763
+ body: {
164764
+ sourceChain: input.sourceChain,
164765
+ destinationChain: input.destinationChain,
164766
+ ...input.amountUsdc ? { amountUsdc: input.amountUsdc } : {},
164767
+ ...input.walletAddress ? { walletAddress: input.walletAddress } : {}
164768
+ },
164769
+ signal: input.signal
164770
+ },
164771
+ bridgeResponseSchema
164772
+ );
164773
+ var relayInitialized = false;
164774
+ var ensureRelayClient = () => {
164775
+ if (relayInitialized) return;
164776
+ createClient2({
164777
+ baseApiUrl: MAINNET_RELAY_API,
164778
+ source: "zero-sdk",
164779
+ chains: [
164780
+ convertViemChainToRelayChain(base),
164781
+ convertViemChainToRelayChain(tempo)
164782
+ ]
164783
+ });
164784
+ relayInitialized = true;
164785
+ };
164786
+ var executeByoReverseBridge = async (input) => {
164787
+ ensureRelayClient();
164788
+ const { account } = input;
164789
+ const tempoClient = createPublicClient({ chain: tempo, transport: http() });
164790
+ const balance = await tempoClient.readContract({
164791
+ address: USDC_TEMPO,
164792
+ abi: erc20Abi,
164793
+ functionName: "balanceOf",
164794
+ args: [account.address]
164795
+ });
164796
+ const amountRaw = input.amountUsdc ? parseUnits(input.amountUsdc, USDC_DECIMALS) : balance - TEMPO_SWEEP_FEE_HEADROOM;
164797
+ if (amountRaw <= 0n) {
164798
+ throw new ZeroInsufficientFundsError("No USDC on Tempo to consolidate", {
164799
+ have: formatUnits(balance, USDC_DECIMALS),
164800
+ need: formatUnits(TEMPO_SWEEP_FEE_HEADROOM, USDC_DECIMALS)
164801
+ });
164802
+ }
164803
+ if (amountRaw + TEMPO_SWEEP_FEE_HEADROOM > balance) {
164804
+ const have = formatUnits(balance, USDC_DECIMALS);
164805
+ throw new ZeroInsufficientFundsError(
164806
+ `Tempo balance is ${have} USDC; bridging ${formatUnits(amountRaw, USDC_DECIMALS)} needs ${formatUnits(TEMPO_SWEEP_FEE_HEADROOM, USDC_DECIMALS)} fee headroom on top`,
164807
+ {
164808
+ have,
164809
+ need: formatUnits(amountRaw + TEMPO_SWEEP_FEE_HEADROOM, USDC_DECIMALS)
164810
+ }
164811
+ );
164812
+ }
164813
+ const quote = await getClient().actions.getQuote({
164814
+ chainId: tempo.id,
164815
+ toChainId: base.id,
164816
+ currency: USDC_TEMPO,
164817
+ toCurrency: USDC_BASE,
164818
+ amount: amountRaw.toString(),
164819
+ tradeType: "EXACT_INPUT",
164820
+ user: account.address,
164821
+ recipient: account.address
164822
+ });
164823
+ const stepTxs = [];
164824
+ for (const step of quote.steps ?? []) {
164825
+ for (const item of step.items ?? []) {
164826
+ const tx = item.data;
164827
+ if (tx?.to) stepTxs.push(tx);
164828
+ }
164829
+ }
164830
+ if (stepTxs.length === 0) {
164831
+ throw new ZeroWalletError(
164832
+ "Relay quote for the Tempo\u2192Base bridge contained no executable steps"
164833
+ );
164834
+ }
164835
+ const walletClient = createWalletClient({
164836
+ account,
164837
+ chain: tempo,
164838
+ transport: http()
164839
+ });
164840
+ const depository = stepTxs[0]?.to;
164841
+ if (depository) {
164842
+ const allowance = await tempoClient.readContract({
164843
+ address: USDC_TEMPO,
164844
+ abi: erc20Abi,
164845
+ functionName: "allowance",
164846
+ args: [account.address, depository]
164847
+ });
164848
+ if (allowance < amountRaw) {
164849
+ const approveParams = {
164850
+ address: USDC_TEMPO,
164851
+ abi: erc20Abi,
164852
+ functionName: "approve",
164853
+ args: [depository, maxUint256],
164854
+ feeToken: USDC_TEMPO
164855
+ };
164856
+ const approveHash = await walletClient.writeContract(
164857
+ approveParams
164858
+ );
164859
+ const receipt = await tempoClient.waitForTransactionReceipt({
164860
+ hash: approveHash
164861
+ });
164862
+ if (receipt.status !== "success") {
164863
+ throw new ZeroWalletError(
164864
+ `Tempo depository approve reverted (tx ${approveHash})`
164865
+ );
164866
+ }
164867
+ }
164868
+ }
164869
+ let lastHash = null;
164870
+ for (const tx of stepTxs) {
164871
+ const stepParams = {
164872
+ to: tx.to,
164873
+ data: tx.data,
164874
+ value: tx.value ? BigInt(tx.value) : 0n,
164875
+ feeToken: USDC_TEMPO
164876
+ };
164877
+ lastHash = await walletClient.sendTransaction(
164878
+ stepParams
164879
+ );
164880
+ const receipt = await tempoClient.waitForTransactionReceipt({
164881
+ hash: lastHash
164882
+ });
164883
+ if (receipt.status !== "success") {
164884
+ throw new ZeroWalletError(
164885
+ `Tempo\u2192Base bridge deposit reverted (tx ${lastHash})`
164886
+ );
164887
+ }
164888
+ }
164889
+ return {
164890
+ sourceChain: "tempo",
164891
+ destinationChain: "base",
164892
+ sourceAmountUsdc: formatUnits(amountRaw, USDC_DECIMALS),
164893
+ txHash: lastHash ?? void 0,
164894
+ status: "pending"
164895
+ };
164896
+ };
164897
+ var KNOWN_EIP712_DOMAINS = {
164898
+ // Base mainnet USDC. On-chain `name()` is "USD Coin" (NOT "USDC")
164899
+ // — verified via the contract's name() / version().
164900
+ "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913": {
164901
+ name: "USD Coin",
164902
+ version: "2"
164903
+ },
164904
+ // Base Sepolia USDC. Testnet's on-chain `name()` is "USDC",
164905
+ // differs from mainnet's "USD Coin".
164906
+ "0x036cbd53842c5426634e7929541ec2318f3dcf7e": {
164907
+ name: "USDC",
164908
+ version: "2"
164909
+ }
164910
+ };
164911
+ var trimmedOrUndefined = (v) => {
164912
+ if (typeof v !== "string") return void 0;
164913
+ const t = v.trim();
164914
+ return t.length > 0 ? t : void 0;
164915
+ };
164916
+ var trimStringFields = (extra) => {
164917
+ if (!extra) return extra;
164918
+ const out = {};
164919
+ for (const [k, v] of Object.entries(extra)) {
164920
+ out[k] = typeof v === "string" ? v.trim() : v;
164921
+ }
164922
+ return out;
164923
+ };
164924
+ var withKnownEip712Domain = (asset, extra) => {
164925
+ const trimmedExtra = trimStringFields(extra);
164926
+ const known = KNOWN_EIP712_DOMAINS[(asset ?? "").toLowerCase()];
164927
+ if (!known) return trimmedExtra;
164928
+ return {
164929
+ ...trimmedExtra,
164930
+ name: trimmedOrUndefined(trimmedExtra?.name) ?? known.name,
164931
+ version: trimmedOrUndefined(trimmedExtra?.version) ?? known.version
164932
+ };
164933
+ };
164362
164934
  var looksLikeX402Body = (body) => {
164363
164935
  if (!body || typeof body !== "object") return false;
164364
164936
  const b = body;
@@ -164423,6 +164995,9 @@ var networkToChain = (network) => {
164423
164995
  var MAX_PAY_RE = /^[0-9]+(\.[0-9]{1,6})?$/;
164424
164996
  var UINT_RE = /^[0-9]+$/;
164425
164997
  var MAX_AMOUNT_DIGITS = 30;
164998
+ var JIT_REVERSE_FEE_MARGIN = 50000n;
164999
+ var BRIDGE_FILL_POLL_INTERVAL_MS = 2e3;
165000
+ var BRIDGE_FILL_TIMEOUT_MS = 9e4;
164426
165001
  var USDC_BASE2 = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
164427
165002
  var USDC_TEMPO2 = "0x20c000000000000000000000b9537d11c60e8b50";
164428
165003
  var PATHUSD_TEMPO = "0x20c0000000000000000000000000000000000000";
@@ -164720,6 +165295,9 @@ var Payments = class {
164720
165295
  });
164721
165296
  }
164722
165297
  if (balance !== null && balance < rawAmountUnits) {
165298
+ if (balanceChain === "base" && await this.tryJitReverseBridge(account, rawAmountUnits, balance)) {
165299
+ return;
165300
+ }
164723
165301
  const have = formatUnits(balance, 6);
164724
165302
  const requiredAmount = formatUnits(rawAmountUnits, 6);
164725
165303
  preflightError = new ZeroInsufficientFundsError(
@@ -165075,6 +165653,28 @@ var Payments = class {
165075
165653
  level: "info",
165076
165654
  message: `Bridging ${formatUnits(bridgeAmount, 6)} USDC from Base to Tempo...`
165077
165655
  });
165656
+ if (this.client.getCredentials().kind === "session") {
165657
+ const tempoBalanceBefore = await this.readUsdcBalance(
165658
+ account.address,
165659
+ "tempo"
165660
+ );
165661
+ const response = await callBridgeEndpoint(this.client, {
165662
+ sourceChain: "base",
165663
+ destinationChain: "tempo",
165664
+ amountUsdc: formatUnits(bridgeAmount, 6)
165665
+ });
165666
+ const filled = await this.waitForUsdcBalance(
165667
+ account.address,
165668
+ "tempo",
165669
+ tempoBalanceBefore + requiredAmount
165670
+ );
165671
+ if (!filled) {
165672
+ throw new ZeroPaymentError(
165673
+ "Base\u2192Tempo bridge did not fill in time; funds may still arrive \u2014 retry shortly"
165674
+ );
165675
+ }
165676
+ return response.txHash ?? null;
165677
+ }
165078
165678
  const walletClient = createWalletClient({
165079
165679
  account,
165080
165680
  chain: base,
@@ -165105,6 +165705,59 @@ var Payments = class {
165105
165705
  });
165106
165706
  return bridgeTxHash;
165107
165707
  };
165708
+ // Recover an x402 shortfall from the wallet's Tempo balance. Returns true
165709
+ // once the Base balance covers `requiredRaw`; false means "couldn't help"
165710
+ // (not enough combined funds, bridge failure, or fill timeout) and the
165711
+ // caller falls through to the normal insufficient-funds abort. Deliberately
165712
+ // swallows errors — this path must never make a failing payment fail worse.
165713
+ tryJitReverseBridge = async (account, requiredRaw, baseBalance) => {
165714
+ try {
165715
+ const tempoBalance = await this.readUsdcBalance(account.address, "tempo");
165716
+ const usable = tempoBalance - JIT_REVERSE_FEE_MARGIN;
165717
+ if (usable <= 0n || baseBalance + usable < requiredRaw) return false;
165718
+ this.client.logger?.({
165719
+ level: "info",
165720
+ message: `Bridging ${formatUnits(tempoBalance, 6)} USDC from Tempo back to Base to cover this payment...`
165721
+ });
165722
+ if (this.client.getCredentials().kind === "session") {
165723
+ await callBridgeEndpoint(this.client, {
165724
+ sourceChain: "tempo",
165725
+ destinationChain: "base"
165726
+ });
165727
+ } else {
165728
+ await executeByoReverseBridge({ account });
165729
+ }
165730
+ return await this.waitForUsdcBalance(
165731
+ account.address,
165732
+ "base",
165733
+ requiredRaw
165734
+ );
165735
+ } catch (err) {
165736
+ this.client.logger?.({
165737
+ level: "warn",
165738
+ message: "JIT Tempo\u2192Base bridge failed; falling back to insufficient-funds",
165739
+ meta: { error: err instanceof Error ? err.message : String(err) }
165740
+ });
165741
+ return false;
165742
+ }
165743
+ };
165744
+ // Poll until the address's USDC balance on `chain` reaches `targetRaw`.
165745
+ // Bridge fills typically land in 5-15s; the timeout is a generous multiple.
165746
+ waitForUsdcBalance = async (address5, chain2, targetRaw, timeoutMs = BRIDGE_FILL_TIMEOUT_MS) => {
165747
+ const deadline = Date.now() + timeoutMs;
165748
+ while (Date.now() < deadline) {
165749
+ await new Promise(
165750
+ (resolve7) => setTimeout(resolve7, BRIDGE_FILL_POLL_INTERVAL_MS)
165751
+ );
165752
+ try {
165753
+ if (await this.readUsdcBalance(address5, chain2) >= targetRaw) {
165754
+ return true;
165755
+ }
165756
+ } catch {
165757
+ }
165758
+ }
165759
+ return false;
165760
+ };
165108
165761
  ensureRelayClient = () => {
165109
165762
  if (this.relayInitialized) return;
165110
165763
  createClient2({
@@ -166121,10 +166774,6 @@ var clientFetch = async (client, url4, opts = {}) => {
166121
166774
  result.capabilityResolution = capabilityResolution;
166122
166775
  return result;
166123
166776
  };
166124
- var package_default = {
166125
- version: "0.18.0"
166126
- };
166127
- var SDK_VERSION2 = package_default.version;
166128
166777
  var agentAuthMetadataSchema = external_exports3.object({
166129
166778
  token_endpoint: external_exports3.string().optional(),
166130
166779
  agent_auth: external_exports3.object({
@@ -166179,238 +166828,6 @@ var agentZeroSessionSchema = external_exports3.object({
166179
166828
  user: external_exports3.object({ id: external_exports3.string() }).loose(),
166180
166829
  walletAddress: external_exports3.string().optional()
166181
166830
  }).loose();
166182
- var refreshResponseSchema = external_exports3.object({
166183
- accessToken: external_exports3.string(),
166184
- refreshToken: external_exports3.string()
166185
- });
166186
- var tryRefreshSession = async (client, callerSignal) => {
166187
- const credentials = client.getCredentials();
166188
- if (credentials.kind !== "session") return false;
166189
- if (!credentials.refreshToken) return false;
166190
- const url4 = `${client.baseUrl.replace(/\/$/, "")}/v1/auth/refresh`;
166191
- const timeoutSignal = AbortSignal.timeout(client.timeout);
166192
- const signal = callerSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : timeoutSignal;
166193
- let response;
166194
- try {
166195
- response = await client.fetchImpl(url4, {
166196
- method: "POST",
166197
- headers: { "content-type": "application/json" },
166198
- body: JSON.stringify({ refreshToken: credentials.refreshToken }),
166199
- signal
166200
- });
166201
- } catch {
166202
- return false;
166203
- }
166204
- if (!response.ok) return false;
166205
- let body;
166206
- try {
166207
- body = await response.json();
166208
- } catch {
166209
- return false;
166210
- }
166211
- const parsed = refreshResponseSchema.safeParse(body);
166212
- if (!parsed.success) return false;
166213
- await client.applyRefreshedTokens({
166214
- accessToken: parsed.data.accessToken,
166215
- refreshToken: parsed.data.refreshToken
166216
- });
166217
- return true;
166218
- };
166219
- var buildSessionAuthHeaders = (accessToken) => ({
166220
- authorization: `Bearer ${accessToken}`
166221
- });
166222
- var buildCanonicalMessage = (method, pathWithQuery, body, timestamp, nonce2) => {
166223
- const bodyHash = createHash("sha256").update(body ?? "").digest("hex");
166224
- return `${method}:${pathWithQuery}:${bodyHash}:${timestamp}:${nonce2}`;
166225
- };
166226
- var buildAccountAuthHeaders = async (account, method, pathWithQuery, body) => {
166227
- const timestamp = Math.floor(Date.now() / 1e3).toString();
166228
- const nonce2 = crypto.randomUUID();
166229
- const message2 = buildCanonicalMessage(
166230
- method,
166231
- pathWithQuery,
166232
- body,
166233
- timestamp,
166234
- nonce2
166235
- );
166236
- const signature2 = await account.signMessage({ message: message2 });
166237
- return {
166238
- "x-zero-address": account.address,
166239
- "x-zero-timestamp": timestamp,
166240
- "x-zero-nonce": nonce2,
166241
- "x-zero-signature": signature2
166242
- };
166243
- };
166244
- var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 502, 503, 504]);
166245
- var sleep = (ms, signal) => new Promise((resolve7, reject) => {
166246
- if (signal?.aborted) {
166247
- reject(new DOMException("Aborted", "AbortError"));
166248
- return;
166249
- }
166250
- const timer = setTimeout(() => {
166251
- signal?.removeEventListener("abort", onAbort);
166252
- resolve7();
166253
- }, ms);
166254
- const onAbort = () => {
166255
- clearTimeout(timer);
166256
- reject(new DOMException("Aborted", "AbortError"));
166257
- };
166258
- signal?.addEventListener("abort", onAbort, { once: true });
166259
- });
166260
- var buildPathWithQuery = (path3, query) => {
166261
- const normalizedPath = path3.startsWith("/") ? path3 : `/${path3}`;
166262
- if (!query) return normalizedPath;
166263
- const params = new URLSearchParams();
166264
- for (const [key, value] of Object.entries(query)) {
166265
- if (value !== void 0) params.set(key, String(value));
166266
- }
166267
- const qs = params.toString();
166268
- return qs ? `${normalizedPath}?${qs}` : normalizedPath;
166269
- };
166270
- var joinUrl = (baseUrl, pathWithQuery) => {
166271
- const base3 = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
166272
- return `${base3}${pathWithQuery}`;
166273
- };
166274
- var buildHeaders = async (client, method, pathWithQuery, hasBody, bodyStr) => {
166275
- const headers = { ...client.defaultHeaders };
166276
- if (hasBody) headers["content-type"] = "application/json";
166277
- headers["x-zero-sdk-version"] = SDK_VERSION2;
166278
- if (client.apiVersion) headers["x-zero-api-version"] = client.apiVersion;
166279
- const credentials = client.getCredentials();
166280
- if (credentials.kind === "session") {
166281
- Object.assign(headers, buildSessionAuthHeaders(credentials.accessToken));
166282
- } else if (credentials.kind === "account") {
166283
- const signed = await buildAccountAuthHeaders(
166284
- credentials.account,
166285
- method,
166286
- pathWithQuery,
166287
- bodyStr
166288
- );
166289
- Object.assign(headers, signed);
166290
- }
166291
- return headers;
166292
- };
166293
- var parseErrorBody = async (response) => {
166294
- try {
166295
- return await response.clone().json();
166296
- } catch {
166297
- try {
166298
- return await response.text();
166299
- } catch {
166300
- return void 0;
166301
- }
166302
- }
166303
- };
166304
- var errorMessageFromBody = (body, status) => {
166305
- if (body !== null && typeof body === "object" && "error" in body && typeof body.error === "string") {
166306
- return body.error;
166307
- }
166308
- return `HTTP ${status}`;
166309
- };
166310
- var request2 = async (client, opts, schema) => {
166311
- const pathWithQuery = buildPathWithQuery(opts.path, opts.query);
166312
- const url4 = joinUrl(client.baseUrl, pathWithQuery);
166313
- const hasBody = opts.body !== void 0;
166314
- const bodyStr = hasBody ? JSON.stringify(
166315
- opts.body,
166316
- (_k, v) => typeof v === "bigint" ? v.toString() : v
166317
- ) : void 0;
166318
- const configuredFetch = buildConfiguredFetch(client);
166319
- const performOnce = async () => configuredFetch(url4, {
166320
- method: opts.method,
166321
- headers: await buildHeaders(
166322
- client,
166323
- opts.method,
166324
- pathWithQuery,
166325
- hasBody,
166326
- bodyStr
166327
- ),
166328
- body: bodyStr,
166329
- signal: opts.signal
166330
- });
166331
- let refreshAttempted = false;
166332
- const executeWithRefresh = async () => {
166333
- const response = await performOnce();
166334
- if (response.status !== 401 || refreshAttempted) return response;
166335
- const credentials = client.getCredentials();
166336
- if (credentials.kind !== "session") return response;
166337
- refreshAttempted = true;
166338
- const refreshed = await client.refreshSessionOnce(opts.signal);
166339
- if (!refreshed) return response;
166340
- return await performOnce();
166341
- };
166342
- let lastNetworkError;
166343
- for (let attempt = 0; attempt <= client.maxRetries; attempt++) {
166344
- let response;
166345
- try {
166346
- response = await executeWithRefresh();
166347
- } catch (e) {
166348
- if (e instanceof ZeroError) throw e;
166349
- lastNetworkError = e;
166350
- if (attempt < client.maxRetries) {
166351
- try {
166352
- await sleep(2 ** attempt * 200, opts.signal);
166353
- } catch (sleepErr) {
166354
- throw new ZeroApiError("Request aborted by caller", {
166355
- status: 0,
166356
- cause: sleepErr
166357
- });
166358
- }
166359
- continue;
166360
- }
166361
- throw new ZeroApiError("Network request failed", {
166362
- status: 0,
166363
- cause: e
166364
- });
166365
- }
166366
- if (response.ok) {
166367
- let text;
166368
- try {
166369
- text = await response.text();
166370
- } catch (e) {
166371
- throw new ZeroApiError("Response body read failed", {
166372
- status: response.status,
166373
- cause: e
166374
- });
166375
- }
166376
- let json3;
166377
- try {
166378
- json3 = JSON.parse(text);
166379
- } catch {
166380
- }
166381
- const parsed = schema.safeParse(json3);
166382
- if (!parsed.success) {
166383
- throw new ZeroValidationError(
166384
- `Response did not match expected schema for ${opts.method} ${opts.path}`,
166385
- { cause: parsed.error, body: text }
166386
- );
166387
- }
166388
- return parsed.data;
166389
- }
166390
- if (RETRYABLE_STATUS.has(response.status) && attempt < client.maxRetries) {
166391
- try {
166392
- await sleep(2 ** attempt * 200, opts.signal);
166393
- } catch (sleepErr) {
166394
- throw new ZeroApiError("Request aborted by caller", {
166395
- status: 0,
166396
- cause: sleepErr
166397
- });
166398
- }
166399
- continue;
166400
- }
166401
- const errorBody = await parseErrorBody(response);
166402
- const requestId = response.headers.get("x-request-id") ?? void 0;
166403
- throw new ZeroApiError(errorMessageFromBody(errorBody, response.status), {
166404
- status: response.status,
166405
- requestId,
166406
- body: errorBody
166407
- });
166408
- }
166409
- throw new ZeroApiError("Exhausted retries", {
166410
- status: 0,
166411
- cause: lastNetworkError
166412
- });
166413
- };
166414
166831
  var JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
166415
166832
  var trimSlash = (value) => value.replace(/\/$/, "");
166416
166833
  var wrapNetworkError = (url4, err) => {
@@ -167422,12 +167839,14 @@ var Wallet = class {
167422
167839
  );
167423
167840
  let sum = 0n;
167424
167841
  const errors = [];
167842
+ const breakdown = {};
167425
167843
  for (let i = 0; i < settled.length; i++) {
167426
167844
  const result = settled[i];
167427
167845
  const chainName = chains[i];
167428
167846
  if (!result || !chainName) continue;
167429
167847
  if (result.status === "fulfilled") {
167430
167848
  sum += result.value;
167849
+ breakdown[chainName] = formatUnits(result.value, USDC_DECIMALS);
167431
167850
  } else {
167432
167851
  errors.push({ chain: chainName, message: errorMessage(result.reason) });
167433
167852
  }
@@ -167439,11 +167858,45 @@ var Wallet = class {
167439
167858
  }
167440
167859
  const balance = {
167441
167860
  amount: formatUnits(sum, USDC_DECIMALS),
167442
- asset: "USDC"
167861
+ asset: "USDC",
167862
+ breakdown
167443
167863
  };
167444
167864
  if (errors.length > 0) balance.errors = errors;
167445
167865
  return balance;
167446
167866
  };
167867
+ // Move the wallet's Tempo USDC back to Base (a self-transfer — funds never
167868
+ // leave the wallet, only the chain changes, minus ~$0.02 bridge fees).
167869
+ // Session (managed) wallets go through the server's bridge endpoint;
167870
+ // account (BYO) wallets execute client-side via Relay with the local key.
167871
+ // Resolves when the bridge is initiated; the Base balance lands seconds
167872
+ // later — poll `balance()` to observe it.
167873
+ consolidate = async (opts = {}) => {
167874
+ const credentials = this.client.getCredentials();
167875
+ if (credentials.kind === "session") {
167876
+ return callBridgeEndpoint(this.client, {
167877
+ sourceChain: "tempo",
167878
+ destinationChain: "base",
167879
+ amountUsdc: opts.amount,
167880
+ walletAddress: opts.address,
167881
+ signal: opts.signal
167882
+ });
167883
+ }
167884
+ if (credentials.kind === "account") {
167885
+ if (opts.address && opts.address.toLowerCase() !== credentials.account.address.toLowerCase()) {
167886
+ throw new ZeroWalletError(
167887
+ `consolidate({ address }) with account credentials can only target the account's own wallet (${credentials.account.address})`
167888
+ );
167889
+ }
167890
+ return executeByoReverseBridge({
167891
+ account: credentials.account,
167892
+ amountUsdc: opts.amount,
167893
+ signal: opts.signal
167894
+ });
167895
+ }
167896
+ throw new ZeroWalletError(
167897
+ "consolidate() requires a client constructed with an account or session credentials"
167898
+ );
167899
+ };
167447
167900
  // All wallets linked to the authenticated session user. Session-only.
167448
167901
  list = (opts = {}) => request2(
167449
167902
  this.client,
@@ -168324,7 +168777,7 @@ var placeholderFor = (fieldName, propSchema) => String(sampleValueFor(fieldName,
168324
168777
 
168325
168778
  // package.json
168326
168779
  var package_default2 = {
168327
- version: "1.25.0"};
168780
+ version: "1.26.0"};
168328
168781
 
168329
168782
  // src/app.ts
168330
168783
  init_esm_shims();
@@ -172495,6 +172948,7 @@ var readStdin = async () => {
172495
172948
 
172496
172949
  // src/commands/wallet-command.ts
172497
172950
  var PRIVATE_KEY_PATTERN = /^0x[0-9a-fA-F]{64}$/;
172951
+ var TEMPO_DUST_USDC = 0.01;
172498
172952
  var parseProvider = (raw) => raw === "stripe" ? "stripe" : "coinbase";
172499
172953
  var safeFundingUrl = async (client, opts) => {
172500
172954
  try {
@@ -172526,6 +172980,19 @@ var walletBalanceCommand = (appContext) => new Command("balance").description("S
172526
172980
  address: target
172527
172981
  });
172528
172982
  console.log(`${balance.amount} ${balance.asset}`);
172983
+ const tempoAmount = balance.breakdown?.tempo;
172984
+ if (tempoAmount && Number.parseFloat(tempoAmount) >= TEMPO_DUST_USDC) {
172985
+ console.log(
172986
+ color.dim(
172987
+ ` ${balance.breakdown?.base ?? "?"} USDC on Base network \xB7 ${tempoAmount} USDC on Tempo network`
172988
+ )
172989
+ );
172990
+ console.log(
172991
+ color.dim(
172992
+ " Zero bridges between networks automatically when you fetch; `zero wallet consolidate` moves Tempo funds back now."
172993
+ )
172994
+ );
172995
+ }
172529
172996
  if (balance.errors?.length) {
172530
172997
  const failed = balance.errors.map((e) => e.chain).join(", ");
172531
172998
  console.error(
@@ -172538,6 +173005,66 @@ var walletBalanceCommand = (appContext) => new Command("balance").description("S
172538
173005
  process.exitCode = 1;
172539
173006
  }
172540
173007
  });
173008
+ var CONSOLIDATE_FILL_TIMEOUT_MS = 9e4;
173009
+ var CONSOLIDATE_POLL_INTERVAL_MS = 2e3;
173010
+ var walletConsolidateCommand = (appContext) => new Command("consolidate").description(
173011
+ "Move your wallet's USDC from the Tempo network back to the Base network (self-transfer, ~$0.02 bridge fee)"
173012
+ ).option(
173013
+ "--amount <usdc>",
173014
+ "Amount in USDC to move (defaults to the full Tempo balance)"
173015
+ ).option(
173016
+ "--address <address>",
173017
+ "Consolidate a specific wallet on your profile (defaults to the primary)"
173018
+ ).action(async (options) => {
173019
+ const { client } = appContext.services;
173020
+ if (options.address && !isAddress(options.address)) {
173021
+ console.error("Invalid address (expected 0x + 40 hex chars).");
173022
+ process.exitCode = 1;
173023
+ return;
173024
+ }
173025
+ try {
173026
+ const address5 = options.address ?? await client.wallet.address();
173027
+ const before = await client.wallet.balance({ address: address5 });
173028
+ const tempoBefore = before.breakdown?.tempo ?? "0";
173029
+ if (!options.amount && Number.parseFloat(tempoBefore) < TEMPO_DUST_USDC) {
173030
+ console.log("No USDC on the Tempo network \u2014 nothing to consolidate.");
173031
+ return;
173032
+ }
173033
+ console.log(
173034
+ `Bridging ${options.amount ?? tempoBefore} USDC from the Tempo network to the Base network (~10s)...`
173035
+ );
173036
+ await client.wallet.consolidate({
173037
+ amount: options.amount,
173038
+ address: options.address
173039
+ });
173040
+ const baseBefore = Number.parseFloat(before.breakdown?.base ?? "0");
173041
+ const deadline = Date.now() + CONSOLIDATE_FILL_TIMEOUT_MS;
173042
+ while (Date.now() < deadline) {
173043
+ await new Promise(
173044
+ (resolve7) => setTimeout(resolve7, CONSOLIDATE_POLL_INTERVAL_MS)
173045
+ );
173046
+ try {
173047
+ const now = await client.wallet.balance({ address: address5 });
173048
+ const baseNow = Number.parseFloat(now.breakdown?.base ?? "0");
173049
+ if (baseNow > baseBefore) {
173050
+ console.log(
173051
+ `Done. Base network: ${now.breakdown?.base} USDC \xB7 Tempo network: ${now.breakdown?.tempo ?? "0"} USDC (total ${now.amount}).`
173052
+ );
173053
+ return;
173054
+ }
173055
+ } catch {
173056
+ }
173057
+ }
173058
+ console.log(
173059
+ "Bridge initiated; the fill hasn't landed yet. Check `zero wallet balance` in a moment."
173060
+ );
173061
+ } catch (err) {
173062
+ console.error(
173063
+ err instanceof Error ? err.message : "Consolidation failed"
173064
+ );
173065
+ process.exitCode = 1;
173066
+ }
173067
+ });
172541
173068
  var readPrivateKeyFromStdin = async () => {
172542
173069
  if (process.stdin.isTTY) {
172543
173070
  console.error(
@@ -172998,6 +173525,7 @@ var walletNicknameCommand = (appContext) => new Command("nickname").description(
172998
173525
  var walletCommand = (appContext) => {
172999
173526
  const cmd = new Command("wallet").description("Manage your wallet");
173000
173527
  cmd.addCommand(walletBalanceCommand(appContext));
173528
+ cmd.addCommand(walletConsolidateCommand(appContext));
173001
173529
  cmd.addCommand(walletFundCommand(appContext));
173002
173530
  cmd.addCommand(walletAddressCommand(appContext));
173003
173531
  cmd.addCommand(walletSetCommand(appContext));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxyz/cli",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zero": "dist/index.js",