Reserved funds
List the payments holding part of the private balance, and give the user a button that releases the stopped ones.
While a payment runs, it holds the part of the private balance it spends. readBalances shows that
part as reserved. Most payments let go on their own when they finish.
Reserved funds explains why they hold it.
A payment that stopped midway can keep holding funds. In this guide, Alice closed her tab during a Private Send to Bob. Her app lists what is still held and offers her a Release button.
List what is held
import { } from "@umbra/nimbus-solana-web";
const = await (, {});
for (const of ) {
.(., ., ., .);
}It reads only the device, so it is cheap to call after every sync. Each ReservedSpend has:
id, whichreleaseReservedNotestakes.kind, what the payment is, below.mintandamount, what it moves, in the token's smallest unit.mintis absent for a token the SDK's preset does not list.state, where it stands, below.running,truewhile a tab of your app is still running it.attempts, how many times it has been tried.
kind | What the payment is |
|---|---|
spend | A Private Send, a withdrawal, or a step that combines amounts before one. The SDK does not tell these apart. |
escape | A withdrawal through the pool's escape hatch. |
deposit | A deposit that also combines amounts already in the private balance. Its amount is what it takes from the wallet. |
Each state
state | What it means | What Release does |
|---|---|---|
unsent | Nothing left the device. | Frees the funds at once. |
sent | It may have reached the relayer or Solana. | Stops new attempts. The funds come back once it lands or can no longer land. |
releasing | Already released, while a sent attempt may still land. | Nothing more. Wait for it to settle. |
running is separate from state. A running payment is still in progress in some tab. Leave it
alone. It frees on its own when that tab finishes or closes.
If a sent payment lands, it completes. Bob receives his USDC, and Alice's funds are spent, not
returned. Say so beside the button.
Release them
import { } from "@umbra/nimbus-solana-web";
const = await (, {}, { });
const = await (, {});
.(.., ..);Leave out spendId to release every held payment. The result sorts them by what happened:
| Field | What happened |
|---|---|
released | The funds are available again now. |
settling | An attempt may still land. The funds come back once it lands or can no longer land. |
running | A tab is still running them, so they were left alone. |
Releasing what is already released, or an id that names nothing, changes nothing. A release reads
the latest slot from your RPC. When it cannot be reached, it throws a NimbusSdkError with reason
service-unavailable.
A Release button
This React component follows the example app's balances panel. It shows one held payment, hides the button while the payment runs, and reports what the release did:
import { } from "@umbra/nimbus-solana-web";
import type {
ReservedSpend,
,
} from "@umbra/nimbus-solana-web";
import { } from "react";
const : <, string> = {
: "Stopped before it was sent. Release returns it now.",
: "May have been sent. Releasing stops new attempts.",
: "Released. It returns once the sent attempt settles.",
};
export function ({ }: { : ReservedSpend }) {
const [, ] = ("");
async function (): <void> {
const = await (
,
{},
{ : . },
);
if (.. > 0) {
("Released. The funds are available again.");
} else if (.. > 0) {
("It may still land. The funds return once it settles.");
} else if (.. > 0) {
("Another tab is still running it.");
}
await ();
}
if (.) {
return <>Still running in another tab. It frees when that tab finishes.</>;
}
return (
<>
<>{[.]}</>
{. === "releasing" ? null : (
< ="button" ={() => void ()}>
Release
</>
)}
< ="status">{}</>
</>
);
}Force a release
Pass force: true to release a payment a tab is still running. That payment then fails at its next
step.
Do not offer it as a normal button. Use it only when Alice says the other tab is gone, but the browser still reports it running. A payment released mid-flight may already have reached the relayer, so it can still land.
Next
Last updated