import { PublicKey, type Connection } from "@solana/web3.js"; import type { NoteCipherInput } from "../compactNote.js"; import type { QuotedInput } from "../relayer/types.js"; import type { VeiloRelayerClient } from "../relayer/client.js"; /** * A note the caller owns and wants to spend. * * There is deliberately no `leafIndex`: the nullifier depends on it, and only a * relayer quote is authoritative for it. A guessed index yields a proof the * chain rejects, after the proving time has already been spent. */ export interface ClientProvingNote { /** Base units. */ amount: bigint; blinding: bigint; /** Note spending key. Used only inside this process; never sent. */ privateKey: bigint; /** 64-hex commitment you expect. Recomputed from the fields and checked when given. */ commitment?: string; } /** A note this SDK created as a spend output. Can be passed straight back in as an input. */ export interface ClientProvingOutputNote extends ClientProvingNote { commitment: string; mintAddress: string; /** * On-chain tree the note was appended to. Null until landing when the relayer * picks the tree at submit, as it does for a swap's destination note. */ treeId: number | null; /** Known only once the spend has landed and the relayer reported it. */ leafIndex?: number; } /** Who can decrypt an output note's on-chain cipher. Exactly one key. */ export type NoteCipherRecipient = Pick | Pick; /** * JSON-safe record of one output note, written before any proof is generated. * * Carries the blinding but not the spending key: the owner key is the caller's * own, identified here by `ownerPublicKey`. */ export interface RecoverableOutputNote { commitment: string; amount: string; /** 64 hex. */ blinding: string; /** Decimal Poseidon public key of the owning note key. */ ownerPublicKey: string; mintAddress: string; treeId: number | null; } /** What a spend will do, persisted before proving so a crash can never lose a note. */ export interface ClientSpendRecovery { quoteId: string; mintAddress: string; consumedCommitments: string[]; /** * Every nullifier the transaction publishes. Pass these to * `resolveClientSpend` to learn whether it landed. */ inputNullifiers: string[]; outputs: RecoverableOutputNote[]; } export type ClientProvingErrorCode = "INVALID_PARAMS" | "INDEX_TIMEOUT" | "QUOTE_MISMATCH" | "ROOT_NOT_ON_CHAIN" | "FEE_TOO_HIGH" | "OUTPUT_TOO_LOW" | "RECOVERY_NOT_PERSISTED"; /** * Raised only before anything has been sent to the relayer for submission, so * it always means "nothing was spent". Outcomes after submit are results, not * exceptions. */ export declare class ClientProvingError extends Error { readonly code: ClientProvingErrorCode; readonly cause?: unknown; constructor(code: ClientProvingErrorCode, message: string, cause?: unknown); } /** A relayer answer, flattened for results. `httpStatus` 0 means no response arrived. */ export interface ClientProvingFailure { code: string; message: string; httpStatus: number; } export type ClientProvingRelayer = Pick; /** @internal Seams for tests. Not exported from the package. */ export interface ClientProvingDeps { relayer: ClientProvingRelayer; /** Root history of one on-chain note tree, as lowercase hex. */ fetchRootHistory(connection: Connection, mintAddress: PublicKey, treeId: number): Promise; sleep(ms: number): Promise; } /** @internal */ export declare function defaultClientProvingDeps(): ClientProvingDeps; export declare function toHex(bytes: Uint8Array): string; export declare function fromHex32(value: unknown, field: string): Uint8Array; export declare function normalizeHex32(value: unknown, field: string): string; export declare function scalarHex(value: bigint): string; /** Parse a relayer amount: unsigned decimal only. */ export declare function parseQuotedAmount(value: unknown, field: string): bigint; export declare function expectEqual(actual: unknown, expected: unknown, field: string): void; export declare function assertTreeId(value: unknown, field: string): number; export interface ResolvedNote { note: ClientProvingNote; pubkey: bigint; commitment: Uint8Array; commitmentHex: string; } /** Recompute a note's commitment from its secrets, and check it against the caller's. */ export declare function resolveNote(note: ClientProvingNote, mintAddress: PublicKey, field: string): ResolvedNote; export declare function resolveNoteSet(notes: ClientProvingNote[], mintAddress: PublicKey): ResolvedNote[]; export declare function toRecoverable(note: ClientProvingOutputNote, ownerPublicKey: bigint): RecoverableOutputNote; export declare function rootFromPath(leaf: Uint8Array, pathElements: Uint8Array[], pathIndices: number[]): Uint8Array; /** * Check a quoted Merkle path against the note it claims to prove. * * The path must reproduce the quoted root from the commitment the device * computed itself, and its direction bits must spell the quoted leaf index — * the index the nullifier is derived from. */ export declare function verifyQuotedInput(quoted: QuotedInput, note: ResolvedNote, rootHex: string, field: string): { leafIndex: number; pathElements: Uint8Array[]; }; export declare function assertRootOnChain(deps: ClientProvingDeps, connection: Connection, mintAddress: PublicKey, treeId: number, rootHex: string): Promise; export declare const DEFAULT_INDEX_TIMEOUT_MS = 90000; /** The proof and quote endpoints take the native mint as "no mint". */ export declare function relayerMintParam(mintAddress: PublicKey): string | undefined; /** * Wait until the relayer has indexed every commitment. * * Its tree trails the chain by a few seconds after any append. Quoting earlier * fails with COMMITMENT_NOT_INDEXED, and burns a single-use quote to learn it. */ export declare function waitForIndexed(deps: ClientProvingDeps, commitments: string[], mintAddress: PublicKey, timeoutMs?: number): Promise; export type SubmitErrorOutcome = { kind: "unconfirmed"; txSignature: string; } | { kind: "rejected"; failure: ClientProvingFailure; } | { kind: "unknown"; failure: ClientProvingFailure; }; /** * Decide what a thrown submit call means for the user's money. * * Only an answer the relayer itself wrote can say "nothing moved". A dropped * connection, a timeout, or a proxy's error page says nothing about whether the * transaction was sent — the relayer keeps working after the client is gone — * so each of those is `unknown`, never `rejected`. * * @param serverErrorIsDefinitive whether a relayer-written 500 guarantees no * send. True for withdraw, whose 500s all come from before landing. */ export declare function classifySubmitError(error: unknown, serverErrorIsDefinitive: boolean): SubmitErrorOutcome; export declare function failureFrom(error: unknown): ClientProvingFailure; export declare const DEFAULT_CLAIMANT: PublicKey;