/** * Gasless escrow deposits: EIP-3009 `ReceiveWithAuthorization` signed by the * app key, relayed by the gateway (`POST /v1/escrow/deposit-with-authorization`). * * The app key signs one typed-data message over the TOKEN's EIP-712 domain, * with `to` = the escrow contract. The gateway relayer broadcasts * `depositTokenWithAuthorization` and pays the gas, so an app wallet that * holds only USDC.e can fund its escrow. The beneficiary is committed inside * the signed nonce (`keccak256(abi.encode(account, salt))`), which the escrow * recomputes on chain, so neither the gateway nor anyone relaying the body can * redirect the deposit. * * Mirrors the Vana Account builder UI (unity-surfaces * apps/account/src/lib/developers/escrow-authorization.ts). */ import { type Hex } from "viem"; import type { LocalAccount } from "viem/accounts"; export interface Erc3009Domain { name: string; version: string; chainId: number; verifyingContract: Hex; } export declare const RECEIVE_WITH_AUTHORIZATION_TYPES: { readonly ReceiveWithAuthorization: readonly [{ readonly name: "from"; readonly type: "address"; }, { readonly name: "to"; readonly type: "address"; }, { readonly name: "value"; readonly type: "uint256"; }, { readonly name: "validAfter"; readonly type: "uint256"; }, { readonly name: "validBefore"; readonly type: "uint256"; }, { readonly name: "nonce"; readonly type: "bytes32"; }]; }; /** Long enough to relay, short enough to bound a leaked authorization. */ export declare const AUTHORIZATION_TTL_SEC = 3600; /** The body `POST /v1/escrow/deposit-with-authorization` takes; all strings. */ export interface DepositAuthorization { account: Hex; from: Hex; token: Hex; value: string; validAfter: string; validBefore: string; salt: Hex; signature: Hex; } /** * The escrow recomputes the EIP-3009 nonce on chain as * keccak256(abi.encode(account, salt)). The signature must cover exactly this * nonce; it is what pins the beneficiary. */ export declare function deriveEscrowAuthorizationNonce(account: Hex, salt: Hex): Hex; /** Fresh 32 random bytes. An authorization is single-use per (account, salt). */ export declare function generateAuthorizationSalt(): Hex; export declare function computeErc3009DomainSeparator(domain: Erc3009Domain): Hex; /** * The gateway wants 65-byte r||s||v with v in {27, 28} and rejects EIP-2098 * compact signatures. viem's local signer already returns v in {27, 28}; a * {0, 1} v is normalised rather than trusted to be rejected downstream. */ export declare function normalizeAuthorizationSignature(value: string): Hex | null; declare const ERC3009_DOMAIN_ABI: readonly [{ readonly type: "function"; readonly name: "name"; readonly stateMutability: "view"; readonly inputs: readonly []; readonly outputs: readonly [{ readonly type: "string"; }]; }, { readonly type: "function"; readonly name: "version"; readonly stateMutability: "view"; readonly inputs: readonly []; readonly outputs: readonly [{ readonly type: "string"; }]; }, { readonly type: "function"; readonly name: "DOMAIN_SEPARATOR"; readonly stateMutability: "view"; readonly inputs: readonly []; readonly outputs: readonly [{ readonly type: "bytes32"; }]; }]; export interface Erc3009DomainReader { readContract(args: { abi: typeof ERC3009_DOMAIN_ABI; address: Hex; functionName: "name" | "version" | "DOMAIN_SEPARATOR"; }): Promise; } /** * Resolve the token's EIP-712 domain from chain state and prove it by * recomputing the token's own DOMAIN_SEPARATOR(). A wrong name or version * signs perfectly well and is rejected by the token (gateway 409), so a domain * that does not reproduce the separator is an error, never a guess. */ export declare function resolveErc3009Domain(client: Erc3009DomainReader, input: { chainId: number; token: Hex; }): Promise; /** * Sign a deposit of `value` base units of `token` into `account`'s escrow, * paid from `signer`'s own token balance. */ export declare function signDepositAuthorization(input: { signer: LocalAccount; domain: Erc3009Domain; escrowContract: Hex; account: Hex; value: bigint; now?: () => number; salt?: Hex; }): Promise; /** A gateway answer that is not 202, with its meaning kept apart. */ export declare class DepositAuthorizationError extends Error { readonly status: number; /** 409: permanent for this body; sign a fresh authorization. */ readonly rejected: boolean; /** 502: nothing was broadcast; the same body is safe to resend. */ readonly transient: boolean; constructor(message: string, status: number, /** 409: permanent for this body; sign a fresh authorization. */ rejected: boolean, /** 502: nothing was broadcast; the same body is safe to resend. */ transient: boolean); } /** * Hand a signed authorization to the gateway relayer. 202 means the deposit * was broadcast and the gateway already holds its deposit row: there is no * second "register the tx hash" step, unlike a self-broadcast deposit. */ export declare function submitDepositAuthorization(gatewayUrl: string, authorization: DepositAuthorization, fetchImpl?: typeof fetch): Promise<{ txHash: Hex; status: string; }>; export type DepositSettlement = "finalized" | "failed" | "pending"; /** * Wait for the gateway to credit a relayed deposit. The gateway only moves a * relayed deposit from pending to available when something asks it to * reconcile (`POST /v1/escrow/balance/sync`); a plain balance read keeps * showing it pending. The builder UI polls the same way. */ export declare function awaitDepositSettlement(gatewayUrl: string, account: Hex, txHash: Hex, options?: { fetchImpl?: typeof fetch; timeoutMs?: number; intervalMs?: number; sleep?: (ms: number) => Promise; now?: () => number; }): Promise; export {}; //# sourceMappingURL=escrow-authorization.d.ts.map