@zeroxyz/cli 1.25.0 → 1.26.1

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 +843 -310
  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-YB3TVCUC.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.1"
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";
@@ -164469,6 +165044,10 @@ var resolveDefaultPrefund = (displayCostAmount) => {
164469
165044
  return bounded.toFixed(6).replace(/\.?0+$/, "");
164470
165045
  };
164471
165046
  var isUintString = (v) => typeof v === "string" && v.length > 0 && v.length <= MAX_AMOUNT_DIGITS && UINT_RE.test(v);
165047
+ var buildTempoPaymentMethods = (parameters) => [
165048
+ tempo2(parameters),
165049
+ tempo2.sessionLegacy.method(parameters)
165050
+ ];
164472
165051
  var coerceTempoChainId = (raw) => {
164473
165052
  const id = typeof raw === "number" && Number.isFinite(raw) ? raw : typeof raw === "string" && /^-?[0-9]+$/.test(raw) ? Number(raw) : null;
164474
165053
  if (id === TEMPO_CHAIN_ID2) return { id, chain: "tempo" };
@@ -164720,6 +165299,9 @@ var Payments = class {
164720
165299
  });
164721
165300
  }
164722
165301
  if (balance !== null && balance < rawAmountUnits) {
165302
+ if (balanceChain === "base" && await this.tryJitReverseBridge(account, rawAmountUnits, balance)) {
165303
+ return;
165304
+ }
164723
165305
  const have = formatUnits(balance, 6);
164724
165306
  const requiredAmount = formatUnits(rawAmountUnits, 6);
164725
165307
  preflightError = new ZeroInsufficientFundsError(
@@ -164855,43 +165437,44 @@ var Payments = class {
164855
165437
  let capturedChallenge;
164856
165438
  let channelEntry;
164857
165439
  let openTimeCumulative;
165440
+ const handleChannelUpdate = (entry) => {
165441
+ let cumulative;
165442
+ if (typeof entry.cumulativeAmount === "bigint") {
165443
+ cumulative = entry.cumulativeAmount;
165444
+ } else {
165445
+ cumulative = 0n;
165446
+ this.client.logger?.({
165447
+ level: "warn",
165448
+ message: "mppx emitted non-bigint cumulativeAmount; coerced to 0n",
165449
+ meta: { actualType: typeof entry.cumulativeAmount }
165450
+ });
165451
+ }
165452
+ if (openTimeCumulative === void 0 && entry.opened) {
165453
+ openTimeCumulative = cumulative;
165454
+ }
165455
+ const wasOpened = channelEntry?.opened ?? false;
165456
+ channelEntry = {
165457
+ channelId: entry.channelId,
165458
+ // mppx >=0.8 renamed the channel-entry escrow field from
165459
+ // `escrowContract` to `escrow` (see SessionManager
165460
+ // entryFromSnapshot); the legacy client still emits
165461
+ // `escrowContract`. Read `escrow` first, fall back to the old
165462
+ // name, then to any value we already latched so a later
165463
+ // escrow-less emit can't blank an open channel's escrow.
165464
+ escrowContract: entry.escrow ?? entry.escrowContract ?? channelEntry?.escrowContract ?? "0x0000000000000000000000000000000000000000",
165465
+ cumulativeAmount: cumulative,
165466
+ opened: wasOpened || entry.opened
165467
+ };
165468
+ };
165469
+ const tempoMethodParams = {
165470
+ account,
165471
+ maxDeposit: input.maxPay ?? resolveDefaultPrefund(input.displayCostAmount),
165472
+ onChannelUpdate: handleChannelUpdate
165473
+ };
164858
165474
  const mppx = Mppx_exports.create({
164859
165475
  polyfill: false,
164860
165476
  fetch: configuredFetch,
164861
- methods: [
164862
- tempo2({
164863
- account,
164864
- maxDeposit: input.maxPay ?? resolveDefaultPrefund(input.displayCostAmount),
164865
- onChannelUpdate: (entry) => {
164866
- let cumulative;
164867
- if (typeof entry.cumulativeAmount === "bigint") {
164868
- cumulative = entry.cumulativeAmount;
164869
- } else {
164870
- cumulative = 0n;
164871
- this.client.logger?.({
164872
- level: "warn",
164873
- message: "mppx emitted non-bigint cumulativeAmount; coerced to 0n",
164874
- meta: { actualType: typeof entry.cumulativeAmount }
164875
- });
164876
- }
164877
- if (openTimeCumulative === void 0 && entry.opened) {
164878
- openTimeCumulative = cumulative;
164879
- }
164880
- const wasOpened = channelEntry?.opened ?? false;
164881
- channelEntry = {
164882
- channelId: entry.channelId,
164883
- // mppx >=0.8 renamed the channel-entry escrow field from
164884
- // `escrowContract` to `escrow` (see SessionManager
164885
- // entryFromSnapshot). Read `escrow` first, fall back to the
164886
- // old name, then to any value we already latched so a later
164887
- // escrow-less emit can't blank an open channel's escrow.
164888
- escrowContract: entry.escrow ?? entry.escrowContract ?? channelEntry?.escrowContract ?? "0x0000000000000000000000000000000000000000",
164889
- cumulativeAmount: cumulative,
164890
- opened: wasOpened || entry.opened
164891
- };
164892
- }
164893
- })
164894
- ],
165477
+ methods: buildTempoPaymentMethods(tempoMethodParams),
164895
165478
  onChallenge: async (challenge2) => {
164896
165479
  capturedChallenge = challenge2;
164897
165480
  capturedAmount = await this.prepareTempoFunds(
@@ -165075,6 +165658,28 @@ var Payments = class {
165075
165658
  level: "info",
165076
165659
  message: `Bridging ${formatUnits(bridgeAmount, 6)} USDC from Base to Tempo...`
165077
165660
  });
165661
+ if (this.client.getCredentials().kind === "session") {
165662
+ const tempoBalanceBefore = await this.readUsdcBalance(
165663
+ account.address,
165664
+ "tempo"
165665
+ );
165666
+ const response = await callBridgeEndpoint(this.client, {
165667
+ sourceChain: "base",
165668
+ destinationChain: "tempo",
165669
+ amountUsdc: formatUnits(bridgeAmount, 6)
165670
+ });
165671
+ const filled = await this.waitForUsdcBalance(
165672
+ account.address,
165673
+ "tempo",
165674
+ tempoBalanceBefore + requiredAmount
165675
+ );
165676
+ if (!filled) {
165677
+ throw new ZeroPaymentError(
165678
+ "Base\u2192Tempo bridge did not fill in time; funds may still arrive \u2014 retry shortly"
165679
+ );
165680
+ }
165681
+ return response.txHash ?? null;
165682
+ }
165078
165683
  const walletClient = createWalletClient({
165079
165684
  account,
165080
165685
  chain: base,
@@ -165105,6 +165710,59 @@ var Payments = class {
165105
165710
  });
165106
165711
  return bridgeTxHash;
165107
165712
  };
165713
+ // Recover an x402 shortfall from the wallet's Tempo balance. Returns true
165714
+ // once the Base balance covers `requiredRaw`; false means "couldn't help"
165715
+ // (not enough combined funds, bridge failure, or fill timeout) and the
165716
+ // caller falls through to the normal insufficient-funds abort. Deliberately
165717
+ // swallows errors — this path must never make a failing payment fail worse.
165718
+ tryJitReverseBridge = async (account, requiredRaw, baseBalance) => {
165719
+ try {
165720
+ const tempoBalance = await this.readUsdcBalance(account.address, "tempo");
165721
+ const usable = tempoBalance - JIT_REVERSE_FEE_MARGIN;
165722
+ if (usable <= 0n || baseBalance + usable < requiredRaw) return false;
165723
+ this.client.logger?.({
165724
+ level: "info",
165725
+ message: `Bridging ${formatUnits(tempoBalance, 6)} USDC from Tempo back to Base to cover this payment...`
165726
+ });
165727
+ if (this.client.getCredentials().kind === "session") {
165728
+ await callBridgeEndpoint(this.client, {
165729
+ sourceChain: "tempo",
165730
+ destinationChain: "base"
165731
+ });
165732
+ } else {
165733
+ await executeByoReverseBridge({ account });
165734
+ }
165735
+ return await this.waitForUsdcBalance(
165736
+ account.address,
165737
+ "base",
165738
+ requiredRaw
165739
+ );
165740
+ } catch (err) {
165741
+ this.client.logger?.({
165742
+ level: "warn",
165743
+ message: "JIT Tempo\u2192Base bridge failed; falling back to insufficient-funds",
165744
+ meta: { error: err instanceof Error ? err.message : String(err) }
165745
+ });
165746
+ return false;
165747
+ }
165748
+ };
165749
+ // Poll until the address's USDC balance on `chain` reaches `targetRaw`.
165750
+ // Bridge fills typically land in 5-15s; the timeout is a generous multiple.
165751
+ waitForUsdcBalance = async (address5, chain2, targetRaw, timeoutMs = BRIDGE_FILL_TIMEOUT_MS) => {
165752
+ const deadline = Date.now() + timeoutMs;
165753
+ while (Date.now() < deadline) {
165754
+ await new Promise(
165755
+ (resolve7) => setTimeout(resolve7, BRIDGE_FILL_POLL_INTERVAL_MS)
165756
+ );
165757
+ try {
165758
+ if (await this.readUsdcBalance(address5, chain2) >= targetRaw) {
165759
+ return true;
165760
+ }
165761
+ } catch {
165762
+ }
165763
+ }
165764
+ return false;
165765
+ };
165108
165766
  ensureRelayClient = () => {
165109
165767
  if (this.relayInitialized) return;
165110
165768
  createClient2({
@@ -166121,10 +166779,6 @@ var clientFetch = async (client, url4, opts = {}) => {
166121
166779
  result.capabilityResolution = capabilityResolution;
166122
166780
  return result;
166123
166781
  };
166124
- var package_default = {
166125
- version: "0.18.0"
166126
- };
166127
- var SDK_VERSION2 = package_default.version;
166128
166782
  var agentAuthMetadataSchema = external_exports3.object({
166129
166783
  token_endpoint: external_exports3.string().optional(),
166130
166784
  agent_auth: external_exports3.object({
@@ -166179,238 +166833,6 @@ var agentZeroSessionSchema = external_exports3.object({
166179
166833
  user: external_exports3.object({ id: external_exports3.string() }).loose(),
166180
166834
  walletAddress: external_exports3.string().optional()
166181
166835
  }).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
166836
  var JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
166415
166837
  var trimSlash = (value) => value.replace(/\/$/, "");
166416
166838
  var wrapNetworkError = (url4, err) => {
@@ -167422,12 +167844,14 @@ var Wallet = class {
167422
167844
  );
167423
167845
  let sum = 0n;
167424
167846
  const errors = [];
167847
+ const breakdown = {};
167425
167848
  for (let i = 0; i < settled.length; i++) {
167426
167849
  const result = settled[i];
167427
167850
  const chainName = chains[i];
167428
167851
  if (!result || !chainName) continue;
167429
167852
  if (result.status === "fulfilled") {
167430
167853
  sum += result.value;
167854
+ breakdown[chainName] = formatUnits(result.value, USDC_DECIMALS);
167431
167855
  } else {
167432
167856
  errors.push({ chain: chainName, message: errorMessage(result.reason) });
167433
167857
  }
@@ -167439,11 +167863,45 @@ var Wallet = class {
167439
167863
  }
167440
167864
  const balance = {
167441
167865
  amount: formatUnits(sum, USDC_DECIMALS),
167442
- asset: "USDC"
167866
+ asset: "USDC",
167867
+ breakdown
167443
167868
  };
167444
167869
  if (errors.length > 0) balance.errors = errors;
167445
167870
  return balance;
167446
167871
  };
167872
+ // Move the wallet's Tempo USDC back to Base (a self-transfer — funds never
167873
+ // leave the wallet, only the chain changes, minus ~$0.02 bridge fees).
167874
+ // Session (managed) wallets go through the server's bridge endpoint;
167875
+ // account (BYO) wallets execute client-side via Relay with the local key.
167876
+ // Resolves when the bridge is initiated; the Base balance lands seconds
167877
+ // later — poll `balance()` to observe it.
167878
+ consolidate = async (opts = {}) => {
167879
+ const credentials = this.client.getCredentials();
167880
+ if (credentials.kind === "session") {
167881
+ return callBridgeEndpoint(this.client, {
167882
+ sourceChain: "tempo",
167883
+ destinationChain: "base",
167884
+ amountUsdc: opts.amount,
167885
+ walletAddress: opts.address,
167886
+ signal: opts.signal
167887
+ });
167888
+ }
167889
+ if (credentials.kind === "account") {
167890
+ if (opts.address && opts.address.toLowerCase() !== credentials.account.address.toLowerCase()) {
167891
+ throw new ZeroWalletError(
167892
+ `consolidate({ address }) with account credentials can only target the account's own wallet (${credentials.account.address})`
167893
+ );
167894
+ }
167895
+ return executeByoReverseBridge({
167896
+ account: credentials.account,
167897
+ amountUsdc: opts.amount,
167898
+ signal: opts.signal
167899
+ });
167900
+ }
167901
+ throw new ZeroWalletError(
167902
+ "consolidate() requires a client constructed with an account or session credentials"
167903
+ );
167904
+ };
167447
167905
  // All wallets linked to the authenticated session user. Session-only.
167448
167906
  list = (opts = {}) => request2(
167449
167907
  this.client,
@@ -168324,7 +168782,7 @@ var placeholderFor = (fieldName, propSchema) => String(sampleValueFor(fieldName,
168324
168782
 
168325
168783
  // package.json
168326
168784
  var package_default2 = {
168327
- version: "1.25.0"};
168785
+ version: "1.26.1"};
168328
168786
 
168329
168787
  // src/app.ts
168330
168788
  init_esm_shims();
@@ -172495,6 +172953,7 @@ var readStdin = async () => {
172495
172953
 
172496
172954
  // src/commands/wallet-command.ts
172497
172955
  var PRIVATE_KEY_PATTERN = /^0x[0-9a-fA-F]{64}$/;
172956
+ var TEMPO_DUST_USDC = 0.01;
172498
172957
  var parseProvider = (raw) => raw === "stripe" ? "stripe" : "coinbase";
172499
172958
  var safeFundingUrl = async (client, opts) => {
172500
172959
  try {
@@ -172526,6 +172985,19 @@ var walletBalanceCommand = (appContext) => new Command("balance").description("S
172526
172985
  address: target
172527
172986
  });
172528
172987
  console.log(`${balance.amount} ${balance.asset}`);
172988
+ const tempoAmount = balance.breakdown?.tempo;
172989
+ if (tempoAmount && Number.parseFloat(tempoAmount) >= TEMPO_DUST_USDC) {
172990
+ console.log(
172991
+ color.dim(
172992
+ ` ${balance.breakdown?.base ?? "?"} USDC on Base network \xB7 ${tempoAmount} USDC on Tempo network`
172993
+ )
172994
+ );
172995
+ console.log(
172996
+ color.dim(
172997
+ " Zero bridges between networks automatically when you fetch; `zero wallet consolidate` moves Tempo funds back now."
172998
+ )
172999
+ );
173000
+ }
172529
173001
  if (balance.errors?.length) {
172530
173002
  const failed = balance.errors.map((e) => e.chain).join(", ");
172531
173003
  console.error(
@@ -172538,6 +173010,66 @@ var walletBalanceCommand = (appContext) => new Command("balance").description("S
172538
173010
  process.exitCode = 1;
172539
173011
  }
172540
173012
  });
173013
+ var CONSOLIDATE_FILL_TIMEOUT_MS = 9e4;
173014
+ var CONSOLIDATE_POLL_INTERVAL_MS = 2e3;
173015
+ var walletConsolidateCommand = (appContext) => new Command("consolidate").description(
173016
+ "Move your wallet's USDC from the Tempo network back to the Base network (self-transfer, ~$0.02 bridge fee)"
173017
+ ).option(
173018
+ "--amount <usdc>",
173019
+ "Amount in USDC to move (defaults to the full Tempo balance)"
173020
+ ).option(
173021
+ "--address <address>",
173022
+ "Consolidate a specific wallet on your profile (defaults to the primary)"
173023
+ ).action(async (options) => {
173024
+ const { client } = appContext.services;
173025
+ if (options.address && !isAddress(options.address)) {
173026
+ console.error("Invalid address (expected 0x + 40 hex chars).");
173027
+ process.exitCode = 1;
173028
+ return;
173029
+ }
173030
+ try {
173031
+ const address5 = options.address ?? await client.wallet.address();
173032
+ const before = await client.wallet.balance({ address: address5 });
173033
+ const tempoBefore = before.breakdown?.tempo ?? "0";
173034
+ if (!options.amount && Number.parseFloat(tempoBefore) < TEMPO_DUST_USDC) {
173035
+ console.log("No USDC on the Tempo network \u2014 nothing to consolidate.");
173036
+ return;
173037
+ }
173038
+ console.log(
173039
+ `Bridging ${options.amount ?? tempoBefore} USDC from the Tempo network to the Base network (~10s)...`
173040
+ );
173041
+ await client.wallet.consolidate({
173042
+ amount: options.amount,
173043
+ address: options.address
173044
+ });
173045
+ const baseBefore = Number.parseFloat(before.breakdown?.base ?? "0");
173046
+ const deadline = Date.now() + CONSOLIDATE_FILL_TIMEOUT_MS;
173047
+ while (Date.now() < deadline) {
173048
+ await new Promise(
173049
+ (resolve7) => setTimeout(resolve7, CONSOLIDATE_POLL_INTERVAL_MS)
173050
+ );
173051
+ try {
173052
+ const now = await client.wallet.balance({ address: address5 });
173053
+ const baseNow = Number.parseFloat(now.breakdown?.base ?? "0");
173054
+ if (baseNow > baseBefore) {
173055
+ console.log(
173056
+ `Done. Base network: ${now.breakdown?.base} USDC \xB7 Tempo network: ${now.breakdown?.tempo ?? "0"} USDC (total ${now.amount}).`
173057
+ );
173058
+ return;
173059
+ }
173060
+ } catch {
173061
+ }
173062
+ }
173063
+ console.log(
173064
+ "Bridge initiated; the fill hasn't landed yet. Check `zero wallet balance` in a moment."
173065
+ );
173066
+ } catch (err) {
173067
+ console.error(
173068
+ err instanceof Error ? err.message : "Consolidation failed"
173069
+ );
173070
+ process.exitCode = 1;
173071
+ }
173072
+ });
172541
173073
  var readPrivateKeyFromStdin = async () => {
172542
173074
  if (process.stdin.isTTY) {
172543
173075
  console.error(
@@ -172998,6 +173530,7 @@ var walletNicknameCommand = (appContext) => new Command("nickname").description(
172998
173530
  var walletCommand = (appContext) => {
172999
173531
  const cmd = new Command("wallet").description("Manage your wallet");
173000
173532
  cmd.addCommand(walletBalanceCommand(appContext));
173533
+ cmd.addCommand(walletConsolidateCommand(appContext));
173001
173534
  cmd.addCommand(walletFundCommand(appContext));
173002
173535
  cmd.addCommand(walletAddressCommand(appContext));
173003
173536
  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.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "zero": "dist/index.js",