Problem
withdraw in contracts/pool/src/lib.rs pays out directly to a recipient address supplied and bound at proof time (recipient_hash_from_address, per docs/THREAT_MODEL.md). This means the recipient must be a specific, known Stellar address at the moment the proof is generated — there's no way to shield-then-gift value to someone who hasn't chosen (or doesn't yet have) a receiving address, or to let the sender construct the payout before the recipient is even involved.
Why it matters
"Send privately to someone who isn't onboarded yet" is a basic gifting/remittance use case competing wallets support. Today it forces a public withdraw to a known address plus an out-of-band handoff, which is exactly the amount/timing leak the shielded design exists to avoid.
Scope
contracts/pool/src/lib.rs: new withdraw_claimable entrypoint that, instead of a direct token::Client::transfer, creates a Stellar claimable balance (via the host's claimable-balance operations) predicated on a claim condition the recipient can satisfy later.
circuits/shielded_pool/src/main.nr: evaluate whether the claim predicate (e.g. a recipient's future address or a hash-preimage claim) needs to be bound into the proof the same way recipient_hash_from_address binds a direct payout today.
frontend/src/lib/stellar.ts: claimable-balance creation/claim helpers.
frontend/src/app/withdraw/page.tsx: new "gift" mode alongside direct withdraw.
- New
frontend/src/app/claim/page.tsx: recipient-side claim UX (paste a claim reference, connect a wallet, claim).
frontend/src/lib/notes.ts: encode/share the claim reference alongside (or instead of) a note secret when gifting.
- Contract tests covering claim, double-claim rejection, and claimant-mismatch rejection.
README.md/DESIGN.md: document the gifting flow and its privacy properties relative to direct withdraw.
Acceptance criteria
- A shielded note can be withdrawn as a claimable balance without specifying a final on-chain recipient address at proof-generation time.
- Only the intended claimant can claim it, and a double-claim attempt is rejected by the contract.
Problem
withdrawincontracts/pool/src/lib.rspays out directly to arecipientaddress supplied and bound at proof time (recipient_hash_from_address, perdocs/THREAT_MODEL.md). This means the recipient must be a specific, known Stellar address at the moment the proof is generated — there's no way to shield-then-gift value to someone who hasn't chosen (or doesn't yet have) a receiving address, or to let the sender construct the payout before the recipient is even involved.Why it matters
"Send privately to someone who isn't onboarded yet" is a basic gifting/remittance use case competing wallets support. Today it forces a public withdraw to a known address plus an out-of-band handoff, which is exactly the amount/timing leak the shielded design exists to avoid.
Scope
contracts/pool/src/lib.rs: newwithdraw_claimableentrypoint that, instead of a directtoken::Client::transfer, creates a Stellar claimable balance (via the host's claimable-balance operations) predicated on a claim condition the recipient can satisfy later.circuits/shielded_pool/src/main.nr: evaluate whether the claim predicate (e.g. a recipient's future address or a hash-preimage claim) needs to be bound into the proof the same wayrecipient_hash_from_addressbinds a direct payout today.frontend/src/lib/stellar.ts: claimable-balance creation/claim helpers.frontend/src/app/withdraw/page.tsx: new "gift" mode alongside direct withdraw.frontend/src/app/claim/page.tsx: recipient-side claim UX (paste a claim reference, connect a wallet, claim).frontend/src/lib/notes.ts: encode/share the claim reference alongside (or instead of) a note secret when gifting.README.md/DESIGN.md: document the gifting flow and its privacy properties relative to direct withdraw.Acceptance criteria