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:
| Label | What it means for your app |
|---|---|
| Breaking | You may need to change your code when you upgrade. |
| Added | Something new, and nothing you use changes. |
| Fixed | A bug fix. |
Older entries name functions as they were then. A later entry may have renamed them.
nimbus-solana-fee-payer-unfundedA 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.
nimbus-solana-relay-refusedA 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.
nimbus-solana-web-coordinator-batchesThe 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.
nimbus-solana-web-coordinator-busy-itemsOn 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.
nimbus-solana-web-direct-relay-policyOn 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.
nimbus-solana-web-mainnet-lookup-tableThe 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.
nimbus-solana-default-creation-feeA 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.
nimbus-web-unregistered-partyA 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.
nimbus-solana-web-tsdoc-correctionsCorrect 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.
nimbus-solana-web-misconfigured-argumentsA bad argument is refused as NimbusSdkError with reason misconfigured, before anything is
fetched or read, so isNimbusSdkError recognises every caller's mistake:
resolvePendingDepositrefuses a submission whose outcome is notunknown. It threw@umbra/nimbus-primitives'ProtocolError, which neitherisNimbusSdkErrornorisNimbusSolanaFlowErrorrecognised.findMintByIdentifierrefuses an identifier shorter than 32 bytes. It threw aRangeError.readHistoryrefuses alimitthat is not a whole number from 1 to 100. It threwunreadable, and for a session whose store held nothing yet it returned an empty page instead.syncNoteStorerefuses apageRowsthat is not a whole number from 1 to 1000. It threw the note store'sRangeError, and only for a registered identity; an unregistered one returnedunregistered.
nimbus-solana-web-reserved-depositslistReservedSpends 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.
nimbus-solana-web-transaction-typesThe 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 asubmittedregistration, deposit or refund result carries.TransactionVersion, thetransactionVersionthe flows' options andConnectedWalletname.ConfirmationCommitment, thecommitmentthe flows' options name.
All three are type-only exports.
nimbus-solana-web-tsdocDocument 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.
nimbus-solana-web-app-typesThe 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,TransferStageandRefundStage. DepositSubmission, whichresolvePendingDeposittakes.SpendRefusaland the refusals a spend result names:MergeFailureCause,WalletStoreRefusal,LeaseRefusal,BeginAttemptRefusalandMarkAttemptSentRefusal.HistoryPosition, the type ofreadHistory'safter.- The wallet types:
ConnectedWallet,WalletConnectionResult,WalletDiscoveryOptionsand the Wallet Standard'sWallet. isNimbusSolanaFlowError, withNimbusSolanaFlowErrorandNimbusSolanaFlowErrorReason, to recognise the protocol's own refusals a flow throws.
nimbus-solana-web-asset-integrityThe SDK checks the WebAssembly and scripts it ships before it runs them, and the primitives before it uses them.
build:assets, whichprepackruns afterbuild, pins every file it writes toassets/by its SHA-256: in the builtdist/asset-digests.jsfor the main thread, and inprover-worker.jsfor the files the worker loads. A packed package carries the digests of exactly the assets packed with it.loadNimbusSdkhashes 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 asdependency-nonconforming, naming the file and both digests. The shipped worker checks each.wasmon the bytes it instantiates and each glue or helper script before importing it, answeringfailedwith reasondependency-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.loadNimbusSdkruns@umbra/nimbus-primitives/conformance's known-answer checks on the loaded primitives, refusing a failure asdependency-nonconformingnaming the primitive and its check.settings.assetPinningispinnedin a packed package, andunpinnedwhen it runs from source, where no file is checked.NimbusPlatform.computeSha256Digestis Web Crypto's SHA-256 by default, andplatform.startProverWorkermay resolve to its worker.
nimbus-solana-web-walletsThe 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:mainnetorsolana:devnet).followWallets(sdk, { onChange })callsonChangewith that list whenever a wallet registers or unregisters, and returns the function that stops following.openWalletConnection(sdk, { wallet })asks the wallet to connect and answersconnectedwith theWalletStandardSigneropenNimbusSessiontakes and the transaction version the wallet signs (1when it supports it, else0),no-accountwhen it offers no account on the cluster, ordeclinedwhen the user refuses; a wallet that cannot sign on the cluster, or fails to connect, is refused aswallet-unsupported.- Each takes
options.wallets, the registry to read, which defaults to@wallet-standard/app'sgetWallets().
nimbus-solana-web-shipped-assetsThe 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, andprover-worker.js, a self-contained module worker that loads both WASM modules beside itself and answers withproveWorkerRequest.nimbus-solana-web copy-assets <dir>copies it into the directory the application serves.LoadNimbusSdkOptions.assetsBaseUrlnames that directory, ending with/. The primitives' module is fetched from it unlessendpoints.primitivesWasmis given, and each proof runs in a module worker started from itsprover-worker.jsunlessplatform.startProverWorkeris given. A value without the trailing slash, or one that is not a URL, is refused asmisconfigured.loadNimbusSdkrefuses a page that is not cross-origin isolated asunsupported-browser, namingCross-Origin-Opener-Policy: same-originandCross-Origin-Embedder-Policy: require-corp: the prover's WebAssembly declares shared memory, which a browser creates on no other page.settings.proving.allowSingleThreadedisfalse, andthreadsthe core count.
nimbus-solana-web-mainnet-out-of-the-boxMainnet 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'szk/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.primitivesWasmstays unconfigured on every preset, and the refusal says the application serves it. openNimbusSessiontakes agatewayTokenoption, a bearer token string or an issuer() => Promise<string>asked once per gateway request. The session holds it assession.gatewayToken(NimbusGatewayToken, a string in aSecret), andsession.sdkis the loaded SDK with itsfetchsendingAuthorization: Bearerto the origins ofsettings.gatewayEndpointsand nowhere else.closeNimbusSessiondrops a held token. A string that is not an RFC 6750 bearer token is refused asmisconfigured.NimbusSdkErrorReasongainsunauthorized: 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.NimbusSettingsgainsgatewayEndpoints(NimbusGatewayEndpoint[]): every configured endpoint the preset marks gateway-routed, the coordinator only on thecoordinatorroute. 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.
nimbus-solana-web-merge-noteswithdraw 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.
nimbus-solana-web-session-namesRenames 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→OpenNimbusSessionOptionsOpenNimbusAccountResult({ kind: "opened", account }) →OpenNimbusSessionResult({ kind: "opened", session })closeNimbusAccount(account)→closeNimbusSession(session)NimbusAccount→NimbusSession, whosesessionfield (theWalletSession) is nowwalletSessioncreateSessionStorageAccountSecretStore→createTabSignatureSecretStorecreateLocalStorageAccountSecretStore→createPersistentSignatureSecretStorecreateMemoryAccountSecretStore→createMemorySignatureSecretStoreAccountSecretStore(readAccountSecret,writeAccountSecret,wipeAccountSecret) →SignatureSecretStore(readSignatureSecret,writeSignatureSecret,wipeSignatureSecret)AccountSecretKey→SignatureSecretKey,AccountSecretReading→SignatureSecretReading,AccountSecretStoreOptions→SignatureSecretStoreOptions- the stores'
accountSecretfield, in afoundreading and inwriteSignatureSecret's arguments →signatureSecret ingestAccountActivity(account, …)→syncNoteStore(session, …)AccountActivityResult→NoteStoreSyncResult,IngestAccountActivityOptions→SyncNoteStoreOptions,AccountActivityRound→NoteStoreSyncRoundreadAccountBalances(account, …)→readBalances(session, …),AccountBalance→BalancereadAccountHistory(account, …)→readHistory(session, …),AccountHistoryPage→HistoryPage, re-exported from@umbra/nimbus-storage-solana/record-storerefund(ctx, { mint }, options?)→refundStrandedDeposit(ctx, { mint }, options?),RefundOptions→StrandedDepositRefundOptions,RefundResult→StrandedDepositRefundResultresolveDeposit(ctx, { submission })→resolvePendingDeposit(ctx, { submission }),DepositResolutionResult→PendingDepositResolutionResult- the contexts'
accountfield →session, andcreateRegistrationContext,createDepositContext,createWithdrawalContextandcreateTransferContexttake the session
nimbus-solana-web-mainnet-deploymentMainnet'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.
nimbus-solana-web-flowsAdds 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 }.
nimbus-solana-routing-contextEvery flow's ctx is { deployment, session, store, routing }, and args holds only the payment.
routing(FlowRouting) carriesaccount,poolIndex,screenerNodeIndex,dataEndorserNodeIndexandclusterIdentifier; a registration takesRegisterRouting, the same without the screener.clusterIdentifierleaves the deposit's and spends'ctxtop level and the registration'sargsfor it.storeis{ backend, choices }(FlowWalletStore) everywhere. Withdrawals and transfers no longer take an openedwallet: they open the store bound to the account's identity, as a deposit does, refuse withstore-refused(WalletStoreRefusal) when it will not open, and carry the opened store inPendingSpend<Backend>, sorelaySpend,relayEndorsedSpendandabandonSpendno longer read the store fromctx.args: deposit{ mint, amount, recipient }, withdraw{ mint, amount, recipient }(the recipient is the token account and ownerWithdrawDestinationwas, nowWithdrawRecipient), transfer{ mint, amount, recipient }, register{}, refund{ mint }, andresolveDeposit{ submission }.destinationisrecipientthroughout, the deposit stage and the context port'sDepositContextRequestincluded;SpendSourceisSpendPayment.- The zone is a flow option:
opts.zoneis{ kind: "zone-0" }or{ kind: "zone"; id; signer },idfrom 1 to 2^20 - 1 andsigneraZoneSignerPort, in the one-call deposit, withdrawal and transfer and inprepareDeposit,prepareWithdrawalandprepareTransfer.signZoneAuthorisationleavesNimbusSolanaDeps; the defaults spend in zone 0. - A deposit pays a registered identity only:
DepositDestinationand itsprogramkind are gone, and a recipient the context service resolves with no registration path is refused ascontext-mismatched. The wallet's balances and history show the notes the identity owns.
nimbus-solana-web-unregistered-storeopenNimbusAccount 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.
nimbus-solana-web-load-and-accountsReplaces 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.
no-sdk-reaches-for-global-fetchNo 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 }.
inject-fetch-service-clientsEvery 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_argsbecomes a required{}. For example,readIdentity(ctx, {}, { fetch }). GuardianDepsandFeeEnclaveDepsgainfetch, so the endorse calls, the committee helpers andprovideFeeClaimkeep 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 typedunknownand ignored. The directories whose closure takes one args object (createRegistrationDirectory,createOwnerDirectory) take it as the closure's second parameter. - note-sync's
DownloadDepsgainsfetch, 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.
nimbus-solana-web-zone-signerThe 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.
nimbus-solana-web-context-leafThe 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.
nimbus-solana-web-wallet-entryImports the wallet's types from @umbra/nimbus-solana/wallet, the entry ./wallet-store became.
nimbus-solana-webAdd 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