opacity-issuer-core 0.2.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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/dist/chunk-2STMWRIV.js +164 -0
  4. package/dist/chunk-2STMWRIV.js.map +1 -0
  5. package/dist/chunk-3L7NNVUV.cjs +184 -0
  6. package/dist/chunk-3L7NNVUV.cjs.map +1 -0
  7. package/dist/chunk-54Q3MU62.js +29 -0
  8. package/dist/chunk-54Q3MU62.js.map +1 -0
  9. package/dist/chunk-7ODYBEZH.js +889 -0
  10. package/dist/chunk-7ODYBEZH.js.map +1 -0
  11. package/dist/chunk-GMPQFX25.cjs +923 -0
  12. package/dist/chunk-GMPQFX25.cjs.map +1 -0
  13. package/dist/chunk-HJQZWNZJ.cjs +33 -0
  14. package/dist/chunk-HJQZWNZJ.cjs.map +1 -0
  15. package/dist/documents/index.cjs +170 -0
  16. package/dist/documents/index.cjs.map +1 -0
  17. package/dist/documents/index.d.cts +1370 -0
  18. package/dist/documents/index.d.ts +1370 -0
  19. package/dist/documents/index.js +5 -0
  20. package/dist/documents/index.js.map +1 -0
  21. package/dist/documents/validate.cjs +53 -0
  22. package/dist/documents/validate.cjs.map +1 -0
  23. package/dist/documents/validate.d.cts +70 -0
  24. package/dist/documents/validate.d.ts +70 -0
  25. package/dist/documents/validate.js +4 -0
  26. package/dist/documents/validate.js.map +1 -0
  27. package/dist/index.cjs +8917 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +11088 -0
  30. package/dist/index.d.ts +11088 -0
  31. package/dist/index.js +8647 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/subgraph.cjs +891 -0
  34. package/dist/subgraph.cjs.map +1 -0
  35. package/dist/subgraph.d.cts +456 -0
  36. package/dist/subgraph.d.ts +456 -0
  37. package/dist/subgraph.js +882 -0
  38. package/dist/subgraph.js.map +1 -0
  39. package/dist/transport-0_X4HTI6.d.ts +39 -0
  40. package/dist/transport-uaC5Yl3p.d.cts +39 -0
  41. package/dist/types-B4Yi7WA4.d.cts +108 -0
  42. package/dist/types-B4Yi7WA4.d.ts +108 -0
  43. package/package.json +75 -0
@@ -0,0 +1,1370 @@
1
+ import { Hex, Address } from 'viem';
2
+ import { C as ClockOptions, b as BuildDocumentAcceptanceTypedDataParams, B as BuildAttestDocumentTransactionParams, T as TransactionRequest } from '../types-B4Yi7WA4.cjs';
3
+ export { A as AttestationErrorCode, a as AttestationSession, D as DocumentAttestorContext } from '../types-B4Yi7WA4.cjs';
4
+
5
+ /**
6
+ * Minimal ABI for `DocumentAttestor`, hand-written rather than copied from a Foundry artifact.
7
+ *
8
+ * The contracts repo's `release-abis.yml` only syncs `AllowlistRegistry.json` downstream, so there
9
+ * is no artifact pipeline to consume here; a hand-written `as const` array also keeps viem's type
10
+ * inference sharp and the published bundle small.
11
+ *
12
+ * It covers the write path this SDK builds (`attestDocument`), the reads worth verifying a
13
+ * deployment against, and the errors reachable from `attestDocument` — including the two from
14
+ * `AllowlistRegistry` and `EnforcedPause`, which bubble up through `enableTraitAsWriter` and are
15
+ * the likeliest failures in practice (writer role never granted, trait not writer-enableable,
16
+ * registry paused). Pass this ABI to viem's revert decoding to get a named error instead of a raw
17
+ * selector.
18
+ *
19
+ * Owner-only functions (`setServerSigner`, the `Ownable2Step` surface) are deliberately absent —
20
+ * they are outside this module's scope.
21
+ */
22
+ declare const documentAttestorAbi: readonly [{
23
+ readonly type: "function";
24
+ readonly name: "attestDocument";
25
+ readonly stateMutability: "nonpayable";
26
+ readonly inputs: readonly [{
27
+ readonly name: "wallet";
28
+ readonly type: "address";
29
+ }, {
30
+ readonly name: "traitId";
31
+ readonly type: "bytes32";
32
+ }, {
33
+ readonly name: "docHash";
34
+ readonly type: "bytes32";
35
+ }, {
36
+ readonly name: "nonce";
37
+ readonly type: "bytes32";
38
+ }, {
39
+ readonly name: "issuedAt";
40
+ readonly type: "uint256";
41
+ }, {
42
+ readonly name: "deadline";
43
+ readonly type: "uint256";
44
+ }, {
45
+ readonly name: "serverSignature";
46
+ readonly type: "bytes";
47
+ }, {
48
+ readonly name: "userSignature";
49
+ readonly type: "bytes";
50
+ }];
51
+ readonly outputs: readonly [];
52
+ }, {
53
+ readonly type: "function";
54
+ readonly name: "serverSigner";
55
+ readonly stateMutability: "view";
56
+ readonly inputs: readonly [];
57
+ readonly outputs: readonly [{
58
+ readonly type: "address";
59
+ }];
60
+ }, {
61
+ readonly type: "function";
62
+ readonly name: "usedNonces";
63
+ readonly stateMutability: "view";
64
+ readonly inputs: readonly [{
65
+ readonly name: "nonce";
66
+ readonly type: "bytes32";
67
+ }];
68
+ readonly outputs: readonly [{
69
+ readonly type: "bool";
70
+ }];
71
+ }, {
72
+ readonly type: "function";
73
+ readonly name: "retiredSigners";
74
+ readonly stateMutability: "view";
75
+ readonly inputs: readonly [{
76
+ readonly name: "signer";
77
+ readonly type: "address";
78
+ }];
79
+ readonly outputs: readonly [{
80
+ readonly type: "bool";
81
+ }];
82
+ }, {
83
+ readonly type: "function";
84
+ readonly name: "allowlistRegistry";
85
+ readonly stateMutability: "view";
86
+ readonly inputs: readonly [];
87
+ readonly outputs: readonly [{
88
+ readonly type: "address";
89
+ }];
90
+ }, {
91
+ readonly type: "function";
92
+ readonly name: "MAX_ATTESTATION_LIFETIME";
93
+ readonly stateMutability: "view";
94
+ readonly inputs: readonly [];
95
+ readonly outputs: readonly [{
96
+ readonly type: "uint256";
97
+ }];
98
+ }, {
99
+ readonly type: "function";
100
+ readonly name: "DOCUMENT_ACCEPTANCE_TYPEHASH";
101
+ readonly stateMutability: "view";
102
+ readonly inputs: readonly [];
103
+ readonly outputs: readonly [{
104
+ readonly type: "bytes32";
105
+ }];
106
+ }, {
107
+ readonly type: "function";
108
+ readonly name: "ATTESTATION_AUTHORIZATION_TYPEHASH";
109
+ readonly stateMutability: "view";
110
+ readonly inputs: readonly [];
111
+ readonly outputs: readonly [{
112
+ readonly type: "bytes32";
113
+ }];
114
+ }, {
115
+ readonly type: "function";
116
+ readonly name: "eip712Domain";
117
+ readonly stateMutability: "view";
118
+ readonly inputs: readonly [];
119
+ readonly outputs: readonly [{
120
+ readonly name: "fields";
121
+ readonly type: "bytes1";
122
+ }, {
123
+ readonly name: "name";
124
+ readonly type: "string";
125
+ }, {
126
+ readonly name: "version";
127
+ readonly type: "string";
128
+ }, {
129
+ readonly name: "chainId";
130
+ readonly type: "uint256";
131
+ }, {
132
+ readonly name: "verifyingContract";
133
+ readonly type: "address";
134
+ }, {
135
+ readonly name: "salt";
136
+ readonly type: "bytes32";
137
+ }, {
138
+ readonly name: "extensions";
139
+ readonly type: "uint256[]";
140
+ }];
141
+ }, {
142
+ readonly type: "event";
143
+ readonly name: "DocumentAttested";
144
+ readonly inputs: readonly [{
145
+ readonly name: "userId";
146
+ readonly type: "bytes32";
147
+ readonly indexed: true;
148
+ }, {
149
+ readonly name: "wallet";
150
+ readonly type: "address";
151
+ readonly indexed: true;
152
+ }, {
153
+ readonly name: "traitId";
154
+ readonly type: "bytes32";
155
+ readonly indexed: true;
156
+ }, {
157
+ readonly name: "docHash";
158
+ readonly type: "bytes32";
159
+ readonly indexed: false;
160
+ }, {
161
+ readonly name: "nonce";
162
+ readonly type: "bytes32";
163
+ readonly indexed: false;
164
+ }];
165
+ readonly anonymous: false;
166
+ }, {
167
+ readonly type: "error";
168
+ readonly name: "ZeroAddress";
169
+ readonly inputs: readonly [];
170
+ }, {
171
+ readonly type: "error";
172
+ readonly name: "InvalidTraitId";
173
+ readonly inputs: readonly [];
174
+ }, {
175
+ readonly type: "error";
176
+ readonly name: "InvalidDocHash";
177
+ readonly inputs: readonly [];
178
+ }, {
179
+ readonly type: "error";
180
+ readonly name: "NonceAlreadyUsed";
181
+ readonly inputs: readonly [];
182
+ }, {
183
+ readonly type: "error";
184
+ readonly name: "AttestationExpired";
185
+ readonly inputs: readonly [{
186
+ readonly name: "deadline";
187
+ readonly type: "uint256";
188
+ }];
189
+ }, {
190
+ readonly type: "error";
191
+ readonly name: "AttestationDeadlineTooFar";
192
+ readonly inputs: readonly [{
193
+ readonly name: "deadline";
194
+ readonly type: "uint256";
195
+ }, {
196
+ readonly name: "maxAllowed";
197
+ readonly type: "uint256";
198
+ }];
199
+ }, {
200
+ readonly type: "error";
201
+ readonly name: "AttestationIssuedInFuture";
202
+ readonly inputs: readonly [{
203
+ readonly name: "issuedAt";
204
+ readonly type: "uint256";
205
+ }];
206
+ }, {
207
+ readonly type: "error";
208
+ readonly name: "WalletNotOnboarded";
209
+ readonly inputs: readonly [{
210
+ readonly name: "wallet";
211
+ readonly type: "address";
212
+ }];
213
+ }, {
214
+ readonly type: "error";
215
+ readonly name: "InvalidServerSignature";
216
+ readonly inputs: readonly [];
217
+ }, {
218
+ readonly type: "error";
219
+ readonly name: "SignerRetired";
220
+ readonly inputs: readonly [{
221
+ readonly name: "signer";
222
+ readonly type: "address";
223
+ }];
224
+ }, {
225
+ readonly type: "error";
226
+ readonly name: "InvalidUserSignature";
227
+ readonly inputs: readonly [];
228
+ }, {
229
+ readonly type: "error";
230
+ readonly name: "ECDSAInvalidSignature";
231
+ readonly inputs: readonly [];
232
+ }, {
233
+ readonly type: "error";
234
+ readonly name: "ECDSAInvalidSignatureLength";
235
+ readonly inputs: readonly [{
236
+ readonly name: "length";
237
+ readonly type: "uint256";
238
+ }];
239
+ }, {
240
+ readonly type: "error";
241
+ readonly name: "ECDSAInvalidSignatureS";
242
+ readonly inputs: readonly [{
243
+ readonly name: "s";
244
+ readonly type: "bytes32";
245
+ }];
246
+ }, {
247
+ readonly type: "error";
248
+ readonly name: "NotTraitWriter";
249
+ readonly inputs: readonly [];
250
+ }, {
251
+ readonly type: "error";
252
+ readonly name: "TraitNotWriterEnableable";
253
+ readonly inputs: readonly [];
254
+ }, {
255
+ readonly type: "error";
256
+ readonly name: "EnforcedPause";
257
+ readonly inputs: readonly [];
258
+ }];
259
+
260
+ /**
261
+ * EIP-712 domain `name` and `version`, from `EIP712("DocumentAttestor", "1")` in
262
+ * `DocumentAttestor.sol`. The domain has no `salt` — OpenZeppelin's `EIP712` uses
263
+ * `EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)`.
264
+ */
265
+ declare const DOCUMENT_ATTESTOR_DOMAIN_NAME = "DocumentAttestor";
266
+ declare const DOCUMENT_ATTESTOR_DOMAIN_VERSION = "1";
267
+ /**
268
+ * The payload the investor's wallet signs.
269
+ *
270
+ * It deliberately binds neither `wallet` nor `traitId`: the trait is an internal mapping and the
271
+ * prompt should show only what the human is agreeing to, while the binding to a wallet comes from
272
+ * ECDSA recovery (or, for ERC-1271, from the server's own signature). Cross-deployment and
273
+ * cross-chain replay are stopped by the domain's `chainId` + `verifyingContract`.
274
+ *
275
+ * Field order is consensus-critical — it is part of the type hash.
276
+ */
277
+ declare const DOCUMENT_ACCEPTANCE_TYPES: {
278
+ readonly DocumentAcceptance: readonly [{
279
+ readonly name: "nonce";
280
+ readonly type: "bytes32";
281
+ }, {
282
+ readonly name: "docHash";
283
+ readonly type: "bytes32";
284
+ }, {
285
+ readonly name: "deadline";
286
+ readonly type: "uint256";
287
+ }];
288
+ };
289
+ declare const DOCUMENT_ACCEPTANCE_PRIMARY_TYPE = "DocumentAcceptance";
290
+ /**
291
+ * The EIP-712 domain type, needed only by `toEip712JsonPayload`: raw `eth_signTypedData_v4`
292
+ * requires `EIP712Domain` to be present in `types`, whereas viem derives it and ethers v6 throws
293
+ * if you supply it.
294
+ */
295
+ declare const EIP712_DOMAIN_TYPE: readonly [{
296
+ readonly name: "name";
297
+ readonly type: "string";
298
+ }, {
299
+ readonly name: "version";
300
+ readonly type: "string";
301
+ }, {
302
+ readonly name: "chainId";
303
+ readonly type: "uint256";
304
+ }, {
305
+ readonly name: "verifyingContract";
306
+ readonly type: "address";
307
+ }];
308
+ /**
309
+ * `keccak256("DocumentAcceptance(bytes32 nonce,bytes32 docHash,uint256 deadline)")`.
310
+ *
311
+ * Not used to build the payload (viem derives it from {@link DOCUMENT_ACCEPTANCE_TYPES}); exported
312
+ * so a consumer can assert it against the contract's `DOCUMENT_ACCEPTANCE_TYPEHASH()` getter.
313
+ */
314
+ declare const DOCUMENT_ACCEPTANCE_TYPEHASH: Hex;
315
+ /**
316
+ * Mirrors `DocumentAttestor.MAX_ATTESTATION_LIFETIME` (30 days).
317
+ *
318
+ * Duplicated off-chain so `deadline > issuedAt + MAX_ATTESTATION_LIFETIME` can be caught before the
319
+ * investor is prompted, rather than at redemption. It is a `constant` on a non-upgradeable contract,
320
+ * so it cannot drift for a given deployment — but a successor deployment could choose a different
321
+ * value, which is why `MAX_ATTESTATION_LIFETIME` is kept in `documentAttestorAbi` for
322
+ * consumers that want to read it back.
323
+ */
324
+ declare const MAX_ATTESTATION_LIFETIME_SECONDS = 2592000n;
325
+ /** Length in bytes of the ECDSA signature `ECDSA.recover` accepts — it rejects every other length. */
326
+ declare const ECDSA_SIGNATURE_BYTES = 65;
327
+
328
+ /**
329
+ * Derives a trait id from its canonical name: `keccak256(utf8(name))`.
330
+ *
331
+ * Matches the convention the contracts use verbatim — `keccak256("doc.offering7.subscription.v1")`
332
+ * in Solidity is `documentTraitId('doc.offering7.subscription.v1')` here.
333
+ *
334
+ * The document VERSION is part of the name, deliberately: amendments roll the id forward, so a
335
+ * policy that requires `…v2` makes every `…v1` attestation inert without touching a contract.
336
+ *
337
+ * The resulting id must be writer-enableable on the registry
338
+ * (`AllowlistRegistry.setWriterEnableableTrait`) or `attestDocument` reverts
339
+ * `TraitNotWriterEnableable`.
340
+ */
341
+ declare function documentTraitId(name: string): Hex;
342
+ /**
343
+ * Hashes a document to the `docHash` an attestation binds to.
344
+ *
345
+ * Must be the EXECUTED document — the post-signature artifact — not the blank template. Pass the
346
+ * raw bytes (e.g. a `Uint8Array` of the PDF); a `string` is hashed as UTF-8.
347
+ */
348
+ declare function hashDocument(content: Uint8Array | string): Hex;
349
+
350
+ /**
351
+ * Asserts that the bytes the app rendered still hash to the `docHash` the investor is about to
352
+ * sign.
353
+ *
354
+ * **This is the single most security-relevant check the client half performs**, and the only thing
355
+ * tying a signature to real paperwork. The 2-of-2 design defends against a compromised server
356
+ * acting alone and against an investor acting alone; it does nothing about a client that signs
357
+ * whatever `docHash` arrives over the wire, because the attestor never sees the document at all —
358
+ * only its hash.
359
+ *
360
+ * Since the client is what produced and submitted that hash in the first place, a mismatch never
361
+ * means "the server sent the wrong document" — the server has no document to send. It means one of:
362
+ *
363
+ * - the session echoed back a different `docHash` than the one posted to `POST /attestations`;
364
+ * - the bytes changed between being hashed and being rendered;
365
+ * - the wrong artifact is in hand — a blank template, or a re-encoded copy of the executed one.
366
+ *
367
+ * Call it before `buildDocumentAcceptanceTypedData` and refuse to raise the wallet prompt if it
368
+ * throws. Pass the EXECUTED artifact — the same post-signature bytes that were hashed for
369
+ * `POST /attestations` — not the blank template, and not a re-rendered or re-encoded copy of it.
370
+ * Hashing is byte-exact: a PDF that has been through a viewer's save path will not match even
371
+ * though it looks identical.
372
+ *
373
+ * The comparison is case-insensitive. `hashDocument` returns lower-case hex, but a `docHash` that
374
+ * arrived inside a JSON session payload may be upper- or mixed-case, and rejecting a valid document
375
+ * over hex casing would be a silent, extremely confusing failure. Constant-time comparison is not
376
+ * warranted: both values are public, and anyone in a position to substitute the hash already knows
377
+ * the one they substituted.
378
+ *
379
+ * @throws {OpacityAttestationError} `DocHashMismatch` when the content hashes to something other
380
+ * than `expectedDocHash`; `InvalidDocHash` / `MalformedInput` when `expectedDocHash` is zero or
381
+ * not 32 bytes of hex, or when `content` is empty or of the wrong type.
382
+ */
383
+ declare function assertDocumentMatchesHash(content: Uint8Array | string, expectedDocHash: Hex): void;
384
+ /**
385
+ * The parts of a `POST /attestations` response this module reads.
386
+ *
387
+ * Declared structurally, so the full response type — or a raw `JSON.parse` result — satisfies it
388
+ * without conversion, and so this module does not have to move every time the rest of the response
389
+ * grows a field.
390
+ */
391
+ interface AttestationSessionResponseLike {
392
+ /**
393
+ * Top-level echo of the hash the client submitted. Optional here only for tolerance; the locked
394
+ * schema always sends it, and it is cross-checked against the signed copy when present.
395
+ */
396
+ docHash?: Hex | undefined;
397
+ /** Session expiry, unix seconds. A NUMBER on the locked wire; a bigint or decimal string is fine. */
398
+ deadline: number | bigint | string;
399
+ /** The EIP-712 payload the wallet is about to sign. Only `message` is read. */
400
+ typedData: {
401
+ message: {
402
+ /** The copy of the hash that is actually signed — this is the one that gets checked. */
403
+ docHash: Hex;
404
+ /** Decimal STRING on the wire, since `eth_signTypedData_v4` wants `uint256` as a string. */
405
+ deadline?: number | bigint | string | undefined;
406
+ };
407
+ };
408
+ }
409
+ /** Shared by both shapes {@link verifyAttestationSession} accepts. */
410
+ interface VerifyAttestationSessionBase extends ClockOptions {
411
+ /** The document bytes the app rendered to the investor. A `string` is hashed as UTF-8. */
412
+ content: Uint8Array | string;
413
+ /**
414
+ * Optional, and normally absent: the client-facing session deliberately does not carry
415
+ * `issuedAt` — the investor does not sign it. Supply it if you have it server-side and the
416
+ * 30-day lifetime cap is checked too.
417
+ */
418
+ issuedAt?: bigint | number | string;
419
+ }
420
+ /** Pass the session's fields directly, e.g. when they were carried through your own types. */
421
+ interface VerifyAttestationSessionFields extends VerifyAttestationSessionBase {
422
+ /** `docHash` exactly as the session delivered it. Compared case-insensitively. */
423
+ docHash: Hex;
424
+ /** Session expiry, unix seconds, in any of the forms the wire uses. */
425
+ deadline: bigint | number | string;
426
+ }
427
+ /** Hand over the `POST /attestations` response itself and let the SDK read it. */
428
+ interface VerifyAttestationSessionResponseParams extends VerifyAttestationSessionBase {
429
+ /** The session response as received. `docHash` is read from `typedData.message`. */
430
+ session: AttestationSessionResponseLike;
431
+ }
432
+ /**
433
+ * What {@link verifyAttestationSession} needs: the rendered bytes, plus either the session response
434
+ * itself or the two fields read out of it.
435
+ */
436
+ type VerifyAttestationSessionParams = VerifyAttestationSessionFields | VerifyAttestationSessionResponseParams;
437
+ /**
438
+ * The one pre-flight call a UI should make before prompting the wallet.
439
+ *
440
+ * Combines the two questions worth asking while the investor is still looking at the document: is
441
+ * this still the document the session commits to ({@link assertDocumentMatchesHash}), and is the
442
+ * session still signable (`assertSessionWindow`, so the lifetime and expiry rules live in exactly
443
+ * one place)?
444
+ *
445
+ * Hand it the session response as it arrived, and the fields are read from the copies that are
446
+ * actually signed:
447
+ *
448
+ * ```ts
449
+ * const session = await client.http.post('/attestations', { wallet, docHash }); // 201 body
450
+ *
451
+ * verifyAttestationSession({
452
+ * content: pdfBytes, // the same executed bytes that were hashed into `docHash` above
453
+ * session,
454
+ * now: BigInt(Math.floor(Date.now() / 1000)), // optional; also checks expiry
455
+ * });
456
+ *
457
+ * const signature = await walletClient.signTypedData({
458
+ * account,
459
+ * ...toViemTypedData(session.typedData, { expectedAttestor }),
460
+ * });
461
+ * ```
462
+ *
463
+ * viem would take `session.typedData` unchanged, but `toViemTypedData` is the shape to reach for:
464
+ * it validates `primaryType`, the `DocumentAcceptance` field list and its ORDER, and the domain,
465
+ * and `expectedAttestor` / `expectedChainId` pin the prompt to the deployment this integration
466
+ * trusts. This function checks the document; that one checks the payload built around it.
467
+ *
468
+ * The fields may also be passed directly — `{ content, docHash, deadline }` — when they have been
469
+ * carried through an app's own types. Either way `docHash` comes from `typedData.message` in
470
+ * preference to the top-level echo, since that is the copy the wallet signs, and `deadline` is
471
+ * coerced from the top-level number (a decimal string or a `bigint` works too).
472
+ *
473
+ * The document check runs first on purpose. When a session is both expired and mismatched, the
474
+ * mismatch is the finding that matters: an expiry is a benign retry, whereas a hash that no longer
475
+ * matches the bytes on screen means the backend is not committing to the document being signed,
476
+ * and no amount of re-issuing sessions will fix that.
477
+ *
478
+ * ### Safes, ERC-1271, and the session TTL
479
+ *
480
+ * This is where an expired session surfaces, so it is also where too short a TTL shows up. Sessions
481
+ * default to a 3600-second TTL (env-tunable up to the contract's 30-day `MAX_ATTESTATION_LIFETIME`
482
+ * cap). An ERC-1271 signer — a Safe — produces no signature until enough owners have confirmed, and
483
+ * that ceremony routinely outlasts an hour. The `deadline` must outlive it: issuers whose investors
484
+ * sign from Safes should have the TTL raised, or the session lapses mid-ceremony and the investor
485
+ * is asked to start over. An `AttestationExpired` from this function on a Safe flow is a TTL
486
+ * configuration finding, not an investor mistake.
487
+ *
488
+ * What this does *not* do: verify the server's half of the authorization. That signature is never
489
+ * given to the client — checking it is the attestor's job, on-chain.
490
+ *
491
+ * The clock checks stay opt-in — omit `now` and a skewed device clock cannot refuse a session the
492
+ * chain would have accepted. Pass `BigInt(Math.floor(Date.now() / 1000))` to enable them.
493
+ *
494
+ * @throws {OpacityAttestationError} See {@link assertDocumentMatchesHash}, plus
495
+ * `AttestationExpired`, `AttestationDeadlineTooFar`, `AttestationIssuedInFuture`, and
496
+ * `MalformedInput` for a session whose `deadline` copies disagree or cannot be read.
497
+ */
498
+ declare function verifyAttestationSession(params: VerifyAttestationSessionParams): void;
499
+
500
+ /** The EIP-712 domain of a `DocumentAttestor` deployment. No `salt` — see `constants.ts`. */
501
+ interface DocumentAttestorDomain {
502
+ name: typeof DOCUMENT_ATTESTOR_DOMAIN_NAME;
503
+ version: typeof DOCUMENT_ATTESTOR_DOMAIN_VERSION;
504
+ chainId: number;
505
+ verifyingContract: Address;
506
+ }
507
+ /** What the investor's wallet is asked to sign. */
508
+ interface DocumentAcceptanceMessage {
509
+ nonce: Hex;
510
+ docHash: Hex;
511
+ deadline: bigint;
512
+ }
513
+ /**
514
+ * A viem/wagmi-native EIP-712 payload.
515
+ *
516
+ * `types` deliberately omits `EIP712Domain`: viem derives the domain type from the `domain` object
517
+ * when the entry is absent, and ethers v6 throws if it is present. Omitting it is only safe
518
+ * *because* those two derive it — a raw `eth_signTypedData_v4` provider does not, so use
519
+ * {@link toEip712JsonPayload} / {@link toEip712Json} for that path. Never hand this shape to
520
+ * `eth_signTypedData_v4` directly; see {@link toEip712JsonPayload} for what silently goes wrong.
521
+ */
522
+ interface DocumentAcceptanceTypedData {
523
+ domain: DocumentAttestorDomain;
524
+ types: typeof DOCUMENT_ACCEPTANCE_TYPES;
525
+ primaryType: typeof DOCUMENT_ACCEPTANCE_PRIMARY_TYPE;
526
+ message: DocumentAcceptanceMessage;
527
+ }
528
+ /**
529
+ * Builds the EIP-712 payload the investor's wallet signs to accept a document.
530
+ *
531
+ * This is a signature prompt, not a transaction: it costs no gas and broadcasts nothing. It is
532
+ * also the investor's *only* wallet interaction — the resulting signature is posted back to the
533
+ * backend, which pairs it with the server's half and relays `buildAttestDocumentTransaction` from
534
+ * a gas-sponsor key. The investor never sends a transaction and never needs to hold gas.
535
+ *
536
+ * The returned object is what viem and wagmi want as-is:
537
+ *
538
+ * ```ts
539
+ * const typedData = buildDocumentAcceptanceTypedData({
540
+ * attestorAddress, chainId, nonce, docHash, deadline, issuedAt,
541
+ * });
542
+ *
543
+ * const signature = await walletClient.signTypedData({ account, ...typedData }); // viem
544
+ * const signature = await signTypedDataAsync(typedData); // wagmi
545
+ * ```
546
+ *
547
+ * For other stacks, pass it through {@link toEthersTypedData} or {@link toEip712JsonPayload}.
548
+ *
549
+ * @throws {import('../errors.js').OpacityAttestationError} when a parameter would produce a
550
+ * signature the attestor is guaranteed to reject.
551
+ */
552
+ declare function buildDocumentAcceptanceTypedData(params: BuildDocumentAcceptanceTypedDataParams): DocumentAcceptanceTypedData;
553
+ /**
554
+ * The EIP-712 digest the wallet actually signs.
555
+ *
556
+ * Useful for logging or for checking a returned signature offline. Note that an ERC-1271 wallet's
557
+ * signature is not recoverable to an address, so digest comparison is not a substitute for the
558
+ * on-chain `SignatureChecker` result.
559
+ */
560
+ declare function hashDocumentAcceptance(typedData: DocumentAcceptanceTypedData): Hex;
561
+ /** ethers v6 `signer.signTypedData(domain, types, value)` takes three positional arguments. */
562
+ interface EthersTypedData {
563
+ domain: DocumentAttestorDomain;
564
+ /**
565
+ * A MUTABLE copy of {@link DOCUMENT_ACCEPTANCE_TYPES}, not the readonly constant itself:
566
+ * ethers v6 types `signTypedData`'s second argument as `Record<string, TypedDataField[]>`
567
+ * (mutable arrays), so the SDK's deeply-readonly `as const` shape fails TS2345 when passed
568
+ * directly. Structurally typed rather than importing ethers' `TypedDataField` — ethers is not
569
+ * a dependency of this package, and its types must not leak into the published declarations.
570
+ */
571
+ types: {
572
+ DocumentAcceptance: {
573
+ name: string;
574
+ type: string;
575
+ }[];
576
+ };
577
+ value: DocumentAcceptanceMessage;
578
+ }
579
+ /**
580
+ * Reshapes the payload for ethers v6.
581
+ *
582
+ * ```ts
583
+ * const { domain, types, value } = toEthersTypedData(typedData);
584
+ * const signature = await signer.signTypedData(domain, types, value);
585
+ * ```
586
+ *
587
+ * `types` still omits `EIP712Domain` — ethers infers the domain from its first argument and throws
588
+ * if the type is also declared.
589
+ */
590
+ declare function toEthersTypedData(typedData: DocumentAcceptanceTypedData): EthersTypedData;
591
+ /** A JSON-safe `eth_signTypedData_v4` payload: no bigints, `EIP712Domain` declared. */
592
+ interface Eip712JsonPayload {
593
+ types: Record<string, ReadonlyArray<{
594
+ readonly name: string;
595
+ readonly type: string;
596
+ }>>;
597
+ primaryType: string;
598
+ domain: {
599
+ name: string;
600
+ version: string;
601
+ chainId: number;
602
+ verifyingContract: Address;
603
+ };
604
+ /** `deadline` is a decimal string, not a bigint — JSON cannot carry one. */
605
+ message: {
606
+ nonce: Hex;
607
+ docHash: Hex;
608
+ deadline: string;
609
+ };
610
+ }
611
+ /**
612
+ * Reshapes the payload for a raw EIP-1193 provider.
613
+ *
614
+ * This is the lowest common denominator every wallet connection service bottoms out at — MetaMask,
615
+ * WalletConnect, Coinbase Wallet, Privy, Dynamic, and the Safe Apps SDK all accept it:
616
+ *
617
+ * ```ts
618
+ * const signature = await provider.request({
619
+ * method: 'eth_signTypedData_v4',
620
+ * params: [account, toEip712Json(typedData)],
621
+ * });
622
+ * ```
623
+ *
624
+ * Two differences from the viem shape are required by the JSON-RPC method, not stylistic:
625
+ * `EIP712Domain` must be declared in `types`, and `uint256` values are decimal strings because JSON
626
+ * cannot carry a bigint.
627
+ *
628
+ * **Declaring `EIP712Domain` is load-bearing, and its absence fails SILENTLY.** MetaMask's
629
+ * `eth-sig-util` sanitises the payload with `{ EIP712Domain: [], ...types }`, so a payload that
630
+ * omits the entry does not error — it hashes the domain as `keccak256("EIP712Domain()")`, a type
631
+ * with NO fields, and the digest moves:
632
+ *
633
+ * ```text
634
+ * EIP712Domain declared 0x91feb0709c4ebdff9ccc57b12b26c50936eae7973f67c6d23780fcfdbda3f410 ✓
635
+ * EIP712Domain defaulted [] 0x82ada7044278f3b529058b2dac44bb987f078c23b15195e1dca77110f0c91691 ✗
636
+ * ```
637
+ *
638
+ * The wallet then returns a perfectly well-formed signature over the WRONG digest, which the
639
+ * backend rejects at its simulate step with `InvalidUserSignature` — far from the cause, and
640
+ * indistinguishable to the investor from "the wallet is broken". Declaring the type does not touch
641
+ * the `DocumentAcceptance` struct hash; it only makes the DOMAIN hash correct. So this is strictly
642
+ * a fix, never a behaviour change, and the `EIP712Domain` entry must not be removed from this
643
+ * function on the theory that the wallet will derive it.
644
+ */
645
+ declare function toEip712JsonPayload(typedData: DocumentAcceptanceTypedData): Eip712JsonPayload;
646
+ /**
647
+ * {@link toEip712JsonPayload}, serialised — the second `eth_signTypedData_v4` parameter.
648
+ *
649
+ * A backend-issued payload reaches this through `toEip712Json(toViemTypedData(session.typedData))`:
650
+ * `toViemTypedData` validates and normalises the server's JSON, and this re-serialises it with
651
+ * `EIP712Domain` declared.
652
+ *
653
+ * That round trip is optional on this path. mobile-api already declares `EIP712Domain` and already
654
+ * sends `deadline` as a decimal string, so `session.typedData` is a correct
655
+ * `eth_signTypedData_v4` payload exactly as it arrives — nothing is corrupted by sending it
656
+ * directly. Going through `toViemTypedData` buys validation (`primaryType`, the field list and its
657
+ * ORDER, the domain) and `expectedAttestor` / `expectedChainId` pinning, and it re-declares
658
+ * `EIP712Domain` from this SDK's own constant rather than trusting the wire's copy. Prefer it, but
659
+ * do not treat the direct route as broken.
660
+ */
661
+ declare function toEip712Json(typedData: DocumentAcceptanceTypedData): string;
662
+
663
+ /** `attestDocument` arguments, in the contract's positional order. */
664
+ type AttestDocumentArgs = readonly [
665
+ wallet: Address,
666
+ traitId: Hex,
667
+ docHash: Hex,
668
+ nonce: Hex,
669
+ issuedAt: bigint,
670
+ deadline: bigint,
671
+ serverSignature: Hex,
672
+ userSignature: Hex
673
+ ];
674
+ /**
675
+ * Builds the `attestDocument` transaction that redeems a signed session on-chain.
676
+ *
677
+ * This is the relay half of the flow, so its consumer is the backend, not the browser. The
678
+ * investor's entire involvement is the off-chain signature from
679
+ * {@link import('./typed-data.js').buildDocumentAcceptanceTypedData} — one wallet prompt, no
680
+ * transaction, no gas. The relayer pairs that signature with the server's half and broadcasts this
681
+ * call from a gas-sponsor key.
682
+ *
683
+ * The call is permissionless — `msg.sender` is never trusted and never recorded — so `from` is
684
+ * optional, and when supplied it names the sponsor account paying for gas. The descriptor works
685
+ * as-is with viem, wagmi and ethers v6:
686
+ *
687
+ * ```ts
688
+ * const tx = buildAttestDocumentTransaction({ ... });
689
+ *
690
+ * await walletClient.sendTransaction({ account, ...tx }); // viem
691
+ * sendTransaction(tx); // wagmi
692
+ * await signer.sendTransaction(tx); // ethers v6
693
+ * ```
694
+ *
695
+ * See {@link toWriteContractRequest} for wagmi's `useWriteContract`, and
696
+ * {@link toJsonRpcTransaction} for a raw EIP-1193 provider.
697
+ *
698
+ * @throws {import('../errors.js').OpacityAttestationError} when a parameter would produce a
699
+ * transaction the attestor is guaranteed to revert.
700
+ */
701
+ declare function buildAttestDocumentTransaction(params: BuildAttestDocumentTransactionParams): TransactionRequest;
702
+ /** The ABI-level form of the same call, for viem `writeContract` / wagmi `useWriteContract`. */
703
+ interface WriteContractRequest {
704
+ address: Address;
705
+ abi: typeof documentAttestorAbi;
706
+ functionName: 'attestDocument';
707
+ args: AttestDocumentArgs;
708
+ chainId: number;
709
+ }
710
+ /**
711
+ * The same call expressed as contract arguments rather than calldata.
712
+ *
713
+ * Preferred by wagmi users, and it gives viem the ABI it needs to decode a revert into a named
714
+ * error (`NotTraitWriter`, `NonceAlreadyUsed`, …) instead of a bare selector.
715
+ *
716
+ * ```ts
717
+ * writeContract(toWriteContractRequest(params));
718
+ * ```
719
+ */
720
+ declare function toWriteContractRequest(params: BuildAttestDocumentTransactionParams): WriteContractRequest;
721
+ /** An `eth_sendTransaction` parameter object — every numeric field hex-encoded. */
722
+ interface JsonRpcTransaction {
723
+ from: Address;
724
+ to: Address;
725
+ data: Hex;
726
+ value: Hex;
727
+ chainId: Hex;
728
+ }
729
+ /**
730
+ * Reshapes the descriptor for a raw EIP-1193 provider.
731
+ *
732
+ * `eth_sendTransaction` demands a mandatory `from` and hex-quantity encoding; the chain-agnostic
733
+ * descriptor has neither (its own `from` is optional and its quantities are bigints), so the
734
+ * sending account is passed explicitly here — under relay, that is the gas sponsor:
735
+ *
736
+ * ```ts
737
+ * const hash = await provider.request({
738
+ * method: 'eth_sendTransaction',
739
+ * params: [toJsonRpcTransaction(tx, { from: relayer })],
740
+ * });
741
+ * ```
742
+ */
743
+ declare function toJsonRpcTransaction(transaction: TransactionRequest, options: {
744
+ from: Address;
745
+ }): JsonRpcTransaction;
746
+
747
+ /**
748
+ * Minimal ABI for `SandboxAllowlistRegistry` — the SANDBOX-ONLY registry implementation.
749
+ *
750
+ * The sandbox implementation is the stock `AllowlistRegistry` plus permissionless, sandbox-only
751
+ * helpers (`sandboxAddSelf()`, v2's `sandboxSetSelfTrait()`, and v3's `sandboxRegisterIssuer()`).
752
+ * It runs behind the registry proxy in a sandbox deployment, so there is no separate helper address: this ABI is used against
753
+ * the `allowlistRegistry` address from discovery or the built-in `SANDBOX_DEPLOYMENTS` entry, and
754
+ * only works while that proxy exposes the sandbox implementation.
755
+ *
756
+ * Covers the writes this module builds (`sandboxAddSelf`, `sandboxSetSelfTrait`), the marker view
757
+ * that identifies the sandbox implementation behind a proxy (`sandboxVersion`), the reads a
758
+ * consumer verifies results with (`isAllowed`, `getUserId`, `hasGlobalTrait`), their emitted trait
759
+ * event, and every error reachable from those writes:
760
+ *
761
+ * - `EnforcedPause()` — the `whenNotPaused` modifier (OpenZeppelin `PausableUpgradeable`) when the
762
+ * registry is paused.
763
+ * - `AddressNotEligible(address)` — the parent `_addToAllowlist` when the sender's entry is in a
764
+ * TERMINAL status (`COMPLIANCE_REMOVAL` or `MIGRATED`); those cannot be re-added by any path.
765
+ * - `AddressNotVerified(address)` — `sandboxSetSelfTrait` requires its caller to be verified first.
766
+ * - `InvalidUserId()` — the caller has no usable userId binding.
767
+ * - `InvalidTraitId()` — `traitId` is zero.
768
+ */
769
+ declare const sandboxAllowlistRegistryAbi: readonly [{
770
+ readonly type: "function";
771
+ readonly name: "sandboxRegisterIssuer";
772
+ readonly stateMutability: "nonpayable";
773
+ readonly inputs: readonly [];
774
+ readonly outputs: readonly [];
775
+ }, {
776
+ readonly type: "function";
777
+ readonly name: "sandboxAddSelf";
778
+ readonly stateMutability: "nonpayable";
779
+ readonly inputs: readonly [];
780
+ readonly outputs: readonly [];
781
+ }, {
782
+ readonly type: "function";
783
+ readonly name: "sandboxSetSelfTrait";
784
+ readonly stateMutability: "nonpayable";
785
+ readonly inputs: readonly [{
786
+ readonly name: "traitId";
787
+ readonly type: "bytes32";
788
+ }, {
789
+ readonly name: "enabled";
790
+ readonly type: "bool";
791
+ }];
792
+ readonly outputs: readonly [];
793
+ }, {
794
+ readonly type: "function";
795
+ readonly name: "sandboxVersion";
796
+ readonly stateMutability: "pure";
797
+ readonly inputs: readonly [];
798
+ readonly outputs: readonly [{
799
+ readonly type: "uint256";
800
+ }];
801
+ }, {
802
+ readonly type: "function";
803
+ readonly name: "isAllowed";
804
+ readonly stateMutability: "view";
805
+ readonly inputs: readonly [{
806
+ readonly name: "account";
807
+ readonly type: "address";
808
+ }];
809
+ readonly outputs: readonly [{
810
+ readonly type: "bool";
811
+ }];
812
+ }, {
813
+ readonly type: "function";
814
+ readonly name: "getUserId";
815
+ readonly stateMutability: "view";
816
+ readonly inputs: readonly [{
817
+ readonly name: "account";
818
+ readonly type: "address";
819
+ }];
820
+ readonly outputs: readonly [{
821
+ readonly type: "bytes32";
822
+ }];
823
+ }, {
824
+ readonly type: "function";
825
+ readonly name: "hasGlobalTrait";
826
+ readonly stateMutability: "view";
827
+ readonly inputs: readonly [{
828
+ readonly name: "userId";
829
+ readonly type: "bytes32";
830
+ }, {
831
+ readonly name: "traitId";
832
+ readonly type: "bytes32";
833
+ }];
834
+ readonly outputs: readonly [{
835
+ readonly type: "bool";
836
+ }];
837
+ }, {
838
+ readonly type: "event";
839
+ readonly name: "GlobalTraitSet";
840
+ readonly anonymous: false;
841
+ readonly inputs: readonly [{
842
+ readonly name: "userId";
843
+ readonly type: "bytes32";
844
+ readonly indexed: true;
845
+ }, {
846
+ readonly name: "traitId";
847
+ readonly type: "bytes32";
848
+ readonly indexed: true;
849
+ }, {
850
+ readonly name: "enabled";
851
+ readonly type: "bool";
852
+ readonly indexed: false;
853
+ }, {
854
+ readonly name: "updatedBy";
855
+ readonly type: "address";
856
+ readonly indexed: false;
857
+ }];
858
+ }, {
859
+ readonly type: "error";
860
+ readonly name: "EnforcedPause";
861
+ readonly inputs: readonly [];
862
+ }, {
863
+ readonly type: "error";
864
+ readonly name: "AddressNotEligible";
865
+ readonly inputs: readonly [{
866
+ readonly name: "account";
867
+ readonly type: "address";
868
+ }];
869
+ }, {
870
+ readonly type: "error";
871
+ readonly name: "AddressNotVerified";
872
+ readonly inputs: readonly [{
873
+ readonly name: "account";
874
+ readonly type: "address";
875
+ }];
876
+ }, {
877
+ readonly type: "error";
878
+ readonly name: "InvalidUserId";
879
+ readonly inputs: readonly [];
880
+ }, {
881
+ readonly type: "error";
882
+ readonly name: "InvalidTraitId";
883
+ readonly inputs: readonly [];
884
+ }];
885
+ /**
886
+ * What `sandboxVersion()` returns on the SDK's target sandbox implementation source.
887
+ *
888
+ * Mirrors the `return 3` in the deployed `SandboxAllowlistRegistry.sol`. A lower live version
889
+ * exposes fewer sandbox capabilities; a higher one is a newer surface this SDK may not know yet.
890
+ */
891
+ declare const SANDBOX_IMPLEMENTATION_VERSION = 3n;
892
+ /**
893
+ * Named trait ids used by the current sandbox product flow.
894
+ *
895
+ * `sandboxSetSelfTrait` itself accepts ANY non-zero `bytes32`; these are conveniences, not an
896
+ * allowlist. `mmfDocumentSigned` is specifically the current MMF subscription document version.
897
+ * Use `documentTraitId(<versioned canonical name>)` for another document or offering.
898
+ */
899
+ declare const SANDBOX_TRAIT_IDS: Readonly<{
900
+ /** `keccak256("trait.accredited")` — the contracts' canonical accredited-investor trait. */
901
+ readonly accreditedInvestor: `0x${string}`;
902
+ /** `keccak256("doc.mmf.subscription.v1")` — the current MMF subscription document trait. */
903
+ readonly mmfDocumentSigned: `0x${string}`;
904
+ }>;
905
+ /**
906
+ * Which registry proxy, on which chain, and optionally which wallet sends.
907
+ *
908
+ * Unlike the attestation flow there is no separate helper contract to point at: `registryAddress`
909
+ * is exactly the sandbox deployment's `allowlistRegistry` proxy returned by the discovery endpoint
910
+ * (`GET /chains/:chain_id/attestor`) — or, for a chain with a built-in entry,
911
+ * `SANDBOX_DEPLOYMENTS[chainId].allowlistRegistry`.
912
+ */
913
+ interface BuildSandboxAddSelfTransactionParams {
914
+ /** The allowlist-registry PROXY address — the same one discovery returns. */
915
+ registryAddress: Address;
916
+ /** Chain the registry proxy is deployed on. */
917
+ chainId: number;
918
+ /**
919
+ * Optional expected sender recorded as JSON-RPC `from`. If supplied it MUST be the investor's
920
+ * own wallet. A viem caller must ALSO pass the same wallet as `account`; prefer the ABI-level
921
+ * builder when account binding is desired.
922
+ */
923
+ from?: Address;
924
+ }
925
+ /**
926
+ * Builds the `sandboxAddSelf()` transaction — a SANDBOX-ONLY, permissionless KYC bypass.
927
+ *
928
+ * **SANDBOX ONLY.** `sandboxAddSelf` exists only on the `SandboxAllowlistRegistry` implementation,
929
+ * a deliberate, total KYC bypass for throwaway sandbox deployments. It must NEVER point at a
930
+ * production registry; on production implementations this calldata reverts with an unknown
931
+ * selector.
932
+ *
933
+ * **The SENDER is who gets allowlisted.** This is the exact opposite of `attestDocument`'s
934
+ * permissionless relay: the contract allowlists `msg.sender`, so this transaction CANNOT be
935
+ * relayed on an investor's behalf. The investor's own wallet must send it, and that wallet pays
936
+ * the gas.
937
+ *
938
+ * `registryAddress` is the same allowlist-registry proxy discovery returns (`allowlistRegistry`
939
+ * from `GET /chains/:chain_id/attestor`) — same address, same events.
940
+ *
941
+ * On success the sender holds a VERIFIED allowlist entry stamped with the registry's default
942
+ * expiry window, and on the FIRST call the wallet is bound to the deterministic userId
943
+ * {@link sandboxUserId} computes. With viem, pass the investor explicitly as `account` when
944
+ * sending; the raw descriptor's `from` is not an account-selection mechanism. See
945
+ * {@link toSandboxAddSelfWriteContractRequest} for an ABI-level request whose `account` is bound,
946
+ * and `toJsonRpcTransaction` from `transaction.ts` for a raw EIP-1193 provider.
947
+ *
948
+ * @throws {import('../errors.js').OpacityAttestationError} when `registryAddress`, `chainId` or
949
+ * `from` is malformed or zero.
950
+ */
951
+ declare function buildSandboxAddSelfTransaction(params: BuildSandboxAddSelfTransactionParams): TransactionRequest;
952
+ /** The ABI-level form of the same call, for viem `writeContract` / wagmi `useWriteContract`. */
953
+ interface SandboxAddSelfWriteContractRequest {
954
+ address: Address;
955
+ abi: typeof sandboxAllowlistRegistryAbi;
956
+ functionName: 'sandboxAddSelf';
957
+ args: readonly [];
958
+ chainId: number;
959
+ /** Present only when the caller pinned a `from` — the investor wallet that must send. */
960
+ account?: Address;
961
+ }
962
+ /**
963
+ * The same SANDBOX-ONLY call expressed as contract arguments rather than calldata.
964
+ *
965
+ * Preferred by wagmi users, and it gives viem the ABI it needs to decode a revert into a named
966
+ * error (`EnforcedPause`, `AddressNotEligible`) instead of a bare selector. All the warnings on
967
+ * {@link buildSandboxAddSelfTransaction} apply verbatim — sandbox only, and whichever account
968
+ * sends this request is the one that gets allowlisted; a supplied `from` becomes `account`.
969
+ *
970
+ * ```ts
971
+ * writeContract(toSandboxAddSelfWriteContractRequest({ registryAddress, chainId }));
972
+ * ```
973
+ */
974
+ declare function toSandboxAddSelfWriteContractRequest(params: BuildSandboxAddSelfTransactionParams): SandboxAddSelfWriteContractRequest;
975
+ /** Sandbox v3 issuer registration uses the same caller/registry binding as self-onboarding. */
976
+ type BuildSandboxRegisterIssuerTransactionParams = BuildSandboxAddSelfTransactionParams;
977
+ /** Onboard and authorize the sending wallet as an issuer, without an operator transaction. */
978
+ declare function buildSandboxRegisterIssuerTransaction(params: BuildSandboxRegisterIssuerTransactionParams): TransactionRequest;
979
+ /** ABI-level registration request for the issuer's own wallet. Requires a v3 registry. */
980
+ type SandboxRegisterIssuerWriteContractRequest = Omit<SandboxAddSelfWriteContractRequest, 'functionName'> & {
981
+ functionName: 'sandboxRegisterIssuer';
982
+ };
983
+ declare function toSandboxRegisterIssuerWriteContractRequest(params: BuildSandboxRegisterIssuerTransactionParams): SandboxRegisterIssuerWriteContractRequest;
984
+ /** Inputs shared by the calldata and ABI-level `sandboxSetSelfTrait` builders. */
985
+ interface BuildSandboxSetSelfTraitTransactionParams {
986
+ /** The allowlist-registry PROXY running `SandboxAllowlistRegistry` v2 or newer. */
987
+ registryAddress: Address;
988
+ /** Chain the registry proxy is deployed on. */
989
+ chainId: number;
990
+ /** Any non-zero global trait id. See {@link SANDBOX_TRAIT_IDS} for current presets. */
991
+ traitId: Hex;
992
+ /** `true` grants the trait; `false` clears it. */
993
+ enabled: boolean;
994
+ /**
995
+ * Optional expected sender as JSON-RPC `from`; viem callers must also select it as `account`.
996
+ * This wallet's own userId receives (or loses) the trait.
997
+ */
998
+ from?: Address;
999
+ }
1000
+ /**
1001
+ * Builds a `sandboxSetSelfTrait(bytes32,bool)` transaction for the caller's own global trait.
1002
+ *
1003
+ * **SANDBOX v2 OR NEWER.** This is a deliberate KYC/document/compliance bypass and is absent from the
1004
+ * production registry and sandbox v1. The sending wallet must already have VERIFIED status —
1005
+ * normally by sending {@link buildSandboxAddSelfTransaction} first — and the trait is written to
1006
+ * that wallet's existing userId. There is deliberately no combined add+trait transaction.
1007
+ *
1008
+ * `traitId` can be any non-zero `bytes32`. `enabled: true` grants it; `enabled: false` clears it.
1009
+ * Both directions emit `GlobalTraitSet` with `updatedBy` equal to the sending wallet.
1010
+ *
1011
+ * @throws {import('../errors.js').OpacityAttestationError} when an address, `chainId`, `traitId`,
1012
+ * or `enabled` is malformed; zero `traitId` is rejected locally as `InvalidTraitId`.
1013
+ */
1014
+ declare function buildSandboxSetSelfTraitTransaction(params: BuildSandboxSetSelfTraitTransactionParams): TransactionRequest;
1015
+ /** ABI-level form of `sandboxSetSelfTrait`, for viem/wagmi `writeContract`. */
1016
+ interface SandboxSetSelfTraitWriteContractRequest {
1017
+ address: Address;
1018
+ abi: typeof sandboxAllowlistRegistryAbi;
1019
+ functionName: 'sandboxSetSelfTrait';
1020
+ args: readonly [traitId: Hex, enabled: boolean];
1021
+ chainId: number;
1022
+ /** Present only when the caller pins the wallet whose own trait will change. */
1023
+ account?: Address;
1024
+ }
1025
+ /**
1026
+ * Expresses the same sandbox-v2 trait write as ABI + args for viem/wagmi.
1027
+ *
1028
+ * All warnings and validation on {@link buildSandboxSetSelfTraitTransaction} apply. A supplied
1029
+ * `from` becomes `account`; that account must send the transaction and is the identity modified.
1030
+ */
1031
+ declare function toSandboxSetSelfTraitWriteContractRequest(params: BuildSandboxSetSelfTraitTransactionParams): SandboxSetSelfTraitWriteContractRequest;
1032
+ /**
1033
+ * The deterministic userId `sandboxAddSelf` binds a wallet to on its FIRST call:
1034
+ * `keccak256(abi.encodePacked("sandbox.user.", wallet))` — the 13 UTF-8 bytes of the prefix
1035
+ * concatenated with the 20 address bytes, PACKED, not ABI-padded.
1036
+ *
1037
+ * Checksum-insensitive: the address is normalised before hashing, so any casing of the same
1038
+ * address produces the same id.
1039
+ *
1040
+ * This predicts the userId ONLY for the first-call binding. A wallet previously onboarded through
1041
+ * another path (TEE batch, compliance admin, …) already has a userId, and `sandboxAddSelf` keeps
1042
+ * that existing binding — read the real one back with `getUserId(wallet)` rather than assuming
1043
+ * this value.
1044
+ *
1045
+ * @throws {import('../errors.js').OpacityAttestationError} when `wallet` is malformed or zero.
1046
+ */
1047
+ declare function sandboxUserId(wallet: Address): Hex;
1048
+ /**
1049
+ * The RPC capabilities {@link probeSandboxRegistry} needs: bytecode lookup, chain identity, and
1050
+ * `eth_call`.
1051
+ *
1052
+ * A structural subset of viem's `PublicClient` — pass the public client your app already has
1053
+ * (wagmi's `usePublicClient()`, or `createPublicClient`). The SDK never opens an RPC connection
1054
+ * of its own, which is why this is a parameter and not configuration it resolves.
1055
+ */
1056
+ interface SandboxProbeClient {
1057
+ /** Named `getBytecode` for compatibility with the full declared viem v2 peer range. */
1058
+ getBytecode(args: {
1059
+ address: Address;
1060
+ }): Promise<Hex | undefined>;
1061
+ getChainId(): Promise<number>;
1062
+ call(args: {
1063
+ to: Address;
1064
+ data: Hex;
1065
+ account?: Address;
1066
+ }): Promise<{
1067
+ data?: Hex | undefined;
1068
+ }>;
1069
+ }
1070
+ interface ProbeSandboxRegistryParams {
1071
+ /** The allowlist-registry PROXY to inspect. */
1072
+ registryAddress: Address;
1073
+ /** Expected RPC chain. Required so a wrong-network reader can never produce a verdict. */
1074
+ chainId: number;
1075
+ /**
1076
+ * Sender to impersonate in the legacy `sandboxAddSelf()` `eth_call` — the investor's wallet,
1077
+ * typically, so a `self_add_revert` verdict is about THEIR entry. Defaults to a nobody address.
1078
+ * Nothing is sent either way.
1079
+ */
1080
+ from?: Address;
1081
+ }
1082
+ /**
1083
+ * How {@link probeSandboxRegistry} reached its verdict, most to least direct.
1084
+ *
1085
+ * - `marker` — `sandboxVersion()` answered. A sandbox implementation with the marker (source from
1086
+ * 2026-09-01 on) is attached; `version` carries the answer.
1087
+ * - `self_add_call` — the marker reverted, but an `eth_call` of `sandboxAddSelf()` from `from`
1088
+ * returned the empty data its no-output ABI requires. This is compatibility evidence for an
1089
+ * older, PRE-marker sandbox implementation, but is necessarily heuristic because a foreign
1090
+ * contract can have a permissive empty fallback.
1091
+ * - `self_add_revert` — `sandboxAddSelf()` exists but refused the probe sender with one of its own
1092
+ * errors (`EnforcedPause`, `AddressNotEligible`). The sandbox IS attached; the real transaction
1093
+ * from that sender would revert the same way right now.
1094
+ * - `not_sandbox` — the marker returned an invalid/empty answer, or both selectors reverted with no
1095
+ * sandbox registry error. A stock `AllowlistRegistry` (or something else) answers here.
1096
+ * - `no_code` — bytecode lookup proved nothing is deployed at the address.
1097
+ */
1098
+ type SandboxProbeEvidence = 'marker' | 'self_add_call' | 'self_add_revert' | 'not_sandbox' | 'no_code';
1099
+ /** The verdict of {@link probeSandboxRegistry}. */
1100
+ interface SandboxRegistryProbe {
1101
+ /** The registry that was inspected, checksummed. */
1102
+ registryAddress: Address;
1103
+ /** Whether a `SandboxAllowlistRegistry` implementation answers behind the proxy. */
1104
+ attached: boolean;
1105
+ /**
1106
+ * `sandboxVersion()` when the implementation exposes it; `null` for a pre-marker sandbox
1107
+ * implementation and for anything that is not the sandbox. Version 2 or newer is required for
1108
+ * `sandboxSetSelfTrait`.
1109
+ */
1110
+ version: bigint | null;
1111
+ /** How the verdict was reached. See {@link SandboxProbeEvidence}. */
1112
+ evidence: SandboxProbeEvidence;
1113
+ }
1114
+ /**
1115
+ * Asks a registry proxy, on-chain, whether the sandbox implementation is attached — BEFORE an
1116
+ * investor is prompted to send `sandboxAddSelf()`.
1117
+ *
1118
+ * The sandbox registry is upgradeable, so an address alone does not prove which implementation
1119
+ * currently answers behind it. Two read-only `eth_call`s settle it:
1120
+ *
1121
+ * 1. `sandboxVersion()` — the marker the contracts added for exactly this purpose. Present, and
1122
+ * the answer is the verdict (`marker`).
1123
+ * 2. Otherwise `sandboxAddSelf()` itself, simulated from `from`. Older sandbox implementations
1124
+ * deployed before the marker existed have the function but not the view. Its expected empty
1125
+ * return (or a sandbox-specific revert) is a compatibility signal (`self_add_call` /
1126
+ * `self_add_revert`), not cryptographic proof: a permissive fallback can imitate empty success.
1127
+ * Marker-based version detection is authoritative for v1+.
1128
+ *
1129
+ * The reader's chain is checked first, then bytecode, because a wrong network or an empty account
1130
+ * must never produce a verdict. Only an execution REVERT is treated as an answer; a transport
1131
+ * failure propagates as thrown by the client, so a flaky node never reads as "not a sandbox".
1132
+ *
1133
+ * ```ts
1134
+ * const probe = await probeSandboxRegistry(publicClient, {
1135
+ * registryAddress,
1136
+ * chainId: 11155111,
1137
+ * from: investor,
1138
+ * });
1139
+ * if (!probe.attached) showError('This registry is not running the sandbox implementation.');
1140
+ * ```
1141
+ *
1142
+ * @throws {import('../errors.js').OpacityAttestationError} when `registryAddress`, `chainId`, or
1143
+ * `from` is malformed or zero.
1144
+ * @throws {import('../errors.js').OpacityConfigError} when the reader is connected to a different
1145
+ * chain than `chainId`.
1146
+ * @throws whatever the RPC client throws for a non-revert failure, untouched.
1147
+ */
1148
+ declare function probeSandboxRegistry(client: SandboxProbeClient, params: ProbeSandboxRegistryParams): Promise<SandboxRegistryProbe>;
1149
+
1150
+ /**
1151
+ * Sepolia's chain id — the one chain the shared Opacity sandbox is deployed on today.
1152
+ */
1153
+ declare const SEPOLIA_CHAIN_ID = 11155111;
1154
+ /**
1155
+ * The chain `client.sandbox` assumes when the client has no `chainId` configured and none is
1156
+ * passed per call. Sepolia, for now: it is the only chain with a sandbox deployment, so it is the
1157
+ * only honest default. `client.documents` deliberately does NOT inherit this default — the
1158
+ * attestation flow is chain-agnostic and refuses to guess.
1159
+ */
1160
+ declare const SANDBOX_CHAIN_ID = 11155111;
1161
+ /**
1162
+ * The document/onboarding addresses of one sandbox deployment. Purchase vault/token/payment
1163
+ * addresses are separately pinned in `SANDBOX_PURCHASE_DEPLOYMENT`.
1164
+ *
1165
+ * The sandbox registry is upgradeable, so `allowlistRegistry` is the proxy address rather than
1166
+ * its current implementation. The Sepolia sandbox is a dedicated deployment initialized with
1167
+ * `SandboxAllowlistRegistry`; whether that implementation still answers is a live question — see
1168
+ * `probeSandboxRegistry`.
1169
+ */
1170
+ interface SandboxDeployment {
1171
+ /** The chain these addresses live on. */
1172
+ chainId: number;
1173
+ /** The allowlist-registry PROXY running `SandboxAllowlistRegistry`. The target of `sandboxAddSelf()`. */
1174
+ allowlistRegistry: Address;
1175
+ /**
1176
+ * The `DocumentAttestor` wired as that registry's trait writer — the EIP-712 `verifyingContract`
1177
+ * of every session the sandbox backend issues. Pin it as `expectedAttestor` when you want the
1178
+ * wallet prompt refused if discovery ever disagrees.
1179
+ */
1180
+ documentAttestor: Address;
1181
+ }
1182
+ /**
1183
+ * Built-in sandbox deployments, keyed by chain id. Sepolia only, today.
1184
+ *
1185
+ * These and `SANDBOX_PURCHASE_DEPLOYMENT` are testnet-only constants; production addresses are
1186
+ * never baked in. They serve as
1187
+ * the FALLBACK for `client.sandbox` when discovery cannot answer, and as constants a consumer can
1188
+ * pin (`expectedAttestor`) or use with the pure builders without a backend at all.
1189
+ *
1190
+ * Source and receipt provenance: SEPOLIA_SANDBOX_MANIFEST and
1191
+ * deployments/sepolia-sandbox-v3.json. Verified sandboxVersion() = 3 after all
1192
+ * deployment and MMF seed transactions confirmed. Discovery remains backend-owned;
1193
+ * pin these addresses explicitly during the subgraph/backend cutover.
1194
+ */
1195
+ declare const SANDBOX_DEPLOYMENTS: Readonly<Partial<Record<number, SandboxDeployment>>>;
1196
+ /** The built-in sandbox deployment for a chain, or `undefined` when the SDK ships none for it. */
1197
+ declare function getSandboxDeployment(chainId: number): SandboxDeployment | undefined;
1198
+ /**
1199
+ * The built-in sandbox deployment for a chain, or an error naming the chains that have one.
1200
+ *
1201
+ * @throws {OpacityConfigError} when no built-in deployment exists for `chainId`.
1202
+ */
1203
+ declare function requireSandboxDeployment(chainId: number): SandboxDeployment;
1204
+
1205
+ /** One entry of an EIP-712 struct definition, as it arrives over the wire. */
1206
+ interface Eip712TypeField {
1207
+ name: string;
1208
+ type: string;
1209
+ }
1210
+ /**
1211
+ * The `typedData` block of a create response, exactly as the backend sends it.
1212
+ *
1213
+ * JSON-shaped: `deadline` is a decimal STRING because JSON cannot carry a `bigint`, and `types`
1214
+ * declares `EIP712Domain` alongside `DocumentAcceptance`.
1215
+ *
1216
+ * **Signable as it arrives on a raw `eth_signTypedData_v4` provider.** That method wants exactly
1217
+ * this JSON form, and mobile-api already declares `EIP712Domain` in `types`, so no reshaping is
1218
+ * needed. {@link toEip1193Json} / {@link toEip1193TypedData} remain worthwhile there for their
1219
+ * validation and attestor pinning, not because the payload needs repair.
1220
+ *
1221
+ * **viem and wagmi also take it as it arrives.** viem parses the decimal-string `uint256` to the
1222
+ * same digest a `bigint` produces, and ignores the extra `EIP712Domain` entry. Run it through
1223
+ * {@link toViemTypedData} anyway — for the validation and the `expectedAttestor` /
1224
+ * `expectedChainId` pinning, not for correctness.
1225
+ *
1226
+ * **ethers v6 is the one stack that cannot take it**, and that is the hard requirement
1227
+ * {@link toViemTypedData} exists to satisfy. `TypedDataEncoder` rejects a `types` map that declares
1228
+ * the domain, throwing `ambiguous primary types or unused types: "EIP712Domain",
1229
+ * "DocumentAcceptance"` — so the raw payload never reaches an ethers signer at all. Route it
1230
+ * through {@link toViemTypedData} (whose `types` is this SDK's constant, with no `EIP712Domain`
1231
+ * entry) and then `toEthersTypedData`.
1232
+ */
1233
+ interface ServerAttestationTypedData {
1234
+ domain: {
1235
+ name: string;
1236
+ version: string;
1237
+ chainId: number;
1238
+ verifyingContract: Address;
1239
+ };
1240
+ types: Record<string, readonly Eip712TypeField[]>;
1241
+ primaryType: string;
1242
+ message: {
1243
+ nonce: Hex;
1244
+ docHash: Hex;
1245
+ /** Decimal string of unix seconds. NOT a number, and NOT a bigint. */
1246
+ deadline: string;
1247
+ };
1248
+ }
1249
+ /** Optional domain pinning for {@link toViemTypedData}. */
1250
+ interface ToViemTypedDataOptions {
1251
+ /**
1252
+ * The `DocumentAttestor` address this client will accept, checked against
1253
+ * `typedData.domain.verifyingContract`.
1254
+ *
1255
+ * Supplying it is defence in depth and costs nothing: a client that blindly signs whatever
1256
+ * domain arrives over the wire has no independent anchor on which contract it is authorising, so
1257
+ * a compromised or merely misconfigured backend could aim a valid-looking prompt at a different
1258
+ * deployment. The investor cannot tell those apart; a pinned constant can.
1259
+ */
1260
+ expectedAttestor?: Address;
1261
+ /** The chain id this client will accept, checked against `typedData.domain.chainId`. */
1262
+ expectedChainId?: number;
1263
+ }
1264
+ /**
1265
+ * Validates the backend's JSON `typedData` and returns it in this SDK's own viem-shaped form — the
1266
+ * one `toEthersTypedData`, `toEip712JsonPayload` and `hashDocumentAcceptance` all take.
1267
+ *
1268
+ * **Only ethers strictly needs it, and not for the reason you would guess.** The `deadline`
1269
+ * reshaping is cosmetic: viem and ethers both hash the wire's decimal-string `uint256` to the same
1270
+ * digest as the `bigint` this returns. What ethers v6 will NOT accept is the `EIP712Domain` entry
1271
+ * mobile-api declares in `types` — `TypedDataEncoder` throws `ambiguous primary types or unused
1272
+ * types: "EIP712Domain", "DocumentAcceptance"` — so the server's payload cannot reach an ethers
1273
+ * signer at all. This function's `types` is the SDK's own constant, which omits that entry, which
1274
+ * is what makes the `toEthersTypedData` hand-off work.
1275
+ *
1276
+ * **viem, wagmi and a raw `eth_signTypedData_v4` provider all accept `session.typedData` as it
1277
+ * arrives.** viem tolerates both the decimal string and the extra `EIP712Domain` entry;
1278
+ * `eth_signTypedData_v4` is specified in terms of exactly that JSON shape. Use this function on
1279
+ * those paths for what it validates and pins, not because the payload needs repair — and use
1280
+ * {@link toEip1193Json} for the EIP-1193 one, which runs these same checks and re-serialises.
1281
+ *
1282
+ * Everything else this function does is validation, because the payload it is handed decides what
1283
+ * a human is about to authorise:
1284
+ *
1285
+ * - `primaryType` must be `DocumentAcceptance`.
1286
+ * - The `DocumentAcceptance` field list must match {@link DOCUMENT_ACCEPTANCE_TYPES} exactly —
1287
+ * same names, same types, same ORDER. Order is consensus-critical: it is part of the type hash,
1288
+ * and a reordered list produces a valid signature over a different struct. Unreferenced extra
1289
+ * entries in `types` are ignored: the encoding of this payload is fully determined by the primary
1290
+ * type's field list, all of which are atomic, so an unused extra type cannot change the digest.
1291
+ * - `EIP712Domain` is the one exception to that, and mobile-api declares it on EVERY session — so
1292
+ * this check runs on the normal path, not on a hypothetical future one. A declaration matching
1293
+ * {@link EIP712_DOMAIN_TYPE} is accepted; any other declaration — including the empty list — is
1294
+ * rejected, because that entry is not inert. It replaces the domain type a wallet would otherwise
1295
+ * derive, so a wrong one re-hashes the domain and moves the digest. An absent entry is tolerated
1296
+ * too, since viem and ethers derive the type themselves and this SDK re-declares it on the
1297
+ * EIP-1193 path.
1298
+ * - The domain `name`/`version` must be `DocumentAttestor`/`1`, and `chainId` /
1299
+ * `verifyingContract` must be well-formed.
1300
+ * - `nonce` and `docHash` must be well-formed `bytes32`, and `docHash` non-zero.
1301
+ *
1302
+ * Pass {@link ToViemTypedDataOptions} to additionally pin the domain to a deployment this client
1303
+ * already knows about. Optional, and worth taking: see the field docs for why blind signing leaves
1304
+ * the client with no independent anchor.
1305
+ *
1306
+ * The returned `types` object is this SDK's own constant rather than the server's array. That is
1307
+ * safe precisely because the check above proved the two are identical field for field, and it
1308
+ * means the result is the same `DocumentAcceptanceTypedData` the local builder produces — so
1309
+ * `toEthersTypedData`, `toEip712JsonPayload` and `hashDocumentAcceptance` all accept it.
1310
+ *
1311
+ * @throws {import('../errors.js').OpacityAttestationError} on any mismatch. Nothing is coerced
1312
+ * silently; a payload that does not validate must not reach a wallet.
1313
+ */
1314
+ declare function toViemTypedData(typedData: ServerAttestationTypedData, options?: ToViemTypedDataOptions): DocumentAcceptanceTypedData;
1315
+ /**
1316
+ * Converts the backend's JSON `typedData` into a payload a raw EIP-1193 provider can sign, and
1317
+ * validates it on the way through.
1318
+ *
1319
+ * ```ts
1320
+ * const signature = await provider.request({
1321
+ * method: 'eth_signTypedData_v4',
1322
+ * params: [account, toEip1193Json(session.typedData, { expectedAttestor, expectedChainId })],
1323
+ * });
1324
+ * ```
1325
+ *
1326
+ * Sending `session.typedData` as it arrives is FINE — mobile-api declares `EIP712Domain`
1327
+ * alongside `DocumentAcceptance`, which is what makes it safe. This function is the belt-and-braces
1328
+ * route: it validates and pins the payload before a wallet ever sees it.
1329
+ *
1330
+ * What it guards against is a `types` map that declares `EIP712Domain` INCORRECTLY, or omits it.
1331
+ * `eth_signTypedData_v4` implementations built on MetaMask's `eth-sig-util` sanitise with
1332
+ * `{ EIP712Domain: [], ...types }`, so a missing entry becomes an EMPTY field list rather than an
1333
+ * error. The domain then hashes as `keccak256("EIP712Domain()")` — no `name`, no `version`, no
1334
+ * `chainId`, no `verifyingContract` — and the digest changes:
1335
+ *
1336
+ * ```text
1337
+ * EIP712Domain declared 0x91feb0709c4ebdff9ccc57b12b26c50936eae7973f67c6d23780fcfdbda3f410 ✓
1338
+ * EIP712Domain defaulted [] 0x82ada7044278f3b529058b2dac44bb987f078c23b15195e1dca77110f0c91691 ✗
1339
+ * ```
1340
+ *
1341
+ * Nothing throws. The wallet shows the right prompt and returns a well-formed signature over a
1342
+ * digest the contract has never heard of, and the failure surfaces much later as an
1343
+ * `InvalidUserSignature` from the relay's simulate step — which reads like "your wallet is
1344
+ * broken", not "the payload was missing a type". That is why {@link assertDomainFieldsIfDeclared}
1345
+ * rejects a MALFORMED `EIP712Domain` rather than quietly replacing it: the SDK will not hand a
1346
+ * wallet a payload whose domain type it cannot vouch for.
1347
+ *
1348
+ * The conversion runs {@link toViemTypedData} first, so every check listed there applies — the
1349
+ * primary type, the field list and its ORDER, the domain, and the optional
1350
+ * {@link ToViemTypedDataOptions} pinning — and then {@link toEip712JsonPayload} re-serialises with
1351
+ * `EIP712Domain` declared and `deadline` back to a decimal string. That round trip through a
1352
+ * `bigint` is deliberate: it is what proves the string the wallet receives is a well-formed
1353
+ * `uint256` rather than whatever the wire happened to carry.
1354
+ *
1355
+ * @throws {import('../errors.js').OpacityAttestationError} on any mismatch, exactly as
1356
+ * {@link toViemTypedData} does. A payload that does not validate must not reach a wallet.
1357
+ */
1358
+ declare function toEip1193TypedData(typedData: ServerAttestationTypedData, options?: ToViemTypedDataOptions): Eip712JsonPayload;
1359
+ /**
1360
+ * {@link toEip1193TypedData}, serialised — the second `eth_signTypedData_v4` parameter.
1361
+ *
1362
+ * Providers differ on whether they want the object or the JSON string; the string is accepted
1363
+ * everywhere, so this is the safer default. Same validation, same reason for existing: see
1364
+ * {@link toEip1193TypedData} for the digest that silently changes without it.
1365
+ *
1366
+ * @throws {import('../errors.js').OpacityAttestationError} on any mismatch.
1367
+ */
1368
+ declare function toEip1193Json(typedData: ServerAttestationTypedData, options?: ToViemTypedDataOptions): string;
1369
+
1370
+ export { type AttestationSessionResponseLike, BuildAttestDocumentTransactionParams, BuildDocumentAcceptanceTypedDataParams, type BuildSandboxAddSelfTransactionParams, type BuildSandboxRegisterIssuerTransactionParams, type BuildSandboxSetSelfTraitTransactionParams, ClockOptions, DOCUMENT_ACCEPTANCE_PRIMARY_TYPE, DOCUMENT_ACCEPTANCE_TYPEHASH, DOCUMENT_ACCEPTANCE_TYPES, DOCUMENT_ATTESTOR_DOMAIN_NAME, DOCUMENT_ATTESTOR_DOMAIN_VERSION, type DocumentAcceptanceMessage, type DocumentAcceptanceTypedData, type DocumentAttestorDomain, ECDSA_SIGNATURE_BYTES, EIP712_DOMAIN_TYPE, type Eip712JsonPayload, type Eip712TypeField, type EthersTypedData, type JsonRpcTransaction, MAX_ATTESTATION_LIFETIME_SECONDS, type ProbeSandboxRegistryParams, SANDBOX_CHAIN_ID, SANDBOX_DEPLOYMENTS, SANDBOX_IMPLEMENTATION_VERSION, SANDBOX_TRAIT_IDS, SEPOLIA_CHAIN_ID, type SandboxAddSelfWriteContractRequest, type SandboxDeployment, type SandboxProbeClient, type SandboxProbeEvidence, type SandboxRegisterIssuerWriteContractRequest, type SandboxRegistryProbe, type SandboxSetSelfTraitWriteContractRequest, type ServerAttestationTypedData, type ToViemTypedDataOptions, TransactionRequest, type VerifyAttestationSessionFields, type VerifyAttestationSessionParams, type VerifyAttestationSessionResponseParams, type WriteContractRequest, assertDocumentMatchesHash, buildAttestDocumentTransaction, buildDocumentAcceptanceTypedData, buildSandboxAddSelfTransaction, buildSandboxRegisterIssuerTransaction, buildSandboxSetSelfTraitTransaction, documentAttestorAbi, documentTraitId, getSandboxDeployment, hashDocument, hashDocumentAcceptance, probeSandboxRegistry, requireSandboxDeployment, sandboxAllowlistRegistryAbi, sandboxUserId, toEip1193Json, toEip1193TypedData, toEip712Json, toEip712JsonPayload, toEthersTypedData, toJsonRpcTransaction, toSandboxAddSelfWriteContractRequest, toSandboxRegisterIssuerWriteContractRequest, toSandboxSetSelfTraitWriteContractRequest, toViemTypedData, toWriteContractRequest, verifyAttestationSession };