stealth-vault

Non-revocable custody for one-time Token-2022 Confidential Transfer accounts. Program id 7oFAWawZCxDivMSa73hGh5V4XnhukLbrZQTA9Sq2r2pk (devnet).

Not on the active path. stealth-vault is the custody design for the Token-2022 Confidential Transfer flow, which is disabled in favour of the note model. The program is deployed and documented because it pioneered the recipient-only ownership gate that note-vault now uses. If you are integrating today, you want note-vault, not this.

The problem it solves

In a naive stealth scheme the one-time account owner is derived from the ECDH shared secret, which the sender also knows, so the sender could sign as that owner and reclaim an unclaimed payment. Token-2022 forbids changing a CT account's owner (SetAuthority returns 0x22), so the fix is structural: the one-time CT account is an ATA owned by a per-output PDA of this program, and every release instruction requires a signature from the recipient-only owner R = spend_pub + t*G. The sender can compute R but cannot sign as it (that needs the recipient's spend scalar), so once funds land, only the recipient can move them. Payments are final; there is deliberately no sender reclaim path.

PDA

PDASeedsPurpose
vault["stealth", R]Owns the one-time CT account. Derived from the recipient-only key R.

The program is stateless: release instructions take r as a signer and derive the PDA from r.key() in the seeds, so a wrong signer simply fails the seeds constraint. There is no stored state and nothing to migrate.

Instructions

init_stealth

Sender side. Creates, reallocates, and configures the one-time CT account owned by the vault PDA for recipient-only owner r (passed as a non-signer here, since the sender initializes). The caller must place the VerifyPubkeyValidity instruction immediately after this one in the same transaction: Token-2022 resolves the configure proof at offset +1 against the top-level instructions via the sysvar.

Args: decryptable_zero_balance: [u8;36], maximum_pending_balance_credit_counter: u64.

Internally it does three hand-encoded Token-2022 CPIs: create the ATA (idempotent), Reallocate for the ConfidentialTransferAccount extension, and ConfigureAccount with proof_instruction_offset = +1.

apply_pending

Recipient side. Applies the pending balance on the stealth account. Only r can sign; the PDA seeds bind to it.

Args: expected_pending_balance_credit_counter: u64, new_decryptable_available_balance: [u8;36].

release_transfer

Recipient side. A confidential transfer from the stealth account to a destination. The three proof context-state accounts (equality, validity, range) are created and verified client-side exactly as a normal CT transfer; the program supplies them and signs the transfer with the vault PDA seeds.

Args: new_source_decryptable_available_balance: [u8;36], auditor_ciphertext_lo: [u8;64], auditor_ciphertext_hi: [u8;64].

Why it is documented

The same recipient-only gate (require r: Signer, derive the PDA from r.key()) is what makes note-vault payments final. If the CT path is ever re-enabled, this program is how live stealth outputs get non-revocable custody without a SetAuthority shortcut.