@zeroxyz/sdk 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ All notable changes to `@zeroxyz/sdk` will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — with the pre-1.0 caveat that minor versions may include breaking changes.
6
6
 
7
+ ## 0.13.0
8
+
9
+ ### Added
10
+
11
+ - **Pay and sign from a non-primary wallet** — `client.fetch()`, `payments.pay()` / `payMpp()`, and `client.auth.signMessage/signTransaction/signTypedData` accept an optional `walletAddress` (session mode). The SDK resolves the named profile wallet for the payment intent's `from`, the managed signature, and the recorded run's attribution (`runs.create` gained the same optional `walletAddress`), so per-wallet run filters reflect which wallet acted. The wallet must be on the caller's profile. Default behavior (primary wallet) is unchanged. (ZERO-488)
12
+ - **`client.runs.list({ walletAddress })`** — narrow session run reads to specific profile wallets (string or string[]). Default remains all profile wallets; an address not on the profile is a server-side 400, never a silent intersection. (ZERO-488)
13
+ - **Wallet nicknames** — `client.wallet.setNickname({ walletAddress, nickname })` (null clears), optional `nickname` on `client.wallet.import()`, and `UserWalletDto.nickname`. Nicknames are private to the authenticated user. New export `SetNicknameOptions`. (ZERO-488)
14
+ - **`client.wallet.revokeDelegation()` / `client.wallet.reauthorizeDelegation()`** — per-wallet delegation lifecycle (default: the primary wallet). New export `DelegationOptions`. (ZERO-488)
15
+
7
16
  ## 0.12.0
8
17
 
9
18
  ### Added
package/README.md CHANGED
@@ -133,6 +133,7 @@ const result = await client.fetch(url, {
133
133
  body: JSON.stringify({ city: "tokyo" }),
134
134
  maxPay: "0.01", // USDC ceiling; an over-cap challenge aborts before signing
135
135
  capabilityId: "cap_abc", // attribute the run to a known capability
136
+ walletAddress: "0x…", // session mode: pay from this profile wallet (default: primary)
136
137
  signal: controller.signal,
137
138
  });
138
139
 
@@ -230,20 +231,32 @@ await client.wallet.list(); // every wallet linked to the session user
230
231
  await client.wallet.provision(); // create the user's managed wallet (idempotent)
231
232
 
232
233
  // Link an existing self-custody wallet by private key (0x prefix optional).
233
- // Pass makePrimary to have it pay for the user's calls from now on.
234
+ // Pass makePrimary to have it pay for the user's calls from now on, and an
235
+ // optional nickname (owner-private display name, max 64 chars).
234
236
  const imported = await client.wallet.import({
235
237
  privateKey: "0x…",
236
238
  makePrimary: true,
239
+ nickname: "Trading bot",
237
240
  });
238
241
 
239
242
  // Switch which linked wallet is primary, or unlink one. remove() rejects the
240
243
  // primary wallet and any wallet holding a balance — reassign / drain first.
241
244
  await client.wallet.setPrimary({ walletAddress: imported.walletAddress });
242
245
  await client.wallet.remove({ walletAddress: "0x…" });
246
+
247
+ // Nickname a wallet (or clear with null). Nicknames are private to the
248
+ // authenticated user — they never appear on reviews or public surfaces.
249
+ await client.wallet.setNickname({ walletAddress: "0x…", nickname: "Cold storage" });
250
+
251
+ // Revoke / re-grant Zero's signing delegation per wallet (default: primary).
252
+ await client.wallet.revokeDelegation({ walletAddress: "0x…" });
253
+ await client.wallet.reauthorizeDelegation({ walletAddress: "0x…" });
243
254
  ```
244
255
 
245
256
  `wallet.address()` covers the common case (resolve — or provision — the primary managed wallet); reach for the methods above when you need the full wallet objects, explicit provisioning, or multi-wallet management. Imported wallets carry an `importedAt` timestamp on the returned wallet objects.
246
257
 
258
+ To **pay from a non-primary wallet**, pass `walletAddress` on the call itself — `client.fetch(url, { walletAddress })` (or `payments.pay` / `payMpp`) resolves the named profile wallet for the payment intent's `from`, the managed signature, and the recorded run's attribution, and `client.auth.sign*` accept `{ walletAddress }` too. The wallet must be a delegated Privy-embedded wallet on the session user's profile. Runs can likewise be filtered per wallet: `client.runs.list({ walletAddress: ["0x…"] })` (default remains all profile wallets; an address not on the profile is a 400).
259
+
247
260
  ## Namespaces
248
261
 
249
262
  Beyond `search()` and `fetch()`, the client groups the rest of the API into resource namespaces:
@@ -262,8 +275,8 @@ const cap = await client.capabilities.get("cap_abc"); // by uid
262
275
  const cap2 = await client.capabilities.get("z_Ab12cd.1"); // by search token
263
276
 
264
277
  // Wallet — balance, one-time funding URL, and (session mode) multi-wallet
265
- // management: list / provision / import / setPrimary / remove. See
266
- // "Wallet & funding" above.
278
+ // management: list / provision / import / setPrimary / remove / setNickname /
279
+ // revokeDelegation / reauthorizeDelegation. See "Wallet & funding" above.
267
280
  const { amount } = await client.wallet.balance();
268
281
  const fundUrl = await client.wallet.fundingUrl({ amount: "10" });
269
282
 
@@ -15,36 +15,40 @@ var crypto$1 = require('crypto');
15
15
  var zod = require('zod');
16
16
 
17
17
  // src/auth/managed-account.ts
18
- var createManagedAccount = (client, address) => accounts.toAccount({
19
- address,
20
- async signMessage({ message }) {
21
- return await client.auth.signMessage(
22
- { message },
23
- { expectedAddress: address }
24
- );
25
- },
26
- async signTypedData(typedData) {
27
- const td = typedData;
28
- return await client.auth.signTypedData(
29
- {
30
- domain: td.domain,
31
- types: td.types,
32
- primaryType: td.primaryType,
33
- message: td.message
34
- },
35
- { expectedAddress: address }
36
- );
37
- },
38
- async signTransaction(transaction, options) {
39
- const serialize = options?.serializer ?? viem.serializeTransaction;
40
- const unsigned = await serialize(transaction);
41
- const signature = await client.auth.signTransaction(
42
- { unsignedTransaction: unsigned },
43
- { expectedAddress: address }
44
- );
45
- return await serialize(transaction, viem.parseSignature(signature));
46
- }
47
- });
18
+ var createManagedAccount = (client, address, opts = {}) => {
19
+ const signOptions = {
20
+ // expectedAddress pins verification to this adapter's frozen address.
21
+ expectedAddress: address,
22
+ ...opts.explicitWallet ? { walletAddress: address } : {}
23
+ };
24
+ return accounts.toAccount({
25
+ address,
26
+ async signMessage({ message }) {
27
+ return await client.auth.signMessage({ message }, signOptions);
28
+ },
29
+ async signTypedData(typedData) {
30
+ const td = typedData;
31
+ return await client.auth.signTypedData(
32
+ {
33
+ domain: td.domain,
34
+ types: td.types,
35
+ primaryType: td.primaryType,
36
+ message: td.message
37
+ },
38
+ signOptions
39
+ );
40
+ },
41
+ async signTransaction(transaction, options) {
42
+ const serialize = options?.serializer ?? viem.serializeTransaction;
43
+ const unsigned = await serialize(transaction);
44
+ const signature = await client.auth.signTransaction(
45
+ { unsignedTransaction: unsigned },
46
+ signOptions
47
+ );
48
+ return await serialize(transaction, viem.parseSignature(signature));
49
+ }
50
+ });
51
+ };
48
52
  var USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
49
53
  var USDC_TEMPO = "0x20c000000000000000000000b9537d11c60e8b50";
50
54
  var TEMPO_CHAIN_ID = 4217;
@@ -641,12 +645,12 @@ var cancelBody = (response) => {
641
645
  } catch {
642
646
  }
643
647
  };
644
- var resolveAccount = async (client, overrideAccount, signal) => {
648
+ var resolveAccount = async (client, overrideAccount, signal, walletAddress) => {
645
649
  if (overrideAccount) return overrideAccount;
646
650
  const credentials = client.getCredentials();
647
651
  if (credentials.kind === "account") return credentials.account;
648
652
  if (credentials.kind === "session") {
649
- return await client.resolveManagedAccount(signal);
653
+ return await client.resolveManagedAccount(signal, walletAddress);
650
654
  }
651
655
  throw new ZeroAuthError(
652
656
  "auth_no_credentials",
@@ -692,7 +696,8 @@ var Payments = class {
692
696
  const account = await resolveAccount(
693
697
  this.client,
694
698
  input.account,
695
- input.signal
699
+ input.signal,
700
+ input.walletAddress
696
701
  );
697
702
  let capturedAmount = null;
698
703
  let capturedNetwork;
@@ -827,7 +832,8 @@ var Payments = class {
827
832
  const account = await resolveAccount(
828
833
  this.client,
829
834
  input.account,
830
- input.signal
835
+ input.signal,
836
+ input.walletAddress
831
837
  );
832
838
  const configuredFetch = buildConfiguredFetch(this.client, {
833
839
  timeoutMs: input.timeoutMs
@@ -1607,7 +1613,8 @@ var recordErrorFetchRun = async (client, opts, result, capabilityIdOverride) =>
1607
1613
  ...fetchOriginForRecord && { fetchOrigin: fetchOriginForRecord },
1608
1614
  ...result.status !== null && { status: result.status },
1609
1615
  latencyMs: result.latencyMs,
1610
- ...opts.requestSchema && { requestSchema: opts.requestSchema }
1616
+ ...opts.requestSchema && { requestSchema: opts.requestSchema },
1617
+ ...opts.walletAddress && !opts.account && { walletAddress: opts.walletAddress }
1611
1618
  });
1612
1619
  result.runId = created.runId;
1613
1620
  delete result.runTrackingSkipped;
@@ -1716,6 +1723,7 @@ var clientFetch = async (client, url, opts = {}) => {
1716
1723
  body: requestBody,
1717
1724
  maxPay: opts.maxPay,
1718
1725
  account: opts.account,
1726
+ ...opts.walletAddress && { walletAddress: opts.walletAddress },
1719
1727
  signal: opts.signal,
1720
1728
  timeoutMs: opts.timeoutMs,
1721
1729
  ...opts.displayCostAmount && {
@@ -1888,6 +1896,10 @@ var clientFetch = async (client, url, opts = {}) => {
1888
1896
  ...opts.capabilityId === void 0 ? { fetchOrigin: "direct_url" } : opts.fetchOrigin && { fetchOrigin: opts.fetchOrigin },
1889
1897
  status: response.status,
1890
1898
  latencyMs,
1899
+ // Attribute to the selected wallet in session mode. Skipped when a
1900
+ // per-call `account` override records the run — that account's own
1901
+ // EIP-191 identity is the attribution.
1902
+ ...opts.walletAddress && !opts.account && { walletAddress: opts.walletAddress },
1891
1903
  ...opts.requestSchema && { requestSchema: opts.requestSchema },
1892
1904
  ...derived?.responseSchema && {
1893
1905
  responseSchema: derived.responseSchema
@@ -1953,7 +1965,7 @@ var clientFetch = async (client, url, opts = {}) => {
1953
1965
 
1954
1966
  // package.json
1955
1967
  var package_default = {
1956
- version: "0.12.0"};
1968
+ version: "0.13.0"};
1957
1969
 
1958
1970
  // src/version.ts
1959
1971
  var SDK_VERSION = package_default.version;
@@ -2443,6 +2455,9 @@ var userWalletDtoSchema = zod.z.object({
2443
2455
  walletAddress: zod.z.string(),
2444
2456
  source: zod.z.enum(["self_custody", "privy_embedded"]),
2445
2457
  isPrimary: zod.z.boolean(),
2458
+ // Owner-chosen display name; null/absent = unnamed (show the address).
2459
+ // Optional so the SDK tolerates servers that predate nicknames.
2460
+ nickname: zod.z.string().nullable().optional(),
2446
2461
  linkedAt: zod.z.union([zod.z.string(), zod.z.date()]).nullable().optional(),
2447
2462
  importedAt: zod.z.union([zod.z.string(), zod.z.date()]).nullable().optional(),
2448
2463
  delegationGranted: zod.z.boolean(),
@@ -2660,7 +2675,10 @@ var Auth = class {
2660
2675
  {
2661
2676
  method: "POST",
2662
2677
  path: "/v1/users/me/sign-message",
2663
- body: { message: normalizeMessage(input.message) }
2678
+ body: {
2679
+ message: normalizeMessage(input.message),
2680
+ ...opts.walletAddress ? { walletAddress: opts.walletAddress } : {}
2681
+ }
2664
2682
  },
2665
2683
  signResponseSchema
2666
2684
  );
@@ -2685,7 +2703,10 @@ var Auth = class {
2685
2703
  {
2686
2704
  method: "POST",
2687
2705
  path: "/v1/users/me/sign-transaction",
2688
- body: { unsignedTransaction: input.unsignedTransaction }
2706
+ body: {
2707
+ unsignedTransaction: input.unsignedTransaction,
2708
+ ...opts.walletAddress ? { walletAddress: opts.walletAddress } : {}
2709
+ }
2689
2710
  },
2690
2711
  signResponseSchema
2691
2712
  );
@@ -2724,7 +2745,10 @@ var Auth = class {
2724
2745
  {
2725
2746
  method: "POST",
2726
2747
  path: "/v1/users/me/sign-typed-data",
2727
- body: { typedData: input }
2748
+ body: {
2749
+ typedData: input,
2750
+ ...opts.walletAddress ? { walletAddress: opts.walletAddress } : {}
2751
+ }
2728
2752
  },
2729
2753
  signResponseSchema
2730
2754
  );
@@ -3101,6 +3125,9 @@ var Runs = class {
3101
3125
  if (params.unreviewed !== void 0) query.unreviewed = params.unreviewed;
3102
3126
  if (params.limit !== void 0) query.limit = params.limit;
3103
3127
  if (params.cursor !== void 0) query.cursor = params.cursor;
3128
+ if (params.walletAddress !== void 0) {
3129
+ query.walletAddress = Array.isArray(params.walletAddress) ? params.walletAddress.join(",") : params.walletAddress;
3130
+ }
3104
3131
  return request(
3105
3132
  this.client,
3106
3133
  {
@@ -3236,6 +3263,50 @@ var Wallet = class {
3236
3263
  },
3237
3264
  userWalletDtoSchema
3238
3265
  );
3266
+ // Re-stamp Zero's signing delegation on a profile wallet (default: the
3267
+ // primary). Session-only. Naming a wallet re-authorizes THAT wallet and
3268
+ // never provisions; the server 404s an address not on the profile.
3269
+ reauthorizeDelegation = (opts = {}) => request(
3270
+ this.client,
3271
+ {
3272
+ method: "POST",
3273
+ path: "/v1/users/me/wallets/provision",
3274
+ ...opts.walletAddress ? { body: { walletAddress: opts.walletAddress } } : {},
3275
+ signal: opts.signal
3276
+ },
3277
+ userWalletDtoSchema
3278
+ );
3279
+ // Revoke Zero's signing delegation on a profile wallet (default: the
3280
+ // primary). Session-only. Managed signing on that wallet 403s until it is
3281
+ // re-authorized. Note: this stamps the server-side gate — removing Zero's
3282
+ // signer cryptographically is owner-authorized and happens in the user's
3283
+ // Privy session (the web profile page does both).
3284
+ revokeDelegation = (opts = {}) => request(
3285
+ this.client,
3286
+ {
3287
+ method: "DELETE",
3288
+ path: "/v1/users/me/wallets/delegation",
3289
+ ...opts.walletAddress ? { query: { walletAddress: opts.walletAddress } } : {},
3290
+ signal: opts.signal
3291
+ },
3292
+ userWalletDtoSchema
3293
+ );
3294
+ // Set, change, or clear (null / empty string) the owner-chosen nickname on
3295
+ // a profile wallet. Session-only. Nicknames are private to the
3296
+ // authenticated user — they never appear on reviews or public surfaces.
3297
+ setNickname = (opts) => request(
3298
+ this.client,
3299
+ {
3300
+ method: "PATCH",
3301
+ path: "/v1/users/me/wallets/nickname",
3302
+ body: {
3303
+ walletAddress: opts.walletAddress,
3304
+ nickname: opts.nickname
3305
+ },
3306
+ signal: opts.signal
3307
+ },
3308
+ userWalletDtoSchema
3309
+ );
3239
3310
  // Link an existing self-custody wallet to the session user by private key.
3240
3311
  // Session-only. 409s when the wallet is already linked (to this or another
3241
3312
  // profile — the error message says which).
@@ -3246,7 +3317,8 @@ var Wallet = class {
3246
3317
  path: "/v1/users/me/wallets/import",
3247
3318
  body: {
3248
3319
  privateKey: opts.privateKey,
3249
- ...opts.makePrimary !== void 0 ? { makePrimary: opts.makePrimary } : {}
3320
+ ...opts.makePrimary !== void 0 ? { makePrimary: opts.makePrimary } : {},
3321
+ ...opts.nickname !== void 0 ? { nickname: opts.nickname } : {}
3250
3322
  },
3251
3323
  signal: opts.signal
3252
3324
  },
@@ -3449,6 +3521,9 @@ var ZeroClient = class _ZeroClient {
3449
3521
  managedAccountInFlight = null;
3450
3522
  managedAccountAC = null;
3451
3523
  managedAccountGen = 0;
3524
+ // Explicitly-selected (non-default) managed wallets, keyed by lowercased
3525
+ // address. Same lifecycle as managedAccount: cleared on clearManagedAccount.
3526
+ managedAccountByAddress = /* @__PURE__ */ new Map();
3452
3527
  // Multi-tenant servers calling `client.fetch(url, { account: tenantAccount })`
3453
3528
  // on every request would otherwise construct a new ZeroClient (with
3454
3529
  // five namespace instances) per call. Keyed by lowercased account
@@ -3670,8 +3745,16 @@ var ZeroClient = class _ZeroClient {
3670
3745
  }
3671
3746
  this.withAccountCache.clear();
3672
3747
  };
3673
- /** @internal — used by payments.pay() in session mode. Cached, dedupes concurrent lookups. */
3674
- resolveManagedAccount = (signal) => {
3748
+ /**
3749
+ * @internal — used by payments.pay() in session mode. Cached, dedupes
3750
+ * concurrent lookups. Pass `walletAddress` to sign as a specific profile
3751
+ * wallet instead of the primary; explicit wallets are cached per address
3752
+ * and their sign requests carry the address so intent and signer agree.
3753
+ */
3754
+ resolveManagedAccount = (signal, walletAddress) => {
3755
+ if (walletAddress) {
3756
+ return this.resolveManagedAccountByAddress(walletAddress, signal);
3757
+ }
3675
3758
  if (this.managedAccount) return Promise.resolve(this.managedAccount);
3676
3759
  if (this.managedAccountInFlight) {
3677
3760
  return this.raceWithSignal(this.managedAccountInFlight, signal);
@@ -3713,6 +3796,42 @@ var ZeroClient = class _ZeroClient {
3713
3796
  });
3714
3797
  return this.raceWithSignal(this.managedAccountInFlight, signal);
3715
3798
  };
3799
+ // Explicit-wallet variant of resolveManagedAccount. No in-flight dedupe:
3800
+ // this is the rare path (per-wallet pay), the resolution is idempotent, and
3801
+ // the per-address cache absorbs repeat calls.
3802
+ resolveManagedAccountByAddress = async (walletAddress, signal) => {
3803
+ if (this.credentials.kind !== "session") {
3804
+ throw new ZeroAuthError(
3805
+ "auth_no_credentials",
3806
+ "resolveManagedAccount() requires session credentials."
3807
+ );
3808
+ }
3809
+ const canonical = viem.getAddress(walletAddress);
3810
+ const cached = this.managedAccountByAddress.get(canonical.toLowerCase());
3811
+ if (cached) return cached;
3812
+ const startedAtGen = this.managedAccountGen;
3813
+ const wallets = await this.auth.wallets({ signal });
3814
+ const match = wallets.find(
3815
+ (w) => w.walletAddress.toLowerCase() === canonical.toLowerCase()
3816
+ );
3817
+ if (!match || match.source !== "privy_embedded") {
3818
+ throw new ZeroAuthError(
3819
+ "auth_error",
3820
+ `Wallet ${canonical} is not a Privy-embedded wallet on this profile. Managed signing can only use profile wallets (see client.wallet.list()).`
3821
+ );
3822
+ }
3823
+ if (this.managedAccountGen !== startedAtGen) {
3824
+ throw new ZeroAuthError(
3825
+ "auth_session_expired",
3826
+ "Session cleared during managed-account resolution. Retry to rebuild against the current session."
3827
+ );
3828
+ }
3829
+ const acc = createManagedAccount(this, canonical, {
3830
+ explicitWallet: true
3831
+ });
3832
+ this.managedAccountByAddress.set(canonical.toLowerCase(), acc);
3833
+ return acc;
3834
+ };
3716
3835
  // Per-caller signal gate: each caller can reject on their own abort
3717
3836
  // without cancelling the shared underlying promise.
3718
3837
  raceWithSignal = (p, signal) => {
@@ -3743,6 +3862,7 @@ var ZeroClient = class _ZeroClient {
3743
3862
  this.managedAccountAC?.abort();
3744
3863
  this.managedAccountInFlight = null;
3745
3864
  this.managedAccountAC = null;
3865
+ this.managedAccountByAddress.clear();
3746
3866
  this.managedAccountGen++;
3747
3867
  };
3748
3868
  /** @internal — Read cached managed account without triggering resolution. */
@@ -3835,5 +3955,5 @@ exports.normalizeTransportEnvelopeRequest = normalizeTransportEnvelopeRequest;
3835
3955
  exports.paymentHasAnchor = paymentHasAnchor;
3836
3956
  exports.tempoChainLabelFromId = tempoChainLabelFromId;
3837
3957
  exports.unwrapTransportEnvelope = unwrapTransportEnvelope;
3838
- //# sourceMappingURL=chunk-O7424YAR.cjs.map
3839
- //# sourceMappingURL=chunk-O7424YAR.cjs.map
3958
+ //# sourceMappingURL=chunk-2A2ZZ5XR.cjs.map
3959
+ //# sourceMappingURL=chunk-2A2ZZ5XR.cjs.map