SDK reference

Everything @offrecord/sdk exports: the client, the account, the helpers, the types, the stages and the error codes.

The client

TypeScript
function createOffrecord(options: OffrecordOptions): OffrecordClient

OffrecordClient implements Offrecord.

OptionTypeMeaning
apiUrlstringThe Offrecord API.
proverUrlstring, optionalThe prover that makes a transfer’s proofs. Defaults to <apiUrl>/prover.
proverTeeProverTee, optionalThe attestation policy for a prover that is not on the local machine.
fetchtypeof fetch, optionalA fetch to use in place of the global one.
headersRecord<string, string>, optionalHeaders added to every API request.
fixturestring, optionalThe scenario a fixture server answers with.

Offrecord

MethodReturnsWhat it does
ring()Promise<RingInfo>Reads the Ring’s public state.
resolve(address)Promise<Resolution>Says whether an address has an account and whether it is on the block list.
quote(input)Promise<Quote>Prices one send or withdrawal through the relayer, with no account.
restore(address)Promise<Account | null>Returns the account when its keys were already derived in this browser. No signature.
open(wallet)Promise<Account>Derives the keys and enrolls them on first use (two message signatures), then returns the account.
verifyReceipt(link)Promise<VerifiedReceipt>Opens one transfer with the key in the link and matches it to the chain.
TypeScript
interface Offrecord {
  ring(): Promise<RingInfo>
  resolve(address: string): Promise<Resolution>
  quote(input: QuoteRequest): Promise<Quote>
  restore(address: string): Promise<Account | null>
  open(wallet: WalletSigner): Promise<Account>
  verifyReceipt(link: string): Promise<VerifiedReceipt>
}

Account

MemberReturnsWhat it does
addressstringThe wallet’s Solana address.
sync(onProgress?)Promise<void>Scans the Ring’s history with the account’s viewing key. Resolves when caught up.
subscribe(listener)() => voidCalls the listener when a scan finds new notes or a transfer changes status. Returns the function that stops it.
balance()Promise<Holding[]>What the account holds off the record, per asset.
publicBalance()Promise<Holding[]>What the wallet holds on the record, per asset.
activity()Promise<Transfer[]>The account’s deposits, sends, receipts of payment and withdrawals.
transfer(id)Promise<Transfer | null>One transfer by its id.
quote(input, options?)Promise<Quote>The relay cost, exit fee and received amount of one send or withdrawal.
deposit(input, progress?)Promise<Transfer>Moves an asset from the wallet into the Ring.
send(input, options?)Promise<Transfer>Sends to a Solana address.
withdraw(input, options?)Promise<Transfer>Sends an asset to a public address.
receipt(id)Promise<string>A link that opens this one transfer.
exportViewingKey()Promise<string>The account viewing key as file contents.
TypeScript
interface Account {
  readonly address: string
  sync(onProgress?: (state: SyncState) => void): Promise<void>
  subscribe(listener: () => void): () => void
  balance(): Promise<Holding[]>
  publicBalance(): Promise<Holding[]>
  activity(): Promise<Transfer[]>
  transfer(id: string): Promise<Transfer | null>
  quote(input: QuoteRequest, options?: Pick<TransferOptions, 'feePayer'>): Promise<Quote>
  deposit(input: MoveInput, progress?: Progress): Promise<Transfer>
  send(input: SendInput, options?: TransferOptions): Promise<Transfer>
  withdraw(input: SendInput, options?: TransferOptions): Promise<Transfer>
  receipt(id: string): Promise<string>
  exportViewingKey(): Promise<string>
}

Helpers

ExportWhat it is
AUDITOR_SENTENCEThe sentence the Ring’s auditor key derives from.
auditorKeyFromSentence(sentence)The auditor key for a sentence, as lowercase hex. See The empty seat.
formatReceiptLink(signature, key, origin?)Builds a receipt link. Relative without an origin, absolute with one.
parseReceiptLink(link)Reads the signature and key out of a receipt link. No network request.
NATIVE_SOL_MINTThe mint that stands for native SOL.
WRAPPED_SOL_MINTThe mint of wrapped SOL, a separate SPL asset.
OffrecordErrorThe error every call rejects with.
toOffrecordError(error)Turns any thrown value into an OffrecordError.
TypeScript
function auditorKeyFromSentence(sentence: string): Promise<string>
function formatReceiptLink(signature: string, disclosure: ReceiptKey, origin?: string): string
function parseReceiptLink(link: string): ParsedReceiptLink
function toOffrecordError(error: unknown): OffrecordError

Types

Amounts are base units as decimal strings. Times are ISO 8601 strings. Addresses and signatures are base58, keys and hashes lowercase hex.

Assets and holdings

TypeScript
type Asset = { mint: string; symbol: string; name: string; decimals: number }
type Holding = { asset: Asset; amount: string }

Inputs

TypeScript
type MoveInput = { mint: string; amount: string }
type SendInput = MoveInput & { to: string }
type QuoteRequest = SendInput & { kind: 'send' | 'withdraw' }

type Progress = { onStage?: (stage: Stage) => void }
type TransferOptions = Progress & { feePayer?: WalletSigner }

type WalletSigner = {
  address: string
  signMessage(message: Uint8Array): Promise<Uint8Array>
  signTransaction(transaction: Uint8Array): Promise<Uint8Array>
}

Transfers and quotes

TypeScript
type TransferKind = 'deposit' | 'send' | 'receive' | 'withdraw'
type TransferStatus = 'pending' | 'confirmed' | 'failed'

type Transfer = {
  id: string
  kind: TransferKind
  status: TransferStatus
  asset: Asset
  amount: string
  counterparty: string
  onTheRecord?: boolean
  exitFee?: string
  relayCost?: string
  time: string
  signature: string | null
  failure?: string
}

type Quote = {
  id: string
  route: 'ring' | 'withdrawal'
  mint: string
  amount: string
  to: string
  relayCost: string
  exitFee: string
  receives: string
  expiresAt: string
  exitFeeBps: number
  relayerAddress: string
  relayRecipient: string
  feeVaultAddress: string
}

type SyncState = { scanned: number; total: number; done: boolean }
FieldMeaning
Transfer.counterpartyThe other party: the recipient of a send, the sender of a payment received, the destination of a withdrawal, the funding wallet of a deposit.
Transfer.onTheRecordtrue when a send settled as a withdrawal to an address with no account.
Transfer.failureThe reason, when status is failed.
Quote.routering when the recipient has an account, withdrawal otherwise and for every withdrawal.
Quote.amountThe total that leaves the off-record balance.
Quote.receivesWhat the recipient or destination ends up with.

The Ring

TypeScript
type RingInfo = {
  cluster: 'localnet' | 'devnet' | 'mainnet'
  programId: string
  ringAccount: string
  sppProgramId: string
  storageProgramId: string
  userRegistryProgramId: string
  recordsRegistry: string
  trackingRegistry: string
  auditor: { key: string; sentence: string }
  policy: { hash: string; rules: { subject: 'sender' | 'output-owner'; list: 'block' }[] }
  blockList: { size: number; root: string; lastSyncAt: string | null; source: string }
  notes: number
  totals: { asset: Asset; in: string; out: string }[]
  exitFeeBps: number
  feeVault: { address: string; balances: Holding[] }
  relayer: { status: 'live' | 'down'; address: string }
  paused: boolean
}

type Resolution = {
  address: string
  account: boolean
  listed: boolean
  ringAddress: string | null
}

Receipts

TypeScript
type ReceiptKey =
  | { kind: 'sender'; key: string }
  | { kind: 'recipient'; slot: number; key: string }

type ParsedReceiptLink = { signature: string; disclosure: ReceiptKey }

type OpenedReceiptOutput = {
  index: number
  asset: Asset
  amount: string
  recipient: string
  commitment: string
}

type VerifiedReceipt = {
  signature: string
  asset: Asset
  amount: string
  sender: string
  recipient: string
  time: string
  commitment: string
  keyKind: 'sender' | 'recipient'
  openedOutputs: OpenedReceiptOutput[]
  evidence: { sender: 'stated'; recipient: 'proved' }
}
FieldMeaning
VerifiedReceipt.commitmentThe commitment published on chain that the rebuilt one matched.
VerifiedReceipt.openedOutputsEvery output a sender’s receipt opens, or the one output a recipient’s receipt opens.
VerifiedReceipt.evidenceHow each party is known: the recipient is proved by the rebuilt commitment, the sender is stated by the transfer’s own metadata.

Stages

deposit, send and withdraw report each stage to onStage as they reach it.

StageWhat is happeningReported by
signingThe wallet is asked to sign.deposit, send, withdraw
provingThe two proofs are being made.send, withdraw
relayingThe relayer is submitting the transaction.send, withdraw
followingThe transaction is submitted and followed to confirmation.deposit, send, withdraw
confirmedThe transaction is confirmed. The call resolves with the transfer.deposit, send, withdraw
TypeScript
type Stage = 'signing' | 'proving' | 'relaying' | 'following' | 'confirmed'

Error codes

TypeScript
class OffrecordError extends Error {
  readonly code: OffrecordErrorCode
  readonly details: OffrecordErrorDetails
}
CodeMeaning
listedAn address in the call is on the block list.
pausedThe Ring is paused.
insufficientThe balance does not cover the amount plus its costs.
rejectedThe wallet did not sign.
proofThe prover did not return a proof.
relayThe relayer refused the transfer or is down.
expiredThe quote ran out or the transfer did not land in time.
networkThe API or the RPC did not answer, or the input was not valid.
receipt-keyThis key does not open this transfer.
receipt-unknownNo such transfer.
invalid-addressNot a Solana address.

When a call fails before settlement, nothing has moved.

DetailTypeMeaning
apiCodestringThe API’s own code for the failure.
statusnumberThe HTTP status.
retryablebooleanWhether the same call can be made again.
stagestringThe stage the transfer had reached.
fieldErrorsRecord<string, string[]>What was wrong with each input field.
retryAfterSecondsnumberHow long to wait before the next attempt.
requestIdstringThe id of the request, for tracing it.

Every field of details is optional.