@totalis/hip4-client 0.75.0 → 0.76.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
@@ -3,6 +3,43 @@
3
3
  `@totalis/hip4-client` follows [semantic versioning](https://semver.org).
4
4
  The contract version the generated types come from is `sdkVersion` in `./version`.
5
5
 
6
+ ## 0.76.0
7
+
8
+ Vault agents (TT-2554), one Combo leg per question (TT-2605) and no legs on
9
+ unpriced markets (TT-2606). Generated from contract
10
+ `205.0.0-device-vault-agents`. Additive: 0.75 code runs unchanged, but
11
+ `createRfq` now refuses two legs from one question and legs with a null
12
+ `chance`.
13
+
14
+ ### Added
15
+
16
+ - Vault agents (TT-2554). `agent` in `createTotalis` takes a key the account
17
+ granted; it then signs the account's `TakerAccept`, `SellBack`,
18
+ `CashoutTransfer` and Exits to HyperCore or the account's wallet instead of
19
+ `signer`, so those need no wallet prompt. `signer` still names the account
20
+ and signs USDC authorizations, HyperCore actions, grants and run-off claims.
21
+ - `grantVaultAgent({ agent, validUntil })` has the account sign an
22
+ `AgentGrant` and relays it (`POST /v1/me/vault-agents`); `revokeVaultAgent`
23
+ sends `validUntil` 0, and `vaultAgents()` iterates the live agents.
24
+ - `agentGrantTypedData` in `typed-data`, checked against the vault's vectors.
25
+ - A grant or revoke's default nonce rises past any nonce this client sent
26
+ explicitly for that agent, so it isn't refused for not exceeding it.
27
+
28
+ ### Changed
29
+
30
+ - `createRfq` refuses a Combo with two legs from one native question
31
+ (TT-2605): the second such leg is named in `field_violations` as
32
+ `/legs/{i}/outcome_id` with reason `SAME_QUESTION` (`400
33
+ INVALID_REQUEST`). Makers never quoted these legs, so such an RFQ used
34
+ to open and get no quotes. Allow one leg per `native_question_id` in your
35
+ slip; standalone markets have none and always combine.
36
+ - `createRfq` refuses a leg whose market has a side with a null `chance`
37
+ (an empty, dust or one-sided book with no recent evidence), with reason
38
+ `CHANCE_UNAVAILABLE` on `/legs/{i}/outcome_id` (`400 INVALID_REQUEST`).
39
+ Makers price from the book, and on such a book one small order sets the
40
+ price. Show `chance`, not `value`, and treat `chance: null` as unpriced
41
+ (TT-2606).
42
+
6
43
  ## 0.75.0
7
44
 
8
45
  Maker capital on the stream (TT-2584), a stream that cannot hang in
@@ -2843,7 +2843,7 @@ export type components = {
2843
2843
  readonly CreateRfqCommand: {
2844
2844
  /** @description Stake in atomic USDC (6 decimals), before taker fees; must be positive. Quotes carry the same stake. */
2845
2845
  readonly stake: components["schemas"]["$defs-Amount"];
2846
- /** @description 1 to 5 legs in strictly increasing outcome_id order. */
2846
+ /** @description 1 to 5 legs in strictly increasing outcome_id order, at most one per native question. Every leg's market must have eligibility ENABLED and combo OPEN, and a chance on each side. */
2847
2847
  readonly legs: readonly components["schemas"]["$defs-Leg"][];
2848
2848
  /** @description Optional partner attribution code, from any credential. From an account a partner admitted, it must be one of that partner's active codes and needs no signature; with none, the partner's account code is recorded. From any other account it needs builder_authorization_expiry and builder_signature from the partner's registered signer and is honoured only from the code's registered browser origin, so an API-key request (no Origin) from such an account cannot send one. A refused code returns INVALID_REQUEST naming /builder_code. Revocation rejects new RFQs without vetoing an already-created RFQ. Partners get codes from Totalis. */
2849
2849
  readonly builder_code?: string;
@@ -9558,7 +9558,7 @@ export interface operations {
9558
9558
  readonly "application/json": components["schemas"]["RfqCreated"];
9559
9559
  };
9560
9560
  };
9561
- /** @description `INVALID_REQUEST`: the body is malformed, or a leg's market refuses new positions now. A refused leg is named in `field_violations` as `/legs/{i}/outcome_id`, and the message gives the reason, such as `REFERENCE_UNAVAILABLE` or `OUTSIDE_WINDOW`; the market's `eligibility` and `combo` show when it clears. */
9561
+ /** @description `INVALID_REQUEST`: the body is malformed, or a leg's market refuses new positions now. A refused leg is named in `field_violations` as `/legs/{i}/outcome_id`, and the message gives the reason, such as `REFERENCE_UNAVAILABLE`, `OUTSIDE_WINDOW`, `CHANCE_UNAVAILABLE` for a side whose `chance` is null, or `SAME_QUESTION` for a leg whose `native_question_id` an earlier leg already has; the market's `eligibility` and `combo` show when it clears. */
9562
9562
  readonly 400: components["responses"]["Error"];
9563
9563
  readonly 401: components["responses"]["Error"];
9564
9564
  readonly 403: components["responses"]["Error"];
@@ -1,5 +1,5 @@
1
1
  export declare const sdkVersion = "205.0.0-device-vault-agents";
2
- export declare const openapiSha256 = "e742feade28ca115077a4275e4a6260646278e9adcf0839987b8566a2221c8f5";
2
+ export declare const openapiSha256 = "93141e0bf50949fc37ebceef7c64555eb50a70f69c71c89c8fdc12780e0dd168";
3
3
  export declare const realtimeSha256 = "1cac740e108e84ad1979f6ccc71089efd840404cb72d1612a86edc02769637e3";
4
4
  export declare const jsonSchemaSha256: {
5
5
  readonly "account.schema.json": "5106e2d53abef190990eff113f6692dd27df5848439cf5a80a42c11e89f2bef6";
@@ -1,6 +1,6 @@
1
1
  // Generated by scripts/generate.mjs; do not edit.
2
2
  export const sdkVersion = "205.0.0-device-vault-agents";
3
- export const openapiSha256 = "e742feade28ca115077a4275e4a6260646278e9adcf0839987b8566a2221c8f5";
3
+ export const openapiSha256 = "93141e0bf50949fc37ebceef7c64555eb50a70f69c71c89c8fdc12780e0dd168";
4
4
  export const realtimeSha256 = "1cac740e108e84ad1979f6ccc71089efd840404cb72d1612a86edc02769637e3";
5
5
  export const jsonSchemaSha256 = {
6
6
  "account.schema.json": "5106e2d53abef190990eff113f6692dd27df5848439cf5a80a42c11e89f2bef6",
package/dist/totalis.d.ts CHANGED
@@ -185,6 +185,15 @@ export type TotalisOptions = Omit<ApiClientOptions, "accessToken"> & {
185
185
  readonly accessToken: AccessToken;
186
186
  /** The account's signer (GET /v1/me `wallet`); required for flows that sign. Never stored. */
187
187
  readonly signer?: SignerInput;
188
+ /**
189
+ * A key the account granted as a vault agent (`grantVaultAgent`). When set,
190
+ * it signs the account's TakerAccept, SellBack, CashoutTransfer and Exits to
191
+ * its wallet or HyperCore instead of `signer`, so those need no wallet
192
+ * prompt. `signer` still names the account and signs everything else: USDC
193
+ * authorizations, HyperCore actions, grants and run-off claims. The venue
194
+ * refuses an agent whose grant ends before the signed expiry.
195
+ */
196
+ readonly agent?: SignerInput;
188
197
  /** Stream options; `subscription` adds account subscription fields such as `open_rfqs`. */
189
198
  readonly stream?: Omit<StreamOptions, "subscription" | "accessToken" | "environment" | "baseUrl" | "streamBaseUrl"> & {
190
199
  readonly subscription?: Omit<Extract<StreamSubscription, {
@@ -638,6 +647,27 @@ export declare function createTotalis(options: TotalisOptions): {
638
647
  readonly signal?: AbortSignal;
639
648
  }) => Promise<CoreExit>;
640
649
  directDepositCalls: (amount: Amount) => Promise<readonly WalletCall[]>;
650
+ grantVaultAgent: (request: {
651
+ readonly agent: string;
652
+ readonly validUntil: number | string;
653
+ readonly nonce?: number | string;
654
+ readonly idempotencyKey?: string;
655
+ }) => Promise<{
656
+ readonly operation_id: import("./generated/openapi.js").components["schemas"]["Hash32"];
657
+ }>;
658
+ revokeVaultAgent: (request: {
659
+ readonly agent: string;
660
+ readonly nonce?: number | string;
661
+ readonly idempotencyKey?: string;
662
+ }) => Promise<{
663
+ readonly operation_id: import("./generated/openapi.js").components["schemas"]["Hash32"];
664
+ }>;
665
+ vaultAgents: (limit?: number) => AsyncGenerator<{
666
+ readonly agent: import("./generated/openapi.js").components["schemas"]["Address"];
667
+ readonly valid_until: import("./generated/openapi.js").components["schemas"]["UInt64String"];
668
+ readonly nonce: import("./generated/openapi.js").components["schemas"]["UInt64String"];
669
+ readonly copy_agent: boolean;
670
+ }, void, any>;
641
671
  /** Render an RFQ's live quotes (entry or cash-out) from the stream; `[]` while the stream is down. */
642
672
  watchQuotes: (rfqId: string, listener: (quotes: readonly Quote[], serverNowMs: number) => void) => () => void;
643
673
  /** Issue a personal `ttk_` key; the signer must be the account wallet (embedded, or a wallet-only account's own). */
package/dist/totalis.js CHANGED
@@ -7,7 +7,8 @@ import { spendableCash } from "./money.js";
7
7
  import { ApiError, backoffDelay, envelope, NetworkError, randomBytes32, resendable, retryingFetch, sendOnce, singleFetch, sleep, unwrap, uuidv7 } from "./request.js";
8
8
  import { toTotalisSigner } from "./signer.js";
9
9
  import { TotalisStream } from "./stream.js";
10
- import { address, cashoutTransferTypedData, exitTypedData, receiveWithAuthorizationTypedData, sellBackTypedData, takerAcceptTypedData, payoutNonce, transferWithAuthorizationTypedData, } from "./typed-data.js";
10
+ import { items } from "./pagination.js";
11
+ import { address, agentGrantTypedData, cashoutTransferTypedData, exitTypedData, receiveWithAuthorizationTypedData, sellBackTypedData, takerAcceptTypedData, payoutNonce, transferWithAuthorizationTypedData, } from "./typed-data.js";
11
12
  /**
12
13
  * What an entry's bridge may have moved to its wallet, by its state: nothing
13
14
  * before it is sent, once discarded or refused. Only `LANDED` proves it
@@ -314,7 +315,7 @@ function streamQuotes(stream, read) {
314
315
  * stream; HTTP reads recover after a reconnect or answer a doorbell.
315
316
  */
316
317
  export function createTotalis(options) {
317
- const { environment, signer: signerInput, stream: streamOptions, streamBaseUrl, accessToken, ...clientOptions } = options;
318
+ const { environment, signer: signerInput, agent: agentInput, stream: streamOptions, streamBaseUrl, accessToken, ...clientOptions } = options;
318
319
  const client = audienceClient(environment, { ...clientOptions, accessToken });
319
320
  const keys = createApiKeyClient(environment, { ...clientOptions, accessToken });
320
321
  const stream = new TotalisStream({
@@ -337,6 +338,22 @@ export function createTotalis(options) {
337
338
  };
338
339
  const signer = signerInput === undefined ? undefined : toTotalisSigner(signerInput);
339
340
  const requireSigner = () => signer ?? Promise.reject(new Error("this flow needs a signer"));
341
+ const agent = agentInput === undefined ? undefined : toTotalisSigner(agentInput);
342
+ // The vault reverts a grant whose nonce does not exceed the agent's last, so
343
+ // two grants for one agent in one second (a grant, then its revoke) must not
344
+ // share one, and a default after an explicit nonce must exceed it too. The
345
+ // vault counts per agent, so the mark is per agent.
346
+ const lastGrantNonce = new Map();
347
+ const grantNonce = (agentAddress, explicit) => {
348
+ const last = lastGrantNonce.get(agentAddress) ?? 0n;
349
+ const clock = BigInt(now());
350
+ const nonce = explicit !== undefined ? BigInt(explicit) : clock > last ? clock : last + 1n;
351
+ if (nonce < 1n || nonce > 2n ** 64n - 1n)
352
+ throw new Error(`grant nonce ${nonce} is outside 1..2^64-1`);
353
+ if (nonce > last)
354
+ lastGrantNonce.set(agentAddress, nonce);
355
+ return String(nonce);
356
+ };
340
357
  let deploymentRead;
341
358
  const deployment = () => (deploymentRead ??= client.GET("/v1/deployment").then(unwrap).catch((error) => {
342
359
  deploymentRead = undefined;
@@ -585,7 +602,7 @@ export function createTotalis(options) {
585
602
  const signer = await requireSigner();
586
603
  const account = await signer.address();
587
604
  const chain = await deployment();
588
- return { signer, account, chain, scope: { account, chainId: chain.chain_id, vault: address(chain.vault_address.toLowerCase()) } };
605
+ return { signer, vaultSigner: agent ?? signer, account, chain, scope: { account, chainId: chain.chain_id, vault: address(chain.vault_address.toLowerCase()) } };
589
606
  }
590
607
  /** A stored record signs nothing more, so without a signer the session's wallet (GET /v1/me) stands for the account. */
591
608
  async function checkScope(signed) {
@@ -626,14 +643,14 @@ export function createTotalis(options) {
626
643
  }
627
644
  async function signEntryWith(request, funding) {
628
645
  const { quote, cash } = request;
629
- const { signer, account, chain, scope } = await recordScope();
646
+ const { signer, vaultSigner, account, chain, scope } = await recordScope();
630
647
  if (quote.entry.taker.toLowerCase() !== account)
631
648
  throw new Error("the quote is for another taker");
632
649
  const stake = BigInt(quote.entry.stake);
633
650
  const core = request.core === undefined || request.core === null ? request.core : BigInt(request.core);
634
651
  // Known cash is planned before any prompt, so a plan that fails asks the wallet for nothing.
635
652
  let plan = funding === "plan" && cash !== undefined ? planEntry(stake, core, cash) : undefined;
636
- const signing = signer.signTypedData(takerAcceptTypedData(chain, quote.entry));
653
+ const signing = vaultSigner.signTypedData(takerAcceptTypedData(chain, quote.entry));
637
654
  if (funding === "plan" && cash === undefined) {
638
655
  // The quote signature names no amount, so the wallet signs it while the balances are read; a refusal stops the read and is the call's error.
639
656
  const reading = new AbortController();
@@ -682,12 +699,12 @@ export function createTotalis(options) {
682
699
  /** Sign a cash-out quote as the position's owner without sending it: the same SellBack or CashoutTransfer digest the maker signs. */
683
700
  async function signCashout(request) {
684
701
  const { quote } = request;
685
- const { signer, account, chain, scope } = await recordScope();
702
+ const { vaultSigner, account, chain, scope } = await recordScope();
686
703
  const seller = quote.kind === "SELL_BACK" ? quote.sell_back.owner : quote.transfer.seller;
687
704
  if (seller.toLowerCase() !== account)
688
705
  throw new Error("the quote sells another owner's position");
689
706
  const typedData = quote.kind === "SELL_BACK" ? sellBackTypedData(chain, quote.sell_back) : cashoutTransferTypedData(chain, quote.transfer);
690
- const taker_signature = await signer.signTypedData(typedData);
707
+ const taker_signature = await vaultSigner.signTypedData(typedData);
691
708
  return {
692
709
  kind: "CASHOUT", acceptanceId: request.acceptanceId ?? uuidv7(), rfqId: quote.rfq_id, quoteId: quote.quote_id, ...scope,
693
710
  positionId: termsOf(quote).position_id, command: { taker_signature },
@@ -913,7 +930,7 @@ export function createTotalis(options) {
913
930
  let vault_withdrawal;
914
931
  if (shortfall > 0n) {
915
932
  const exit = { amount: shortfall.toString(), expiry: String(issued + 600), salt: BigInt(randomBytes32()).toString() };
916
- vault_withdrawal = { ...exit, signature: await signer.signTypedData(exitTypedData(chain, { account, to: account, ...exit })) };
933
+ vault_withdrawal = { ...exit, signature: await (agent ?? signer).signTypedData(exitTypedData(chain, { account, to: account, ...exit })) };
917
934
  deadline = issued + 600;
918
935
  }
919
936
  // The venue requires the payout to outlive the vault leg by 300 s; a minute more absorbs clock skew.
@@ -1065,7 +1082,7 @@ export function createTotalis(options) {
1065
1082
  const to = address(route.router_address.toLowerCase());
1066
1083
  // A minute inside the venue's maximum life absorbs clock skew.
1067
1084
  const exit = { amount: BigInt(request.amount).toString(), expiry: String(now() + Math.min(route.maximum_life_seconds - 60, 600)), salt: BigInt(randomBytes32()).toString() };
1068
- const signature = await signer.signTypedData(exitTypedData(chain, { account, to, ...exit }));
1085
+ const signature = await (agent ?? signer).signTypedData(exitTypedData(chain, { account, to, ...exit }));
1069
1086
  const body = envelope({ account, to, ...exit, signature, purpose: request.purpose ?? "ORDER", ...(request.activationFeeAccepted ? { activation_fee_accepted: true } : {}) });
1070
1087
  return { exitId, account, body };
1071
1088
  }
@@ -1190,6 +1207,36 @@ export function createTotalis(options) {
1190
1207
  throw new Error(`core exit ${exitId} ended ${exit.status}, not credited on HyperCore`);
1191
1208
  return exit;
1192
1209
  }
1210
+ /**
1211
+ * Grant `agent`, a key one of the account's devices holds, as a vault agent
1212
+ * until `validUntil` (unix seconds, at most 180 days away): the account's
1213
+ * `signer` signs the AgentGrant and Totalis relays it to the vault, paying
1214
+ * the gas. Pass that key as `agent` to sign accepts, cash-outs and Exits
1215
+ * back to the account without a wallet prompt. The nonce defaults to the
1216
+ * venue's clock in seconds, and rises past this client's last grant, so a
1217
+ * later grant or revoke supersedes an earlier one. The
1218
+ * 202 is admission: follow `operation_id` with `waitForOperation`; the agent
1219
+ * signs once it lands. A resend of the same `idempotencyKey` returns the
1220
+ * same operation.
1221
+ */
1222
+ async function grantVaultAgent(request) {
1223
+ const signer = await requireSigner();
1224
+ const account = address((await signer.address()).toLowerCase());
1225
+ const agentAddress = address(request.agent.toLowerCase());
1226
+ const grant = { account, agent: agentAddress, valid_until: String(request.validUntil), nonce: grantNonce(agentAddress, request.nonce) };
1227
+ const signature = await signer.signTypedData(agentGrantTypedData(await deployment(), grant));
1228
+ const key = request.idempotencyKey ?? uuidv7();
1229
+ return sendOnce(() => client.POST("/v1/me/vault-agents", { params: { header: idempotency(key) },
1230
+ body: { agent: grant.agent, valid_until: grant.valid_until, nonce: grant.nonce, signature } }));
1231
+ }
1232
+ /** The agent keeps signing until the revoke's operation lands; `waitForOperation` before treating it as gone. */
1233
+ function revokeVaultAgent(request) {
1234
+ return grantVaultAgent({ ...request, validUntil: 0 });
1235
+ }
1236
+ /** The account's live vault agents, every page; `copy_agent` marks a Combo copy agent's key. */
1237
+ function vaultAgents(limit = 200) {
1238
+ return items((query) => client.GET("/v1/me/vault-agents", { params: { query: { ...query, limit } } }).then(unwrap), (page) => page.agents);
1239
+ }
1193
1240
  /**
1194
1241
  * The transactions a wallet sends itself to deposit USDC from HyperEVM into
1195
1242
  * its vault balance, paying its own gas: approve the vault for `amount`, then
@@ -1239,6 +1286,9 @@ export function createTotalis(options) {
1239
1286
  sendReturnToCore,
1240
1287
  returnToCore,
1241
1288
  directDepositCalls,
1289
+ grantVaultAgent,
1290
+ revokeVaultAgent,
1291
+ vaultAgents,
1242
1292
  /** Render an RFQ's live quotes (entry or cash-out) from the stream; `[]` while the stream is down. */
1243
1293
  watchQuotes,
1244
1294
  /** Issue a personal `ttk_` key; the signer must be the account wallet (embedded, or a wallet-only account's own). */
@@ -127,15 +127,34 @@ export declare const totalisTypes: {
127
127
  readonly name: "salt";
128
128
  readonly type: "uint256";
129
129
  }];
130
+ readonly AgentGrant: readonly [{
131
+ readonly name: "account";
132
+ readonly type: "address";
133
+ }, {
134
+ readonly name: "agent";
135
+ readonly type: "address";
136
+ }, {
137
+ readonly name: "validUntil";
138
+ readonly type: "uint64";
139
+ }, {
140
+ readonly name: "nonce";
141
+ readonly type: "uint64";
142
+ }];
130
143
  };
131
144
  export type TotalisPrimaryType = Exclude<keyof typeof totalisTypes, "Leg">;
132
- /** Who signs each struct: the maker's signer, the taker's signer, or either side of a cash-out. */
145
+ /**
146
+ * Who signs each struct: the maker's signer, the taker's signer, or either
147
+ * side of a cash-out. A vault agent the account granted may sign the taker's
148
+ * TakerAccept, SellBack and CashoutTransfer, and an Exit back to the account;
149
+ * only the account itself signs an AgentGrant.
150
+ */
133
151
  export declare const signedBy: {
134
152
  readonly Quote: "maker";
135
153
  readonly TakerAccept: "taker";
136
154
  readonly SellBack: "maker_and_taker";
137
155
  readonly CashoutTransfer: "maker_and_taker";
138
156
  readonly Exit: "taker";
157
+ readonly AgentGrant: "taker";
139
158
  };
140
159
  /** The vault deployment a signature binds to: GET /v1/deployment. */
141
160
  export type TotalisDeployment = Pick<Schemas["Deployment"], "chain_id" | "vault_address">;
@@ -201,6 +220,17 @@ export declare function exitTypedData(deployment: TotalisDeployment, exit: {
201
220
  expiry: string;
202
221
  salt: string;
203
222
  }): TotalisTypedData<"Exit">;
223
+ /**
224
+ * The account's grant to a vault agent: a key it holds signs for the account
225
+ * until `valid_until` (unix seconds, at most 180 days away; 0 revokes). The
226
+ * nonce must exceed the agent's last grant. Relay it with POST /v1/me/vault-agents.
227
+ */
228
+ export declare function agentGrantTypedData(deployment: TotalisDeployment, grant: {
229
+ account: Hex;
230
+ agent: Hex;
231
+ valid_until: string;
232
+ nonce: string;
233
+ }): TotalisTypedData<"AgentGrant">;
204
234
  export declare const domainTypes: {
205
235
  readonly EIP712Domain: readonly [{
206
236
  readonly name: "name";
@@ -47,14 +47,26 @@ export const totalisTypes = {
47
47
  { name: "expiry", type: "uint64" },
48
48
  { name: "salt", type: "uint256" },
49
49
  ],
50
+ AgentGrant: [
51
+ { name: "account", type: "address" },
52
+ { name: "agent", type: "address" },
53
+ { name: "validUntil", type: "uint64" },
54
+ { name: "nonce", type: "uint64" },
55
+ ],
50
56
  };
51
- /** Who signs each struct: the maker's signer, the taker's signer, or either side of a cash-out. */
57
+ /**
58
+ * Who signs each struct: the maker's signer, the taker's signer, or either
59
+ * side of a cash-out. A vault agent the account granted may sign the taker's
60
+ * TakerAccept, SellBack and CashoutTransfer, and an Exit back to the account;
61
+ * only the account itself signs an AgentGrant.
62
+ */
52
63
  export const signedBy = {
53
64
  Quote: "maker",
54
65
  TakerAccept: "taker",
55
66
  SellBack: "maker_and_taker",
56
67
  CashoutTransfer: "maker_and_taker",
57
68
  Exit: "taker",
69
+ AgentGrant: "taker",
58
70
  };
59
71
  /** A 20-byte hex address, or a TypeError. */
60
72
  export function address(value) {
@@ -122,6 +134,17 @@ export function exitTypedData(deployment, exit) {
122
134
  message: { account: exit.account, to: exit.to, amount: BigInt(exit.amount), expiry: BigInt(exit.expiry), salt: BigInt(exit.salt) },
123
135
  };
124
136
  }
137
+ /**
138
+ * The account's grant to a vault agent: a key it holds signs for the account
139
+ * until `valid_until` (unix seconds, at most 180 days away; 0 revokes). The
140
+ * nonce must exceed the agent's last grant. Relay it with POST /v1/me/vault-agents.
141
+ */
142
+ export function agentGrantTypedData(deployment, grant) {
143
+ return {
144
+ domain: domain(deployment), types: { AgentGrant: totalisTypes.AgentGrant }, primaryType: "AgentGrant",
145
+ message: { account: address(grant.account), agent: address(grant.agent), validUntil: BigInt(grant.valid_until), nonce: BigInt(grant.nonce) },
146
+ };
147
+ }
125
148
  // ---------------------------------------------------------------------------
126
149
  // EIP-712 hashing, so makers can compute quote_digest and every signer can be
127
150
  // checked against the contract's vectors without a wallet library.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totalis/hip4-client",
3
- "version": "0.75.0",
3
+ "version": "0.76.0",
4
4
  "description": "Totalis HIP-4 SDK: typed HTTP clients, signer-agnostic typed data, a retrying request layer, pagination, a realtime stream and taker/maker flows",
5
5
  "type": "module",
6
6
  "files": [