Skip to content
Resources

Changelog

Every change to the SDK since its last version, generated from the repository's changesets.

The SDK has no released version yet, so it has no CHANGELOG.md. This page lists every change waiting for the next version, newest first. It is built from the repository's changesets when the site is built.

Each entry carries a label:

LabelWhat it means for your app
BreakingYou may need to change your code when you upgrade.
AddedSomething new, and nothing you use changes.
FixedA bug fix.

Older entries name functions as they were then. A later entry may have renamed them.

Addednimbus-solana-fee-payer-unfunded

A flow the connected wallet pays for refuses a wallet that cannot pay before any work: a new NimbusSolanaFlowErrorReason, fee-payer-unfunded, is thrown while register, deposit (both transaction versions) and refundStrandedDeposit are prepared, before any proving key is downloaded, any proof made or the committee and data endorser asked. It is raised when the wallet has no account or holds fewer lamports than the transaction takes from it plus the rent-exempt minimum it keeps; its detail carries requiredLamports and heldLamports as decimal strings. The wallet is read with the accounts each flow reads already.

prepareRegistration and prepareDepositRefund take the priority fee in their options, and RegisterDeps gains fetchSolanaAccounts. Relayed withdrawals and transfers are unchanged: the relayer pays for them.

Fixednimbus-solana-relay-refused

A withdrawal, transfer or merge the relayer refuses (relay-refused) gives its notes back at once. The relayer's definitive refusal, and a committee that withheld its signatures so nothing was posted, are reported to the note store as relay-refused, which ends the attempt, so the abandon that follows frees the notes and restores the balance instead of holding them until the proof window closes. A lost answer, or a withheld outcome whose answers do sum, still holds them.

Fixednimbus-solana-web-coordinator-batches

The reads and committee requests a flow makes together through the process coordinator now share one prepare and one attest, as the earlier SDK's did, instead of one call per item: a deposit makes 3 coordinator calls where it made 11, and a relayed spend 3 where it made 7. Busy items are still re-sent alone, an item's refusal reaches only its caller, and two flows never share a batch.

Fixednimbus-solana-web-coordinator-busy-items

On the coordinator route, a read or committee request an upstream answered busy (a status the service clients retry, or no answer at all) is sent again on its own, up to three sends in all, pausing 500 ms and then 1000 ms through the platform's waitFor. One 429 or 503 from a guardian, the screener, the data endorser, the fee enclave, the context API or the relayer no longer fails a deposit or spend before anything is sent; an item still busy after the third send fails as before.

Fixednimbus-solana-web-direct-relay-policy

On the coordinator route the relayer's policy is read from the relayer itself, as the earlier SDK read it, so a relayed spend makes one coordinator prepare again instead of two.

Fixednimbus-solana-web-mainnet-lookup-table

The mainnet preset names DE3Y2HM8CrwQfb7nTpuuMFeYeXboBnKUDDrMS4RmJLgY as the deposit lookup table, the one mainnet-v2 deposits compile against, which holds every address of the table it named before and 49 more, uUSDv's and kVUSDC's among them.

Fixednimbus-solana-default-creation-fee

A withdrawal to an address whose token account does not exist yet, through a relayer that accepts a zero fee but does not sponsor creating the account, pays the relayer's published floor and destination-creation fee in the fee's mint by default, instead of refusing with relay-unavailable. The fee is the prepared stage's relayerFee and is settled as every relayer fee is: out of the amount under deduct, on top of it under add. Only the default floor changes: an offered fee below what creating the account costs is still refused, and a relayer that publishes no payment or creation fee in the mint, or no tip accrual for it, still refuses. @umbra/nimbus-solana-web's submitterFee option documents the same.

Addednimbus-web-unregistered-party

A deposit or a transfer to a wallet that has not registered throws NimbusSdkError with the new reason recipient-unregistered, its message carrying the context API's detail, before the wallet is asked to sign or anything is sent. It was service-unavailable, which an application could tell from an unreachable service only by its message. When the context API holds no registration of the session's own identity, or for a withdrawal of the connected wallet, the reason is the new account-unregistered. Both route the same way directly and through the coordinator.

@umbra/service-client-core's refuseStatus keeps an RFC 9457 problem body on the ClientError it throws, as problem: the answer's HTTP status, the problem's type, and its code and detail when present. The new ServiceProblem type describes it. The reason and message are unchanged.

Fixednimbus-solana-web-tsdoc-corrections

Correct two TSDoc entries. unsupported-browser also names navigator.storage and IDBKeyRange, which loadNimbusSdk checks for. ReservedSpend.kind says what a merge and an escape are. Docs only: no behaviour changes.

Fixednimbus-solana-web-misconfigured-arguments

A bad argument is refused as NimbusSdkError with reason misconfigured, before anything is fetched or read, so isNimbusSdkError recognises every caller's mistake:

  • resolvePendingDeposit refuses a submission whose outcome is not unknown. It threw @umbra/nimbus-primitives' ProtocolError, which neither isNimbusSdkError nor isNimbusSolanaFlowError recognised.
  • findMintByIdentifier refuses an identifier shorter than 32 bytes. It threw a RangeError.
  • readHistory refuses a limit that is not a whole number from 1 to 100. It threw unreadable, and for a session whose store held nothing yet it returned an empty page instead.
  • syncNoteStore refuses a pageRows that is not a whole number from 1 to 1000. It threw the note store's RangeError, and only for a registered identity; an unregistered one returned unregistered.
Addednimbus-solana-web-reserved-deposits

listReservedSpends lists a deposit that holds the notes it merges, as kind: "deposit", and releaseReservedNotes and the release that ends every syncNoteStore release it as they release a spend. Before, readBalances counted such a deposit's notes as reserved while neither listed nor released them, so a stopped deposit's merged notes stayed held and the application had no call that freed them. A deposit that merges no notes holds none and is still not listed.

ReservedSpend.kind gains the variant "deposit". An application whose switch over it is exhaustive, or which types a table as Record<ReservedSpend["kind"], …>, stops compiling until it handles a deposit.

A deposit released while its transaction may still land keeps its notes until that transaction lands or can no longer land. resolvePendingDeposit still settles it, and a failed or expired resolution then answers intent: "settled", the release having stopped it already.

Addednimbus-solana-web-transaction-types

The package exports the transaction types its public types name, so an application no longer derives them or imports them from @umbra/nimbus-solana:

  • TransactionSubmission, where a transaction the wallet sent ended up, which a submitted registration, deposit or refund result carries.
  • TransactionVersion, the transactionVersion the flows' options and ConnectedWallet name.
  • ConfirmationCommitment, the commitment the flows' options name.

All three are type-only exports.

Fixednimbus-solana-web-tsdoc

Document every public function and type in full: each field of the configuration, contexts, options, results and stages, each inline argument, each NimbusSdkErrorReason with what an application does about it, and each function's return values and failures, including the misconfigured refusals of syncNoteStore's maxRounds and the RangeError its pageRows throws. Comments only; no behaviour changes.

Addednimbus-solana-web-app-types

The package exports the types an application names, so it no longer imports them from the packages below it.

  • The flows' progress stages: RegisterStage, DepositStage, WithdrawStage, TransferStage and RefundStage.
  • DepositSubmission, which resolvePendingDeposit takes.
  • SpendRefusal and the refusals a spend result names: MergeFailureCause, WalletStoreRefusal, LeaseRefusal, BeginAttemptRefusal and MarkAttemptSentRefusal.
  • HistoryPosition, the type of readHistory's after.
  • The wallet types: ConnectedWallet, WalletConnectionResult, WalletDiscoveryOptions and the Wallet Standard's Wallet.
  • isNimbusSolanaFlowError, with NimbusSolanaFlowError and NimbusSolanaFlowErrorReason, to recognise the protocol's own refusals a flow throws.
Addednimbus-solana-web-asset-integrity

The SDK checks the WebAssembly and scripts it ships before it runs them, and the primitives before it uses them.

  • build:assets, which prepack runs after build, pins every file it writes to assets/ by its SHA-256: in the built dist/asset-digests.js for the main thread, and in prover-worker.js for the files the worker loads. A packed package carries the digests of exactly the assets packed with it.
  • loadNimbusSdk hashes the primitives' module before instantiating it, and the default prover worker's script before starting it, refusing a file that is not the pinned one as dependency-nonconforming, naming the file and both digests. The shipped worker checks each .wasm on the bytes it instantiates and each glue or helper script before importing it, answering failed with reason dependency-nonconforming. Scripts are imported by URL after their check, so a server that answers the check and the import with different bytes is not caught; a mismatched build or a corrupted file is.
  • loadNimbusSdk runs @umbra/nimbus-primitives/conformance's known-answer checks on the loaded primitives, refusing a failure as dependency-nonconforming naming the primitive and its check.
  • settings.assetPinning is pinned in a packed package, and unpinned when it runs from source, where no file is checked.
  • NimbusPlatform.computeSha256Digest is Web Crypto's SHA-256 by default, and platform.startProverWorker may resolve to its worker.
Addednimbus-solana-web-wallets

The SDK finds and connects Wallet Standard wallets itself, so an application no longer writes its own discovery and signer.

  • listWallets(sdk) lists the installed wallets that can connect, sign messages and sign transactions on the SDK's cluster chain (solana:mainnet or solana:devnet).
  • followWallets(sdk, { onChange }) calls onChange with that list whenever a wallet registers or unregisters, and returns the function that stops following.
  • openWalletConnection(sdk, { wallet }) asks the wallet to connect and answers connected with the WalletStandardSigner openNimbusSession takes and the transaction version the wallet signs (1 when it supports it, else 0), no-account when it offers no account on the cluster, or declined when the user refuses; a wallet that cannot sign on the cluster, or fails to connect, is refused as wallet-unsupported.
  • Each takes options.wallets, the registry to read, which defaults to @wallet-standard/app's getWallets().
Breakingnimbus-solana-web-shipped-assets

The package ships every browser file it runs, so an application copies one folder, serves the page cross-origin isolated, and loads with loadNimbusSdk({ cluster: "mainnet" }, { assetsBaseUrl: "/nimbus/" }).

  • assets/ holds the primitives' module, the umbra prover's and the witness graph's WASM packages, and prover-worker.js, a self-contained module worker that loads both WASM modules beside itself and answers with proveWorkerRequest. nimbus-solana-web copy-assets <dir> copies it into the directory the application serves.
  • LoadNimbusSdkOptions.assetsBaseUrl names that directory, ending with /. The primitives' module is fetched from it unless endpoints.primitivesWasm is given, and each proof runs in a module worker started from its prover-worker.js unless platform.startProverWorker is given. A value without the trailing slash, or one that is not a URL, is refused as misconfigured.
  • loadNimbusSdk refuses a page that is not cross-origin isolated as unsupported-browser, naming Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp: the prover's WebAssembly declares shared memory, which a browser creates on no other page. settings.proving.allowSingleThreaded is false, and threads the core count.
Breakingnimbus-solana-web-mainnet-out-of-the-box

Mainnet works out of the box: loadNimbusSdk({ cluster: "mainnet" }, …) needs only endpoints.primitivesWasm, which the application serves, and a prover worker.

  • The mainnet preset configures every endpoint the production web app reaches: the Solana RPC, the context and data APIs on their own hosts, and the relayer, process coordinator, screener node 0, data endorser node 0, fee enclave and guardian nodes 1 to 3 behind gateway.api-v2.umbraprivacy.com. Its proving keys are the mainnet-v2 asset host's zk/ files, pinned by the web app's manifest, with a 2-of-3 statement for registration and the transaction circuit and a three-input statement for the absence circuit. primitivesWasm stays unconfigured on every preset, and the refusal says the application serves it.
  • openNimbusSession takes a gatewayToken option, a bearer token string or an issuer () => Promise<string> asked once per gateway request. The session holds it as session.gatewayToken (NimbusGatewayToken, a string in a Secret), and session.sdk is the loaded SDK with its fetch sending Authorization: Bearer to the origins of settings.gatewayEndpoints and nowhere else. closeNimbusSession drops a held token. A string that is not an RFC 6750 bearer token is refused as misconfigured.
  • NimbusSdkErrorReason gains unauthorized: a gateway service answered 401 or 403, or the issuer gave no usable token. The message names the service, never the token. Either status now fails at once: a 403 is no longer retried, and a 401 no longer read as the service's refusal.
  • NimbusSettings gains gatewayEndpoints (NimbusGatewayEndpoint[]): every configured endpoint the preset marks gateway-routed, the coordinator only on the coordinator route. Mainnet marks all six gateway services, and devnet the guardians and the screener, as each stack's gateway routes them.
  • Cached circuit artifacts are keyed by the digest the manifest pins, so a key rotated under the same constraint system is downloaded afresh, where the cached old key would fail every proof. Artifacts cached before this release are downloaded once more.
Breakingnimbus-solana-web-merge-notes

withdraw and transfer merge the session's notes first when the largest three cannot cover the payment, as often as the payment needs, as @umbra/nimbus-solana's one-call flows now do. They take mergeTimeout? (slots for each merge, 300 when omitted), hand onProgress the merge stages (merge-prepared, merge-relayed, merge-settled) before the payment's, and WithdrawalResult and TransferResult gain merge-failed. A refusal insufficient names the total of every note the session may spend in the pool, zone and mint. The RPC filler gains fetchFinalizedSlotAfter, polling at the preset's interval.

Breakingnimbus-solana-web-session-names

Renames the public API to sessions and signature secrets. Behaviour, stored bytes and key derivation are unchanged, and the default sessionStorage key prefix stays umbra/nimbus-solana-web/account-secret/, so secrets already stored still open without a signature.

  • openNimbusAccount(sdk, { wallet }, { secretStore?, accountIndex? }) → openNimbusSession(sdk, { connectedWallet }, { signatureSecretStore?, accountIndex? })
  • OpenNimbusAccountOptions → OpenNimbusSessionOptions
  • OpenNimbusAccountResult ({ kind: "opened", account }) → OpenNimbusSessionResult ({ kind: "opened", session })
  • closeNimbusAccount(account) → closeNimbusSession(session)
  • NimbusAccount → NimbusSession, whose session field (the WalletSession) is now walletSession
  • createSessionStorageAccountSecretStore → createTabSignatureSecretStore
  • createLocalStorageAccountSecretStore → createPersistentSignatureSecretStore
  • createMemoryAccountSecretStore → createMemorySignatureSecretStore
  • AccountSecretStore (readAccountSecret, writeAccountSecret, wipeAccountSecret) → SignatureSecretStore (readSignatureSecret, writeSignatureSecret, wipeSignatureSecret)
  • AccountSecretKey → SignatureSecretKey, AccountSecretReading → SignatureSecretReading, AccountSecretStoreOptions → SignatureSecretStoreOptions
  • the stores' accountSecret field, in a found reading and in writeSignatureSecret's arguments → signatureSecret
  • ingestAccountActivity(account, …) → syncNoteStore(session, …)
  • AccountActivityResult → NoteStoreSyncResult, IngestAccountActivityOptions → SyncNoteStoreOptions, AccountActivityRound → NoteStoreSyncRound
  • readAccountBalances(account, …) → readBalances(session, …), AccountBalance → Balance
  • readAccountHistory(account, …) → readHistory(session, …), AccountHistoryPage → HistoryPage, re-exported from @umbra/nimbus-storage-solana/record-store
  • refund(ctx, { mint }, options?) → refundStrandedDeposit(ctx, { mint }, options?), RefundOptions → StrandedDepositRefundOptions, RefundResult → StrandedDepositRefundResult
  • resolveDeposit(ctx, { submission }) → resolvePendingDeposit(ctx, { submission }), DepositResolutionResult → PendingDepositResolutionResult
  • the contexts' account field → session, and createRegistrationContext, createDepositContext, createWithdrawalContext and createTransferContext take the session
Fixednimbus-solana-web-mainnet-deployment

Mainnet's preset derives keys under the deployment mainnet, as the web app does, not the mainnet-v2 stack name: under mainnet-v2 every existing mainnet account would have opened as a new, empty one. A test pins the web app's own derivation vectors for both clusters.

Breakingnimbus-solana-web-flows

Adds the contexts (createRegistrationContext, createDepositContext, createWithdrawalContext, createTransferContext), the flows with every dependency filled (register, deposit, withdraw, transfer, refund, resolveDeposit) and the account reads (ingestAccountActivity, which also takes in each pool's registrations a spend needs, readAccountBalances, readAccountHistory). Addresses and mints are base58 Addresses; a zone travels as zone: { id, signer }.

Fixednimbus-solana-routing-context

Every flow's ctx is { deployment, session, store, routing }, and args holds only the payment.

  • routing (FlowRouting) carries account, poolIndex, screenerNodeIndex, dataEndorserNodeIndex and clusterIdentifier; a registration takes RegisterRouting, the same without the screener. clusterIdentifier leaves the deposit's and spends' ctx top level and the registration's args for it.
  • store is { backend, choices } (FlowWalletStore) everywhere. Withdrawals and transfers no longer take an opened wallet: they open the store bound to the account's identity, as a deposit does, refuse with store-refused (WalletStoreRefusal) when it will not open, and carry the opened store in PendingSpend<Backend>, so relaySpend, relayEndorsedSpend and abandonSpend no longer read the store from ctx.
  • args: deposit { mint, amount, recipient }, withdraw { mint, amount, recipient } (the recipient is the token account and owner WithdrawDestination was, now WithdrawRecipient), transfer { mint, amount, recipient }, register {}, refund { mint }, and resolveDeposit { submission }. destination is recipient throughout, the deposit stage and the context port's DepositContextRequest included; SpendSource is SpendPayment.
  • The zone is a flow option: opts.zone is { kind: "zone-0" } or { kind: "zone"; id; signer }, id from 1 to 2^20 - 1 and signer a ZoneSignerPort, in the one-call deposit, withdrawal and transfer and in prepareDeposit, prepareWithdrawal and prepareTransfer. signZoneAuthorisation leaves NimbusSolanaDeps; the defaults spend in zone 0.
  • A deposit pays a registered identity only: DepositDestination and its program kind are gone, and a recipient the context service resolves with no registration path is refused as context-mismatched. The wallet's balances and history show the notes the identity owns.
Breakingnimbus-solana-web-unregistered-store

openNimbusAccount no longer gives an unregistered identity's note store a birthday, which made the store refuse the one register sets, and keeps the birthday a registered identity's store already has. NimbusAccount.noteStore is removed; the store is opened where it is read.

Breakingnimbus-solana-web-load-and-accounts

Replaces loadNimbusSolanaWeb, its configuration and platform types, NimbusSolanaWebError, openNimbusSolanaWebBackend, KIT_SOLANA_FORMATS and the ./coordinated entry with loadNimbusSdk({ cluster }, options?), which reads a bundled per-cluster preset completed by its options (route: "coordinator" replaces ./coordinated), openNimbusAccount and closeNimbusAccount, three account secret stores (session storage by default, local storage and memory), and NimbusSdkError with isNimbusSdkError. The wallet is no longer part of loading.

Addedno-sdk-reaches-for-global-fetch

No SDK package reaches for the global fetch.

@umbra/circuit-artifacts: FetchDeps.fetch is required, and fetchManifest, the encodings' download and the resolver no longer fall back to the global. deps stays after opts and is required: fetchManifest(args, opts, { fetch }), download(source, args, opts, { fetch }). ResolveDeps extends FetchDeps, and the resolver hands download that fetch, so an encoding module still passes as EncodingDeps whole: { ...plain, fetch, ...store }.

@umbra/nimbus-robinhood: readArtifactManifest takes required deps with fetch, and createArtifactResolver's deps carry the fetch its downloads go through.

@umbra/nimbus-solana-web: Kit's Solana RPC runs on a transport over the platform's fetch (Kit's own HTTP transport calls the global), so every RPC request, like every service request, goes through the platform's fetch. The transport keeps Kit's bigint-preserving JSON and its HTTP error; it does not coalesce identical requests in flight together, as Kit's default does.

@umbra/solana-indexer-client: the injected fetch is called with no receiver, so a browser's own fetch can be passed as { fetch }.

Fixedinject-fetch-service-clients

Every service client takes fetch as an injected dependency, with no default: nothing reaches for the global fetch. @umbra/service-client-core exports ServiceDeps ({ fetch }), and its request, requestJson, requestBytes and isLive take deps before their optional opts: (ctx, args, deps, opts?). A call that was requestJson(ctx, args, opts) becomes requestJson(ctx, args, { fetch }, opts). The injected fetch is called with no receiver, so a browser's own fetch can be passed as { fetch }.

In each client, every function that reaches its service takes the same deps, in the same position:

  • A function whose deps slot was empty now takes deps: Pick<ServiceDeps, "fetch"> before its opts, and an optional _args becomes a required {}. For example, readIdentity(ctx, {}, { fetch }).
  • GuardianDeps and FeeEnclaveDeps gain fetch, so the endorse calls, the committee helpers and provideFeeClaim keep their positions and take it with their seal primitives. The functions that only seal (buildProcessEndorsement, sealFeeClaim) take the primitives and the random source without it.
  • Factory-made clients (createRelayClient, createDepositDataClient, createSpendDataClient) take it in their closures' existing deps slot, which was typed unknown and ignored. The directories whose closure takes one args object (createRegistrationDirectory, createOwnerDirectory) take it as the closure's second parameter.
  • note-sync's DownloadDeps gains fetch, and its readers (fetchManifest, fetchChunk, fetchChunkHeader, fetchChunkRange, the page and record reads) take it before their opts.

nimbus-robinhood's FlowDeps gains fetch, and createFlowDeps takes it beside the signer, the prover and the storage; createRpc, createAuthorisers, createCoordinatedAuthorisers, the JSON-RPC, indexer and relayer readers take it in their deps. nimbus-solana-web passes the platform's fetch to every client, direct or through the coordinator, and robinhood-pool-cli passes the platform's own at each call, as an entry point does.

Addednimbus-solana-web-zone-signer

The deps fill nimbus-solana's new signZoneAuthorisation with a signer that refuses every zone, naming it and the side: no zone signer service exists, zone 0 never asks one, and a caller using another zone replaces the member.

Fixednimbus-solana-web-context-leaf

The context filler takes a registered standing's leaf from @umbra/solana-context-client's leaf, which is unassigned exactly when the context API serves no index, instead of inferring it from provable. Deposit and spend contexts the API serves now fill the port: the client parses each path's leaf index and shape and each gap's value and shape, which the filler refused as unreadable while the client cast them.

Fixednimbus-solana-web-wallet-entry

Imports the wallet's types from @umbra/nimbus-solana/wallet, the entry ./wallet-store became.

Addednimbus-solana-web

Add the browser SDK: loadNimbusSolanaWeb(config, platform) loads the primitives' WASM module and returns the decoded deployment and one deps object that fills every member @umbra/nimbus-solana's flows and wallet call. Crypto comes from @umbra/nimbus-primitives-wasm; Anza's formats and the instruction builders from @solana/kit and @solana-program/* (KIT_SOLANA_FORMATS); the RPC port, the platform wallet binding and the sync's pool reading from Kit's RPC; the wallet signer from the Wallet Standard's solana:signMessage and solana:signTransaction; the lock from Web Locks; storage from the IndexedDB backend openNimbusSolanaWebBackend opens under buildNoteStoreBackendName; the indexer feeds from @umbra/solana-indexer-client; every service from its client; proving keys from @umbra/circuit-artifacts, cached through @umbra/store-web and checked against the one verifying key the selection names: the configured statement, the manifest's verifying key, and its body in the deployed program's ELF; and proofs from @umbra/prover's umbra backend in a worker that runs ./prover-worker's proveWorkerRequest. ./coordinated's loadCoordinatedNimbusSolanaWeb reaches a transaction's reads, committee and relay through the process coordinator. Configuration is data; nothing is bundled.

Last updated