Skip to content

StealthTransfer

Defined in: ootle/src/stealth/transfer.ts:111

Fluent builder for a confidential (stealth) transfer.

Supports the full input/output matrix: spend revealed input (withdraw from a vault) and/or one or more confidential UTXOs (spendStealthInput), emit one or more stealth outputs (+ optional revealed change), and a fee. The balance equation (revealed-in + stealth-in == stealth-out + revealed-out) must hold; the stealth-input value is carried by each input’s mask, recovered by the authorizer.

const spec = await new StealthTransfer(provider, resourceAddress)
.spendRevealedInput(account, 5n * TARI)
.toStealthOutput(createOutput({ destination, amount: 3n * TARI, resourceAddress }))
.toRevealedOutput(1n * TARI, accountPublicKey) // revealed change back to `account`
.payFeeFromRevealed(1n * TARI)
.prepare();

new StealthTransfer(provider, resourceAddress, crypto?): StealthTransfer

Defined in: ootle/src/stealth/transfer.ts:127

Provider

Read-only chain access (used by prepare to resolve inputs).

string

The resource being transferred.

StealthCryptoProvider = ...

Injectable crypto seam; defaults to WasmStealthCrypto. Tests pass a FakeStealthCrypto for WASM-free runs.

StealthTransfer

payFeeFromRevealed(amount): this

Defined in: ootle/src/stealth/transfer.ts:212

Pay the transaction fee from the revealed source account (max amount).

bigint

this

if no source account is set, or amount is not > 0.


prepare(): Promise<StealthTransferSpec>

Defined in: ootle/src/stealth/transfer.ts:305

Assemble the unsigned transaction and the (incomplete) transfer statement.

  1. validate (≥1 input, ≥1 stealth output);
  2. generate the outputs statement (+ aggregated output mask) via the crypto seam;
  3. build the inputs statement (concatenated commitments + revealed input amount) and assemble an incomplete StealthTransferStatement (balanceProof: undefined);
  4. emit instructions: revealed withdraw → saveVar → the native StealthTransfer instruction (+ deposit the revealed-change bucket);
  5. register the revealed source account + every vault it references as tx inputs (otherwise the engine rejects withdraw / pay_fee with SubstateNotFound);
  6. register each stealth input’s UTXO substate id as a tx input so it is resolved / locked alongside the revealed inputs;
  7. resolve substate versions via the provider;
  8. return the StealthTransferSpec.

Stealth-input values are carried by their masks (recovered later by the authorizer), not as amounts — there is no balance-equation check here when stealth inputs are present, because the builder does not know their values; the balance proof (which the authorizer signs over the recovered masks) is the authoritative check.

Promise<StealthTransferSpec>

if the builder is invalid (no inputs, no outputs, no stealth output, unbalanced revealed-only transfer, or revealed change without a revealed source) or prepare has already been called once.


spendRevealedInput(account, amount): this

Defined in: ootle/src/stealth/transfer.ts:156

Withdraw amount of the transfer’s resource from account’s revealed vault.

All revealed input must come from a single account (the fee/change source). Calling this more than once accumulates the amount but the account must be the same.

string

bigint

this

if amount is not > 0, or a different source account is supplied on a second call.


spendStealthInput(ownerAddr, commitment): this

Defined in: ootle/src/stealth/transfer.ts:262

Spend a confidential UTXO owned by ownerAddr, identified by its 32-byte commitment.

Repeatable: each call adds another stealth input. Inputs are keyed by their commitment hex alone and de-duplicated — a second call with the same commitment throws, regardless of the owner label. A commitment uniquely identifies a UTXO within the builder’s single resource and a UTXO has exactly one owner, so the same commitment under two different owner labels is still the same UTXO; accepting it twice would register the substate id twice and emit the commitment twice in the inputs statement — a self-double-spend the engine rejects. The owner address is recorded alongside the input so the authorizer can pick the one-time spend signer for each.

The input’s value contributes to the balance equation via its mask (recovered by the authorizer when it unblinds the UTXO), so the builder does not need the amount here — only the commitment.

string

Uint8Array

this

if commitment is not 32 bytes, or the same commitment is added twice (under any owner).


toRevealedOutput(amount, receiver): this

Defined in: ootle/src/stealth/transfer.ts:187

Add revealed (un-confidential) change, returned to the revealed source account.

receiver is the public key authorised to take the revealed bucket: the engine requires its badge in the transaction’s auth scope, so it must be the key of a signer of this transaction — normally the source account’s owner key. Calling this more than once accumulates the amount but the receiver must be the same.

bigint

Uint8Array

this

if amount is not > 0, receiver is not 32 bytes, or a different receiver is supplied on a second call.


toStealthOutput(spec): this

Defined in: ootle/src/stealth/transfer.ts:171

Add a stealth output to create (repeatable).

Output

this


withBuilder(fn): this

Defined in: ootle/src/stealth/transfer.ts:237

Escape hatch over the inner TransactionBuilder (e.g. extra instructions).

(b) => TransactionBuilder

this


withMaxEpoch(maxEpoch): this

Defined in: ootle/src/stealth/transfer.ts:230

Set the transaction’s validity window explicitly. Without this, prepare reads the current epoch from the provider and applies DEFAULT_TRANSACTION_VALIDITY_EPOCHS.

number

this

if maxEpoch is not a non-negative integer.