@opaquecash/opaque 0.1.1 → 0.2.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/dist/client.d.ts CHANGED
@@ -1,10 +1,16 @@
1
- import { type Account, type Address, type Chain, type Hex, type Transport, type WalletClient } from "viem";
2
- import { buildActionScope, externalNullifierFromScope, type ProofData } from "@opaquecash/psr-core";
1
+ import { type Account, type Address, type Chain, type EIP1193Provider, type Hex, type PublicClient, type Transport, type WalletClient } from "viem";
2
+ import { Keypair, PublicKey, Transaction, type TransactionInstruction } from "@solana/web3.js";
3
+ import { type SolanaAdapterConfig, type OnsClaimStatus } from "@opaquecash/stealth-chain-solana";
4
+ import type { Announcement } from "@opaquecash/adapter";
5
+ import { buildActionScope, externalNullifierFromScope, type AttestationV2, type FieldDef, type ProofData, type SchemaV2 } from "@opaquecash/psr-core";
3
6
  import type { DiscoveredTrait } from "@opaquecash/psr-core";
4
7
  import { type VerifyReputationArgs } from "@opaquecash/psr-chain";
5
8
  import { type ArtifactPaths, type ProofProgressCallback } from "@opaquecash/psr-prover";
6
9
  import { type TrackedToken } from "@opaquecash/stealth-balance";
10
+ import type { AnnounceWithRelayRequest, UabIndexerAnnouncement } from "@opaquecash/uab";
7
11
  import { type OpaqueChainDeployment } from "./chains.js";
12
+ import { type ResolvedRecipient } from "./resolve.js";
13
+ import { type UnifiedSigner } from "./signer.js";
8
14
  import type { IndexerAnnouncement, OwnedStealthOutput, TokenBalanceSummary } from "./types/indexer.js";
9
15
  /**
10
16
  * Configuration for {@link OpaqueClient.create}.
@@ -24,8 +30,13 @@ export interface OpaqueClientConfig {
24
30
  * you still pass the same address to your wallet when sending txs).
25
31
  */
26
32
  ethereumAddress: Address;
27
- /** Dynamic import URL for wasm-pack `cryptography.js`. */
28
- wasmModuleSpecifier: string;
33
+ /**
34
+ * Dynamic import URL for wasm-pack `cryptography.js`. Required for scanning, sweeping, trait
35
+ * discovery, key reconstruction, and proof generation. **Optional** when you only use the PSR
36
+ * admin API (schema/attestation management uses pure-JS DKSAP, no WASM); omitting it and then
37
+ * calling a WASM-backed method throws a clear error.
38
+ */
39
+ wasmModuleSpecifier?: string;
29
40
  /**
30
41
  * Extra ERC-20s (and native) to aggregate. Merged with chain defaults; native uses
31
42
  * {@link NATIVE_TOKEN_ADDRESS}.
@@ -36,8 +47,230 @@ export interface OpaqueClientConfig {
36
47
  stealthMetaAddressRegistry: Address;
37
48
  stealthAddressAnnouncer: Address;
38
49
  opaqueReputationVerifier: Address;
50
+ uabSender: Address;
51
+ uabReceiver: Address;
52
+ wormholeCore: Address;
39
53
  }>;
54
+ /**
55
+ * Solana access for the unified {@link OpaqueClient.scan} inbox. Optional: only needed when
56
+ * `scan({ chains })` includes `"solana"`. Pass a `connection`, `rpcUrl`, or `cluster`
57
+ * (defaults to devnet). The viewing/spending keys are chain-neutral — no Solana identity required.
58
+ */
59
+ solana?: SolanaAdapterConfig;
60
+ /**
61
+ * EIP-1193 provider (e.g. `window.ethereum` or a wallet bridge) used to SIGN Ethereum PSR
62
+ * writes (`createSchema`, `issueAttestation`, …). Reads never need it. Transactions are signed
63
+ * by {@link ethereumAddress}; omit it for read-only / Solana-only usage.
64
+ */
65
+ ethereumProvider?: EIP1193Provider;
66
+ /**
67
+ * Pre-built viem `WalletClient` for Ethereum PSR writes (e.g. a backend issuer signing with a
68
+ * `privateKeyToAccount`). Takes precedence over {@link ethereumProvider}; the account it carries
69
+ * signs, so it should match {@link ethereumAddress}.
70
+ */
71
+ ethereumWalletClient?: WalletClient;
72
+ /**
73
+ * Solana wallet used to SIGN Solana PSR writes. `publicKey` is the issuer/authority; pass a
74
+ * `@solana/web3.js` `PublicKey` or a base58 string. `signTransaction` matches the wallet-adapter
75
+ * signature. Required only for Solana PSR writes.
76
+ */
77
+ solanaWallet?: {
78
+ publicKey: PublicKey | string;
79
+ signTransaction: (transaction: Transaction) => Promise<Transaction>;
80
+ };
81
+ /**
82
+ * ENS read access for {@link OpaqueClient.resolveRecipient} of `*.eth` names
83
+ * (CSAP §2.9 `com.opaque.meta` text record). Pass an ENS-capable viem `PublicClient`
84
+ * (mainnet or Sepolia — the scan RPC usually is not), or inject a custom `getText`
85
+ * reader (tests, alternative resolvers). Optional: only `*.eth` resolution needs it.
86
+ */
87
+ ens?: {
88
+ client?: PublicClient;
89
+ getText?: (name: string, key: string) => Promise<string | null>;
90
+ };
91
+ /**
92
+ * IPFS access for {@link OpaqueClient.resolveRecipient} of `ipfs://` DID documents.
93
+ * Defaults to public gateways over `globalThis.fetch`; inject `fetch` to mock or to
94
+ * route through a local node / Helia gateway.
95
+ */
96
+ ipfs?: {
97
+ gateways?: readonly string[];
98
+ fetch?: typeof fetch;
99
+ };
100
+ /**
101
+ * ONS (Opaque Name Service, spec/ONS.md) overrides for `*.opq.eth`-style names.
102
+ * Defaults come from `@opaquecash/deployments` for {@link chainId} (testnet parent:
103
+ * `opqtest.eth`). Resolution tries the Solana mirror PDA first (needs {@link solana}),
104
+ * then falls back to the canonical OpaqueNameRegistry over {@link rpcUrl}.
105
+ */
106
+ ons?: {
107
+ /** Parent name in force (lowercase, e.g. `"opq.eth"`). */
108
+ parentName?: string;
109
+ /** Canonical OpaqueNameRegistry address (ENSIP-10 wildcard resolver). */
110
+ registry?: Address;
111
+ /** `ons-mirror` program id (base58) on the configured Solana cluster. */
112
+ mirrorProgram?: string;
113
+ };
114
+ /**
115
+ * SNS read access for `.sol` recipients (CSAP §2.9 TXT record). Defaults to the
116
+ * bundled Records V2 TXT reader over the {@link solana} connection; inject
117
+ * `getRecord` to mock or to use a custom record key/source.
118
+ */
119
+ sns?: {
120
+ getRecord?: (domain: string, key: string) => Promise<string | null>;
121
+ };
122
+ }
123
+ /** Chains the PSR admin API ({@link OpaqueClient.createSchema} etc.) targets. */
124
+ export type PsrChain = OpaqueScanChain;
125
+ /** Future block (Ethereum) / slot (Solana) for a schema or attestation expiry. */
126
+ export interface PsrExpiryInput {
127
+ /** Absolute Ethereum block number or Solana slot. Takes precedence over {@link dateTime}. */
128
+ slotOrBlock?: number;
129
+ /** ISO datetime; converted to a block (~12s/block) or slot (~400ms/slot) at call time. */
130
+ dateTime?: string;
131
+ }
132
+ /** Parameters for {@link OpaqueClient.createSchema}. */
133
+ export interface CreateSchemaParams {
134
+ /** Human-readable schema name (part of the `schemaId` preimage). */
135
+ name: string;
136
+ /** ABI-style string (`"bool passed, u64 score"`) or {@link FieldDef}s — normalized internally. */
137
+ fieldDefinitions: string | FieldDef[];
138
+ /** Whether issued attestations can be revoked (immutable after creation). */
139
+ revocable: boolean;
140
+ /** Optional resolver: EVM address or Solana program pubkey. Omit for none. */
141
+ resolver?: string;
142
+ /** Optional schema expiry. */
143
+ schemaExpiry?: PsrExpiryInput;
144
+ }
145
+ /** Parameters for {@link OpaqueClient.issueAttestation}. */
146
+ export interface IssueAttestationParams {
147
+ /** Target schema id (`0x`-hex bytes32). */
148
+ schemaId: string;
149
+ /** 66-byte meta-address, 20-byte stealth address, or 32-byte `stealth_address_hash` (hex). */
150
+ recipient: string;
151
+ /** Field values keyed by field name — must match the schema's `fieldDefinitions`. */
152
+ fieldValues: Record<string, string>;
153
+ /** Optional attestation expiry. */
154
+ expiration?: PsrExpiryInput;
155
+ /** Optional reference uid (chained credential). */
156
+ refUid?: string;
157
+ /**
158
+ * Publish a discovery announcement after issuance. Defaults to `true` when `recipient` is a
159
+ * meta-address (only then is an ephemeral key available). No-op for raw-hash recipients.
160
+ */
161
+ announce?: boolean;
162
+ }
163
+ /** Result of a PSR write that only returns a transaction id. */
164
+ export interface PsrTxResult {
165
+ /** EVM `0x` tx hash or Solana base58 signature. */
166
+ txHash: string;
167
+ }
168
+ /** Result of {@link OpaqueClient.createSchema}. */
169
+ export interface CreateSchemaResult extends PsrTxResult {
170
+ /** Derived `schemaId` (`0x`-hex bytes32). */
171
+ schemaId: string;
172
+ }
173
+ /** Result of {@link OpaqueClient.issueAttestation}. */
174
+ export interface IssueAttestationResult extends PsrTxResult {
175
+ /** Attestation uid (`0x`-hex bytes32). */
176
+ uid: string;
177
+ /** The 32-byte `stealth_address_hash` the attestation is bound to (`0x`-hex). */
178
+ stealthAddressHash: string;
40
179
  }
180
+ /** Chains the unified {@link OpaqueClient.scan} can read. */
181
+ export type OpaqueScanChain = "ethereum" | "solana";
182
+ /** One owned stealth output from the unified inbox, tagged with its source chain. */
183
+ export interface UnifiedOwnedOutput extends OwnedStealthOutput {
184
+ /** Source chain of this output. */
185
+ chain: OpaqueScanChain;
186
+ /** Wormhole chain id of the source (Ethereum = 2, Solana = 1). */
187
+ chainId: number;
188
+ /**
189
+ * How the announcement was discovered: `"native"` (the chain's own announcer) or `"uab"`
190
+ * (relayed cross-chain over Wormhole and re-emitted by the UABReceiver).
191
+ */
192
+ source: "native" | "uab";
193
+ }
194
+ /** Parameters for {@link OpaqueClient.sendStealthPayment}. */
195
+ export interface SendStealthPaymentParams {
196
+ /** Chain to send on. */
197
+ chain: OpaqueScanChain;
198
+ /**
199
+ * Recipient: a 66-byte meta-address hex (used directly), a Solana base58 pubkey, or an
200
+ * Ethereum `0x` address — the latter two are resolved through the chain's registry.
201
+ */
202
+ recipient: string;
203
+ /** Amount in base units: lamports (Solana) or wei (Ethereum native). */
204
+ amount: bigint;
205
+ /** SPL mint / ERC-20 address; omit for native. Token sends are not yet supported (native only). */
206
+ token?: string;
207
+ /** Publish the discovery announcement (default `true`). */
208
+ announce?: boolean;
209
+ /** Also relay the announcement cross-chain over Wormhole (default `false`). */
210
+ relay?: boolean;
211
+ /** Solana Wormhole nonce (`batch_id`) when `relay` is set. */
212
+ batchId?: number;
213
+ /**
214
+ * Anonymity-set utility (guide §17): decouple send time from announce time. When set,
215
+ * the value transfer is submitted immediately but the announcement is submitted only
216
+ * after this many milliseconds, breaking the timing correlation between the two.
217
+ * The result's `announcePromise` resolves to the announce tx id — await (or attach a
218
+ * handler to) it, or the delayed announce dies with your process.
219
+ */
220
+ delayAnnouncement?: number;
221
+ }
222
+ /** Result of {@link OpaqueClient.sendStealthPayment}. */
223
+ export interface SendStealthPaymentResult {
224
+ chain: OpaqueScanChain;
225
+ /** Transfer tx id (Solana bundles the announce in the same tx unless delayed). */
226
+ txHash: string;
227
+ /** Separate announce tx id (Ethereum submits transfer + announce as two txs). */
228
+ announceTxHash?: string;
229
+ /**
230
+ * Pending announce tx id when `delayAnnouncement` was set: resolves after the delay
231
+ * elapses and the announcement confirms. Undefined for immediate announcements.
232
+ */
233
+ announcePromise?: Promise<string>;
234
+ /** EVM-style 20-byte scanner address the recipient will detect. */
235
+ stealthAddress: Address;
236
+ /** Solana destination account (base58) the funds were sent to. */
237
+ destination?: string;
238
+ /** 33-byte compressed ephemeral public key (hex). */
239
+ ephemeralPublicKey: Hex;
240
+ /** Resolved 66-byte recipient meta-address. */
241
+ metaAddressHex: Hex;
242
+ }
243
+ /** Native balance of one owned stealth output, resolved per chain. */
244
+ export interface OutputBalance {
245
+ chain: OpaqueScanChain;
246
+ /** EVM-style 20-byte scanner address the announcement was matched on. */
247
+ stealthAddress: string;
248
+ /**
249
+ * Account actually holding the funds: the same address on Ethereum, or the derived Solana
250
+ * stealth account (base58) on Solana.
251
+ */
252
+ address: string;
253
+ /** Native balance in base units (wei on Ethereum, lamports on Solana). */
254
+ nativeRaw: bigint;
255
+ }
256
+ /** A cross-chain `announceWithRelay` built for Ethereum (submit `{to,data,value}` via wallet). */
257
+ export interface EvmAnnounceWithRelayResult {
258
+ chain: "ethereum";
259
+ to: Address;
260
+ data: Hex;
261
+ /** Wormhole message fee (wei) to send as `value`. */
262
+ value: bigint;
263
+ chainId: number;
264
+ }
265
+ /** A cross-chain `announce_with_relay` built for Solana (sign with the wallet + extra signers). */
266
+ export interface SolanaAnnounceWithRelayResult {
267
+ chain: "solana";
268
+ instructions: TransactionInstruction[];
269
+ /** Extra signers (the fresh Wormhole message keypair) that must co-sign with the wallet. */
270
+ signers: Keypair[];
271
+ }
272
+ /** Discriminated result of {@link OpaqueClient.buildAnnounceWithRelay}. */
273
+ export type AnnounceWithRelayResult = EvmAnnounceWithRelayResult | SolanaAnnounceWithRelayResult;
41
274
  /**
42
275
  * Result of preparing a stealth send (ephemeral material + announce fields).
43
276
  */
@@ -51,12 +284,25 @@ export interface PrepareStealthSendResult {
51
284
  ephemeralPrivateKey: Uint8Array;
52
285
  /** Metadata bytes for `announce` (view tag byte; extend with WASM for PSR). */
53
286
  metadata: Uint8Array;
287
+ /**
288
+ * Uncompressed (65-byte) stealth public-key point. Needed to derive the Solana destination
289
+ * account (`deriveStealthSolanaAddress`); not required for the EVM `announce`.
290
+ */
291
+ stealthPubKey: Uint8Array;
54
292
  }
55
293
  /**
56
294
  * Result of {@link OpaqueClient.prepareGhostReceive} — same shape as {@link PrepareStealthSendResult},
57
295
  * keyed to your own meta-address for receive-without-prior-announcement flows.
58
296
  */
59
297
  export type PrepareGhostReceiveResult = PrepareStealthSendResult;
298
+ /**
299
+ * One decoy announcement from {@link OpaqueClient.generateDummyAnnouncements}: a valid
300
+ * DKSAP derivation against a throwaway meta-address (included for inspection; its
301
+ * private keys are already gone).
302
+ */
303
+ export type DummyAnnouncement = PrepareStealthSendResult & {
304
+ metaAddressHex: Hex;
305
+ };
60
306
  /**
61
307
  * Calldata-ready request for `StealthAddressAnnouncer.announce` (developer submits via wallet).
62
308
  */
@@ -80,6 +326,15 @@ export interface RegisterMetaAddressTransactionRequest {
80
326
  chainId: number;
81
327
  metaAddressHex: Hex;
82
328
  }
329
+ /** Result of {@link OpaqueClient.registerMetaAddress}. */
330
+ export interface RegisterMetaAddressResult {
331
+ /** Chain the meta-address was registered on. */
332
+ chain: OpaqueScanChain;
333
+ /** EVM `0x` tx hash or Solana base58 signature. */
334
+ txHash: string;
335
+ /** The 66-byte meta-address that was registered. */
336
+ metaAddressHex: Hex;
337
+ }
83
338
  /**
84
339
  * Result of {@link OpaqueClient.resolveRecipientMetaAddress}: registry lookup for a normal EOA.
85
340
  */
@@ -110,11 +365,29 @@ export declare class OpaqueClient {
110
365
  private readonly metaAddressHex;
111
366
  private readonly publicClient;
112
367
  private readonly wasm;
368
+ private evmAdapter?;
369
+ private solanaAdapter?;
370
+ private evmWalletClientCache?;
371
+ private solanaWalletCache?;
113
372
  private constructor();
114
373
  /**
115
374
  * Construct a client: loads WASM, derives keys from `walletSignature`, wires RPC + addresses.
116
375
  */
117
376
  static create(config: OpaqueClientConfig): Promise<OpaqueClient>;
377
+ /**
378
+ * Construct a client from wallet(s) in the {@link UnifiedSigner} shape — the
379
+ * one-adapter entry point for integrators (Phase 2.5). Pass at most one wallet per
380
+ * chain; the FIRST wallet is prompted for the {@link SETUP_MESSAGE} setup signature
381
+ * (HKDF entropy) unless a cached `walletSignature` is supplied. Each wallet is also
382
+ * wired as that chain's write signer (`ethereumProvider`/`ethereumWalletClient`,
383
+ * `solanaWallet`), so PSR writes and sends work without further config.
384
+ */
385
+ static fromWallet(params: Omit<OpaqueClientConfig, "walletSignature" | "ethereumAddress" | "ethereumProvider" | "ethereumWalletClient" | "solanaWallet"> & {
386
+ /** One wallet (or one per chain) in unified shape. */
387
+ wallets: UnifiedSigner | UnifiedSigner[];
388
+ /** Cached setup signature; skips the wallet prompt when present. */
389
+ walletSignature?: Hex;
390
+ }): Promise<OpaqueClient>;
118
391
  /** Chain id from configuration. */
119
392
  getChainId(): number;
120
393
  /** Connected Ethereum address (from config). */
@@ -137,14 +410,129 @@ export declare class OpaqueClient {
137
410
  * @param recipientAddress - Normal Ethereum address of the intended recipient.
138
411
  */
139
412
  resolveRecipientMetaAddress(recipientAddress: Address): Promise<ResolveRecipientMetaResult>;
413
+ /**
414
+ * Resolve ANY supported recipient identity to its 66-byte meta-address (CSAP §2.9):
415
+ *
416
+ * | Input | Path |
417
+ * |-------|------|
418
+ * | 66-byte meta-address (optionally `st:opq:`-prefixed) | validated and passed through |
419
+ * | `0x…` 20-byte EVM address | ERC-6538 `StealthMetaAddressRegistry` |
420
+ * | Solana base58 pubkey | `stealth-registry` PDA (needs {@link OpaqueClientConfig.solana}) |
421
+ * | `ipfs://…` / bare CID | DID document fetch via gateways (configure {@link OpaqueClientConfig.ipfs}) |
422
+ * | ONS name (`alice.opq.eth`) | Solana mirror PDA first, canonical OpaqueNameRegistry fallback (spec/ONS.md) |
423
+ * | other `*.eth` | ENS `com.opaque.meta` text record (needs {@link OpaqueClientConfig.ens}) |
424
+ * | `*.sol` | SNS Records V2 TXT record (needs {@link OpaqueClientConfig.solana} or `sns.getRecord`) |
425
+ *
426
+ * Every path point-validates both 33-byte halves before returning. Throws with a
427
+ * path-specific message when the identity is unregistered, unset, or malformed.
428
+ */
429
+ resolveRecipient(input: string): Promise<ResolvedRecipient>;
430
+ /**
431
+ * Resolve an Opaque Name Service name (`alice.opq.eth`; `alice.opqtest.eth` on
432
+ * testnet) to its meta-address (spec/ONS.md §7). Tries the Solana mirror PDA first
433
+ * (one account read, no Ethereum RPC — needs {@link OpaqueClientConfig.solana});
434
+ * falls back to the canonical OpaqueNameRegistry (ENSIP-10) over the scan RPC.
435
+ * Both paths point-validate the 33-byte halves. Mirror records lag the canonical
436
+ * record by Wormhole latency (eventually consistent, canonical-chain-wins).
437
+ */
438
+ resolveOpaqueMetaAddress(name: string): Promise<Hex>;
439
+ /** ONS resolution: mirror-PDA-first, canonical-registry fallback. */
440
+ private resolveOnsName;
441
+ /** The ONS parent name in force (config override, else bundled deployment), if any. */
442
+ private onsParentName;
443
+ /** The `.sol` record reader: injected, else the bundled Records V2 TXT reader. */
444
+ private snsGetRecord;
445
+ /** Build the {@link ResolveTransports} from config (ENS reader + IPFS gateways). */
446
+ private resolveTransports;
140
447
  /**
141
448
  * Encode `registerKeys` for the user's meta-address (they submit with `ethereumAddress`).
142
449
  */
143
450
  buildRegisterMetaAddressTransaction(): RegisterMetaAddressTransactionRequest;
451
+ /**
452
+ * Register THIS wallet's 66-byte meta-address on-chain so others can resolve it, dispatching on
453
+ * `chain`. Submits the transaction with the configured signer (`ethereumWalletClient` /
454
+ * `ethereumProvider` for Ethereum, `solanaWallet` for Solana) and returns the tx id. For a
455
+ * calldata-only request you submit yourself, see {@link buildRegisterMetaAddressTransaction}
456
+ * (Ethereum) or `SolanaAdapter.buildRegisterKeysInstruction`.
457
+ */
458
+ registerMetaAddress(chain: OpaqueScanChain): Promise<RegisterMetaAddressResult>;
459
+ /**
460
+ * Whether THIS wallet's meta-address is already registered on `chain` (Ethereum reads its
461
+ * configured `ethereumAddress`; Solana reads the `solanaWallet` pubkey).
462
+ */
463
+ isMetaAddressRegistered(chain: OpaqueScanChain): Promise<boolean>;
464
+ /**
465
+ * Register `label`.<parent> for THIS wallet's meta-address on the canonical
466
+ * OpaqueNameRegistry (Ethereum; spec/ONS.md §4.1). Immediately authoritative;
467
+ * the Solana mirror follows after Wormhole relay. Submits with the configured
468
+ * Ethereum signer and returns the tx hash.
469
+ */
470
+ registerOpaqueName(label: string): Promise<Hex>;
471
+ /**
472
+ * Claim `label`.<parent> from Solana (spec/ONS.md §4.2). Creates a PROVISIONAL
473
+ * claim and publishes it to the canonical registry via Wormhole; it becomes
474
+ * authoritative only when the registry confirms (mirror record appears), and it
475
+ * loses to any concurrent direct Ethereum registration. Track with
476
+ * {@link getOpaqueNameStatus}; surface `pending` in UI (never as owned).
477
+ */
478
+ claimOpaqueName(label: string): Promise<{
479
+ signature: string;
480
+ name: string;
481
+ }>;
482
+ /**
483
+ * Reconciliation state of an ONS name's Solana-originated claim
484
+ * (`none`/`pending`/`confirmed`/`lost`/`expired`; spec/ONS.md §6), plus the
485
+ * mirror record when one exists. Two Solana account reads.
486
+ */
487
+ getOpaqueNameStatus(name: string): Promise<OnsClaimStatus>;
488
+ /**
489
+ * Close a finished provisional claim (confirmed / lost / expired) and refund its
490
+ * rent to the claimer. Permissionless; submits with the configured Solana wallet.
491
+ */
492
+ reconcileOpaqueName(name: string): Promise<string>;
493
+ /** The ONS parent name in force, or throw with setup guidance. */
494
+ private requireOnsParentName;
495
+ private requireOnsRegistry;
496
+ private onsMirrorProgram;
497
+ private onsRegistrationProgram;
498
+ /** Split this wallet's meta-address (CSAP V‖S) into its 33-byte halves. */
499
+ private ownMetaAddressHalves;
144
500
  /**
145
501
  * Derive a one-time stealth address for sending to a recipient meta-address.
146
502
  */
147
503
  prepareStealthSend(recipientMetaAddressHex: Hex): PrepareStealthSendResult;
504
+ /**
505
+ * High-level send: resolve the recipient, derive a one-time stealth destination, transfer the
506
+ * native asset, and publish the discovery announcement — in one call, dispatching on `chain`.
507
+ *
508
+ * Solana bundles the transfer and `announce` (or `announce_with_relay` when `relay` is set) into a
509
+ * single transaction and returns its signature. Ethereum submits the value transfer first, then
510
+ * the announce, returning both tx hashes. Token (SPL/ERC-20) sends are not yet supported.
511
+ * Requires the chain's signer (`solanaWallet` / `ethereumWalletClient` or `ethereumProvider`).
512
+ */
513
+ sendStealthPayment(params: SendStealthPaymentParams): Promise<SendStealthPaymentResult>;
514
+ /**
515
+ * Resolve a {@link SendStealthPaymentParams.recipient} to a 66-byte meta-address.
516
+ * Delegates to {@link resolveRecipient}, so sends accept every supported identity
517
+ * form (meta-address, registry address/pubkey, `ipfs://` DID, `*.eth`) on any chain —
518
+ * meta-addresses are chain-neutral.
519
+ */
520
+ private resolveSendRecipientMeta;
521
+ /**
522
+ * Anonymity-set utility (guide §17): mint `n` decoy announcements. Each one is a fully
523
+ * valid DKSAP announcement to a freshly generated THROWAWAY meta-address whose private
524
+ * keys are discarded — on-chain it is indistinguishable from a real payment
525
+ * announcement (valid curve points, correctly derived view tag), but nobody will ever
526
+ * match or spend it. Submit them (e.g. via {@link buildDummyAnnouncementTransactions})
527
+ * interleaved with real sends to grow every recipient's anonymity set.
528
+ */
529
+ generateDummyAnnouncements(n: number): DummyAnnouncement[];
530
+ /**
531
+ * Convenience over {@link generateDummyAnnouncements}: `n` ready-to-submit `announce`
532
+ * calldata requests for this chain's announcer. Broadcast them from any account —
533
+ * announcements carry no value and any caller may announce.
534
+ */
535
+ buildDummyAnnouncementTransactions(n: number): AnnounceTransactionRequest[];
148
536
  /**
149
537
  * Manual “ghost” receive: derive a one-time stealth address for **this** wallet’s meta-address
150
538
  * without any on-chain announcement yet. Cryptographically this is {@link prepareStealthSend}
@@ -163,10 +551,113 @@ export declare class OpaqueClient {
163
551
  * Build calldata for `announce` so the developer can prompt the user to broadcast.
164
552
  */
165
553
  buildAnnounceTransactionRequest(send: PrepareStealthSendResult): AnnounceTransactionRequest;
554
+ /** Resolve UAB addresses for this chain (config override takes precedence over the known deployment). */
555
+ private uabAddresses;
556
+ /**
557
+ * Build a `{to,data,value}` request for a CROSS-CHAIN announce (`announceWithRelay`): it emits the
558
+ * local announcement AND publishes the 96-byte payload through Wormhole. `value` is the Wormhole
559
+ * message fee. Pass the same {@link PrepareStealthSendResult} you'd use for a native announce.
560
+ */
561
+ buildAnnounceWithRelayRequest(send: PrepareStealthSendResult, opts?: {
562
+ consistencyLevel?: number;
563
+ }): Promise<AnnounceWithRelayRequest & {
564
+ chainId: number;
565
+ }>;
566
+ /**
567
+ * Build a CROSS-CHAIN announce for either chain, dispatching on `chain`. Emits the local
568
+ * announcement AND relays the 96-byte payload over Wormhole. Ethereum returns a `{to,data,value}`
569
+ * request (`value` is the Wormhole fee); Solana returns `instructions` + extra `signers` (the
570
+ * fresh Wormhole message keypair) — both must co-sign with the wallet. Pass the same
571
+ * {@link PrepareStealthSendResult} you'd use for a native announce.
572
+ *
573
+ * EVM honours `consistencyLevel`; Solana honours `batchId` (Wormhole nonce) and `wormholeFee`
574
+ * (auto-fetched from the core bridge when omitted; 0 on devnet).
575
+ */
576
+ buildAnnounceWithRelay(chain: OpaqueScanChain, send: PrepareStealthSendResult, opts?: {
577
+ consistencyLevel?: number;
578
+ batchId?: number;
579
+ wormholeFee?: bigint;
580
+ }): Promise<AnnounceWithRelayResult>;
581
+ /**
582
+ * Read inbound CROSS-CHAIN announcements (from the UABReceiver) as indexer-shaped rows, ready to
583
+ * pass into {@link filterOwnedAnnouncements} alongside native rows.
584
+ */
585
+ fetchCrossChainAnnouncements(opts?: {
586
+ fromBlock?: bigint;
587
+ toBlock?: bigint | "latest";
588
+ }): Promise<UabIndexerAnnouncement[]>;
589
+ /** Discover stealth outputs owned by this user that arrived via the cross-chain UAB. */
590
+ scanCrossChain(opts?: {
591
+ fromBlock?: bigint;
592
+ toBlock?: bigint | "latest";
593
+ }): Promise<OwnedStealthOutput[]>;
166
594
  /**
167
595
  * Filter indexer announcements down to outputs owned by this user (WASM scan).
168
596
  */
169
597
  filterOwnedAnnouncements(rows: IndexerAnnouncement[]): Promise<OwnedStealthOutput[]>;
598
+ /**
599
+ * Scan one or more chains for stealth outputs owned by this wallet and return a single,
600
+ * merged inbox. Each chain's native announcements are fetched through its {@link ChainAdapter}
601
+ * and run through the same WASM view-tag + DKSAP filter ({@link filterOwnedAnnouncements}), so
602
+ * detection is identical across chains. Outputs are tagged with their source `chain` / `chainId`.
603
+ *
604
+ * `"ethereum"` reuses this client's viem client + configured announcer/registry. `"solana"`
605
+ * requires {@link OpaqueClientConfig.solana} (connection / rpcUrl / cluster; defaults to devnet).
606
+ * The viewing/spending keys are chain-neutral, so one wallet's inbox spans both chains.
607
+ */
608
+ scan(opts: {
609
+ chains: OpaqueScanChain[];
610
+ /** Lower-bound cursor: EVM block number (Solana scans the most recent signatures). */
611
+ fromBlock?: bigint;
612
+ /** Upper-bound EVM block; omit for the chain tip. */
613
+ toBlock?: bigint;
614
+ /** Max Solana signatures to scan (adapter default when omitted). */
615
+ solanaLimit?: number;
616
+ /**
617
+ * Also merge cross-chain (UAB) announcements, tagged `source: "uab"`: on Ethereum, events
618
+ * re-emitted by the EVM UABReceiver; on Solana, `CrossChainAnnouncement` events from the
619
+ * `uab-receiver` program (merged by the adapter). Defaults to `true` wherever a UAB
620
+ * deployment is configured; set `false` to skip, or `true` to force on the EVM side
621
+ * (throws if UAB is unconfigured there).
622
+ */
623
+ includeCrossChain?: boolean;
624
+ }): Promise<UnifiedOwnedOutput[]>;
625
+ /**
626
+ * Fetch ALL native announcements on `chain` as indexer-shaped rows — unfiltered, with their
627
+ * full on-chain `metadata`. This is the raw input for the metadata-aware scanners:
628
+ * {@link discoverTraits} / {@link getReputationTraitsFromAnnouncements} (PSR attestation
629
+ * markers) and {@link filterOwnedAnnouncements}. Unlike {@link scan}, nothing is dropped, so
630
+ * callers can decode announcement metadata that ownership filtering would discard.
631
+ *
632
+ * Note: cross-chain (UAB) announcements are NOT included — the 96-byte Wormhole payload only
633
+ * carries a 24-byte metadata tail, which cannot hold the 130-byte V2 attestation metadata.
634
+ * For trait discovery, fetch rows natively on each chain instead.
635
+ */
636
+ fetchAnnouncementRows(chain: OpaqueScanChain, opts?: {
637
+ /** Lower-bound cursor: EVM block number (Solana scans the most recent signatures). */
638
+ fromBlock?: bigint;
639
+ /** Upper-bound EVM block; omit for the chain tip. */
640
+ toBlock?: bigint;
641
+ /** Max Solana signatures to scan (adapter default when omitted). */
642
+ solanaLimit?: number;
643
+ }): Promise<IndexerAnnouncement[]>;
644
+ /** Lazily build and cache the {@link ChainAdapter} for a chain. */
645
+ private getAdapter;
646
+ /** Lazily build and cache the concrete {@link SolanaAdapter}. */
647
+ private getSolanaAdapter;
648
+ /**
649
+ * Sweep the full native balance of an owned stealth output to `destination`, signed by the
650
+ * reconstructed one-time key (the on-chain `from` is the stealth address itself). Works for
651
+ * Ethereum (ETH) and Solana (SOL); `"solana"` requires {@link OpaqueClientConfig.solana}.
652
+ */
653
+ sweep(params: {
654
+ output: Pick<OwnedStealthOutput, "ephemeralPublicKey">;
655
+ chain: OpaqueScanChain;
656
+ destination: string;
657
+ }): Promise<{
658
+ chain: OpaqueScanChain;
659
+ tx: string;
660
+ }>;
170
661
  /**
171
662
  * Reconstruct the 32-byte secp256k1 private key that controls `output`’s one-time stealth address.
172
663
  * Uses the same WASM path as the on-chain scanner (`reconstruct_signing_key_wasm`).
@@ -186,6 +677,13 @@ export declare class OpaqueClient {
186
677
  * Owned outputs + balances summed per tracked token (uses `rpcUrl` from config).
187
678
  */
188
679
  getBalancesFromAnnouncements(rows: IndexerAnnouncement[]): Promise<TokenBalanceSummary[]>;
680
+ /**
681
+ * Native balance per owned stealth output across chains. Ethereum reads the stealth address
682
+ * directly; Solana reconstructs the one-time key (WASM), derives the Solana stealth account, and
683
+ * reads its lamports. Pass the {@link UnifiedOwnedOutput}s from {@link scan}. SPL/ERC-20 token
684
+ * sums for the EVM tracked-token set live in {@link getBalancesFromAnnouncements}.
685
+ */
686
+ getBalancesForOutputs(outputs: UnifiedOwnedOutput[]): Promise<OutputBalance[]>;
189
687
  /**
190
688
  * PSR: map owned attestation markers to {@link DiscoveredTrait} list.
191
689
  */
@@ -208,7 +706,58 @@ export declare class OpaqueClient {
208
706
  */
209
707
  buildAssignReputationTransaction(recipientMetaAddressHex: Hex, attestationId: bigint): AnnounceTransactionRequest;
210
708
  /**
211
- * JSON array string for {@link generateReputationProof} when passing `attestationsJson` (WASM Merkle witness).
709
+ * Register a new schema (attestation class + issuance rules). Returns the tx id and derived
710
+ * `schemaId`. `fieldDefinitions` accepts an ABI string or {@link FieldDef}s.
711
+ */
712
+ createSchema(chain: PsrChain, params: CreateSchemaParams): Promise<CreateSchemaResult>;
713
+ /** Schemas where this client's wallet is the authority OR an authorized delegate. */
714
+ getMySchemas(chain: PsrChain): Promise<SchemaV2[]>;
715
+ /** Authority-only, irreversible: deprecate a schema (blocks new attestations). */
716
+ deprecateSchema(chain: PsrChain, schemaId: string): Promise<PsrTxResult>;
717
+ /** Authority-only: authorize `delegate` to issue under `schemaId`. */
718
+ addSchemaDelegate(chain: PsrChain, schemaId: string, delegate: string): Promise<PsrTxResult>;
719
+ /** Authority-only: revoke a delegate's issuance rights under `schemaId`. */
720
+ removeSchemaDelegate(chain: PsrChain, schemaId: string, delegate: string): Promise<PsrTxResult>;
721
+ /** Attestations issued by this client's wallet. */
722
+ getMyIssuedAttestations(chain: PsrChain): Promise<AttestationV2[]>;
723
+ /**
724
+ * Issue a schema-bound attestation against a stealth identity. Resolves the recipient to a
725
+ * `stealth_address_hash`, encodes the field values per the schema, submits `attest`, and
726
+ * (when the recipient is a meta-address and `announce` is not `false`) publishes a discovery
727
+ * announcement so the recipient's scanner can find the trait. Verifies the wallet is an
728
+ * authorized issuer first.
729
+ */
730
+ issueAttestation(chain: PsrChain, params: IssueAttestationParams): Promise<IssueAttestationResult>;
731
+ /** EIP-1193 provider or a clear error. */
732
+ private requireEthereumProvider;
733
+ /** Viem chain for the configured chain id (bundled Sepolia, else a minimal definition). */
734
+ private viemChain;
735
+ /** Lazily build the signing wallet client for Ethereum PSR writes. */
736
+ private evmWalletClient;
737
+ private evmWriteClients;
738
+ /** Solana signer (normalized) or a clear error. */
739
+ private requireSolanaWallet;
740
+ /**
741
+ * Sign + send + confirm a Solana transaction built from `ixs`. Any `extraSigners` (e.g. the
742
+ * Wormhole message keypair for `announce_with_relay`) partial-sign before the wallet signs as
743
+ * fee payer.
744
+ */
745
+ private sendSolanaTx;
746
+ /**
747
+ * Resolve a recipient to a 32-byte `stealth_address_hash` (and, for a meta-address, the
748
+ * ephemeral material needed to announce). Matches the frontends: 66-byte meta-address → DKSAP →
749
+ * `keccak256(stealthAddress)`; 20-byte stealth address → `keccak256(address)`; 32-byte → as-is.
750
+ */
751
+ private resolveStealthAddressHash;
752
+ /** Resolve a {@link PsrExpiryInput} to an absolute Ethereum block (0 = no expiry). */
753
+ private resolveEvmExpiryBlock;
754
+ /** Resolve a {@link PsrExpiryInput} to an absolute Solana slot (0 = no expiry). */
755
+ private resolveSolanaExpirySlot;
756
+ /** Find a Solana schema by `schemaId` (PSR schemas have no id-indexed PDA across authorities). */
757
+ private fetchSolanaSchemaById;
758
+ /**
759
+ * JSON array string in the Rust scanner's announcement format (general scanner interop;
760
+ * no longer consumed by {@link generateReputationProof}, which builds V2 witnesses directly).
212
761
  */
213
762
  announcementsJsonForReputationWitness(rows: IndexerAnnouncement[]): string;
214
763
  /**
@@ -216,15 +765,20 @@ export declare class OpaqueClient {
216
765
  */
217
766
  getStealthSignerPrivateKeyForReputationTrait(trait: DiscoveredTrait): Uint8Array;
218
767
  /**
219
- * Groth16 proof bundle for `OpaqueReputationVerifier` (requires `snarkjs`).
220
- * When `artifacts` is omitted, wasm/zkey are loaded from the default hosted paths on opaque.cash
221
- * (same as the Opaque frontend `/circuits/...` assets).
768
+ * V2 Groth16 proof bundle for the reputation verifiers (requires `snarkjs`).
769
+ * When `artifacts` is omitted, the V2 wasm/zkey are loaded from the default hosted paths on
770
+ * opaque.cash (same as the Opaque frontend `/circuits/v2/...` assets).
771
+ *
772
+ * Public signals: `[merkle_root, attestation_id, external_nullifier, nullifier_hash]`;
773
+ * `ProofData.nullifier` carries `nullifier_hash`.
222
774
  */
223
775
  generateReputationProof(params: {
224
776
  trait: DiscoveredTrait;
225
777
  stealthPrivKeyBytes: Uint8Array;
226
778
  externalNullifier: string;
227
- attestationsJson?: string;
779
+ issuerPkX?: string | bigint;
780
+ traitDataHash?: string | bigint;
781
+ nonce?: string | bigint;
228
782
  artifacts?: ArtifactPaths;
229
783
  onProgress?: ProofProgressCallback;
230
784
  }): Promise<ProofData>;
@@ -242,8 +796,13 @@ export declare class OpaqueClient {
242
796
  verifyReputationProofView(args: VerifyReputationArgs): Promise<boolean>;
243
797
  /** Simulate `verifyReputation` for gas / revert checks. */
244
798
  simulateReputationVerification<TTransport extends Transport, TChain extends Chain, TAccount extends Account | undefined>(wallet: WalletClient<TTransport, TChain, TAccount>, args: VerifyReputationArgs): Promise<void>;
245
- /** Broadcast `verifyReputation` (consumes nullifier when successful). */
246
- submitReputationVerification<TTransport extends Transport, TChain extends Chain, TAccount extends Account | undefined>(wallet: WalletClient<TTransport, TChain, TAccount>, args: VerifyReputationArgs): Promise<Hex>;
799
+ /**
800
+ * Broadcast a reputation proof to the verifier, dispatching on `chain` (consumes the nullifier on
801
+ * success). Uses the configured signer for each chain — `ethereumWalletClient` / `ethereumProvider`
802
+ * for Ethereum, `solanaWallet` for Solana. The same {@link VerifyReputationArgs} feeds both:
803
+ * `proofData` (from {@link generateReputationProof}), `merkleRoot`, and `externalNullifier`.
804
+ */
805
+ submitReputationVerification(chain: OpaqueScanChain, args: VerifyReputationArgs): Promise<PsrTxResult>;
247
806
  private getReputationVerifierAddress;
248
807
  /**
249
808
  * Deterministic scope string for reputation actions (`chainId:module:actionId`).
@@ -264,4 +823,11 @@ export declare class OpaqueClient {
264
823
  */
265
824
  static chainDeployment(chainId: number): OpaqueChainDeployment | undefined;
266
825
  }
826
+ /**
827
+ * Map a chain-neutral {@link Announcement} (from any {@link ChainAdapter}) into the
828
+ * {@link IndexerAnnouncement} row shape consumed by {@link OpaqueClient.filterOwnedAnnouncements}.
829
+ * `txHash` passes through verbatim (an EVM `0x` hash or a Solana base58 signature); `cursor`
830
+ * (EVM block / Solana slot) becomes `blockNumber`.
831
+ */
832
+ export declare function announcementToIndexerRow(a: Announcement): IndexerAnnouncement;
267
833
  //# sourceMappingURL=client.d.ts.map