Skip to main content

Registry and identity

getMetaAddressHex()

Returns the 98-byte stealth meta-address (V||S||S_ed) derived from the wallet signature.

resolveRecipient(input)

Resolve ANY supported recipient identity to a validated meta-address. This is the one entry point for “who am I paying”: it accepts the same inputs you can pass to sendStealthPayment({ recipient }).
Every path validates that both 33-byte halves of the result are valid compressed secp256k1 points before returning. Unregistered, unset, or malformed identities throw with a path-specific message. ONS names (ONS.md). Subnames of the Opaque Name Service parent resolve mirror-first: with solana configured, the SDK derives the mirror PDA from keccak256(name) and reads one Solana account, no Ethereum RPC. Without solana (or when the mirror has no record yet), it falls back to the canonical OpaqueNameRegistry wildcard resolver over your EVM RPC. The mirror lags the canonical record by Wormhole latency (eventually consistent, canonical-chain-wins). Defaults (parent name, registry, mirror program) ship in @opaquecash/deployments; override with ons: { parentName, registry, mirrorProgram }.
.sol names. With solana configured, the SDK reads the domain’s Records V2 TXT record via the bundled @bonfida/spl-name-service reader and validates it as a CSAP 2.9 value (st:opq:-prefixed or raw 132-hex). Inject sns: { getRecord } to mock or to read from a custom source.

ONS name management

Register, claim, and track ONS names for this wallet’s meta-address (see the naming guide for the consistency model):
ENS setup. The *.eth path reads the com.opaque.meta text record (CSAP 2.9). Pass an ENS-capable viem PublicClient (mainnet or Sepolia; your scan RPC usually is not one), or inject a custom reader:
IPFS setup. The ipfs:// path fetches a DID document from public gateways (ipfs.io, cloudflare-ipfs.com by default; tried in order) and extracts the meta-address from either a service entry of type OpaqueStealthMetaAddress or a top-level com.opaque.meta / opaqueMetaAddress field:

resolveRecipientMetaAddress(recipientAddress)

Lower-level Ethereum-only registry lookup. Unlike resolveRecipient, it does not throw for unregistered recipients, so it suits “is this person on Opaque yet?” UI checks.

buildRegisterMetaAddressTransaction()

Encode registerKeys calldata without submitting.

registerMetaAddress(chain)

Submit meta-address registration on "ethereum" or "solana".
Signers: ethereumWalletClient / ethereumProvider (EVM) or solanaWallet (Solana).

isMetaAddressRegistered(chain)

Check if this wallet’s meta-address is registered.

Send

prepareStealthSend(recipientMetaAddressHex)

Derive one-time stealth material for sending. Pure crypto, no chain call.

sendStealthPayment(params)

High-level native send + announce (optional relay, optional delayed announce).
Returns: SendStealthPaymentResult: { chain, txHash, announceTxHash?, announcePromise?, stealthAddress, destination?, ephemeralPublicKey, metaAddressHex } Solana bundles transfer + announce in one transaction (unless delayAnnouncement is set, which forces two). Ethereum submits the transfer first, then the announce.
sendStealthPayment accepts only "ethereum" and "solana" and throws for "starknet" (as does registerMetaAddress). On Starknet, send via buildStarknetStealthSend below; meta-address registration goes through the @opaquecash/stealth-chain-starknet adapter (SNIP-12 on-behalf) — see Starknet.

buildStarknetStealthSend(params)

Starknet send + announce (Starknet Sepolia, testnet-preview). Builds the unsigned transfer and announce calls; the app’s connected Starknet wallet broadcasts them (the one meta-address and wallet already span all three chains).
Requires the starknet: {} client option and @opaquecash/stealth-chain-starknet. See Starknet for the full flow and caveats.

prepareGhostReceive()

Derive stealth receive material for your own meta-address (no announce yet).

buildAnnounceTransactionRequest(send)

Build StealthAddressAnnouncer.announce calldata.

buildAnnounceTransactionRequestForGhost(ephemeralPrivateKey)

Rebuild announce calldata from a stored ephemeral private key only.

Anonymity utilities

Two tools for growing the anonymity set around your payments (guide section 17).

delayAnnouncement (send option)

Decouple send time from announce time: the value transfer is submitted immediately, the announcement only after the delay, breaking the timing correlation between the two on-chain events.
Await (or attach a handler to) announcePromise, and keep the process alive until it resolves. If your process exits first, the announcement is never submitted and the recipient cannot discover the payment by scanning.

generateDummyAnnouncements(n)

Mint n decoy announcements. Each is a fully valid DKSAP announcement to a freshly generated THROWAWAY meta-address whose private keys are discarded: on-chain it is indistinguishable from a real payment announcement (valid curve points, correctly derived view tag), but nobody will ever match or spend it.

buildDummyAnnouncementTransactions(n)

Convenience over generateDummyAnnouncements: ready-to-submit announce calldata. Broadcast from any account, interleaved with real sends; announcements carry no value and anyone may announce.

Scan and filter

fetchAnnouncementRows(chain, opts?)

Fetch ALL native announcements on a chain as indexer-shaped rows, unfiltered, with full on-chain metadata. Raw input for the metadata-aware scanners (filterOwnedAnnouncements, discoverTraitsV2, and legacy discoverTraits); unlike scan, nothing is dropped.
Cross-chain (UAB) announcements are not included: the 96-byte Wormhole payload only carries a 24-byte metadata tail, so relayed rows cannot hold attestation metadata. Fetch rows natively on each chain instead (UAB outputs still surface in scan / fetchCrossChainAnnouncements).

filterOwnedAnnouncements(rows)

WASM scan: filter indexer rows to outputs owned by this wallet. Malformed announcements (for example an ephemeral key that is not a valid curve point) are skipped, never fatal; anyone can announce.

scan(opts)

Unified multichain inbox across all three supported chains.
Include "starknet" (Starknet Sepolia, testnet-preview) only when the client was created with the starknet: {} option. Starknet participates as a native scan chain, not a Wormhole/UAB relay endpoint, so includeCrossChain adds no Starknet rows. See Starknet.

Balances

getBalancesFromAnnouncements(rows)

Filter owned outputs and sum balances per tracked token (EVM RPC).

getBalancesForOutputs(outputs)

Native balance per UnifiedOwnedOutput (multichain).

Sweep and keys

sweep(params)

Sweep the full native balance from a stealth output, or the full balance of token (ERC-20 address / SPL mint) when set. closeAccount (Solana) reclaims the token-account rent.
For a stealth address holding a token but no gas, use buildGaslessTokenSweep and submit the result to a relayer node with postGaslessSweep from @opaquecash/relayer-client; the relayer fronts the gas and is reimbursed fee in-token on both Ethereum and Solana (see Gasless token sweep). The gasless forwarder is EVM/Solana only; Starknet sweeps self-fund instead (below).

buildStarknetSweep(params)

Sweep a Starknet stealth output (Starknet Sepolia, testnet-preview). Returns the sweep call for the app to broadcast via starknet.js (EthSigner, v10+). There is no fee-in-token gasless forwarder on Starknet: the counterfactual stealth account self-funds its deploy_account fee.
See Starknet.

getStealthSignerPrivateKey(output)

Reconstruct the 32-byte secp256k1 private key for an owned output.

getStealthSignerPrivateKeyFromEphemeralPrivateKey(ephemeralPrivateKey)

Same reconstruction from a ghost-stored ephemeral secret.