Invoke and compute-and-invoke
A privacy transaction can end with one call to an external contract, made by the pool after all note operations. There are two variants, and a transaction can include at most one of them:
In both cases the target returns the open-note deposits to fill, and the pool pulls the tokens from the target into the open notes created earlier in the same transaction.
How compute-and-invoke works
- Derive an identity key. Inside the proof, the pool computes
identity_key = H(tag, user address, private viewing key, target address). Only the user can reproduce it, and it is different for every target contract. - Compute. The pool calls the target’s
privacy_computewith[identity_key, ...compute_additional_data]. The target turns the key into a value it can act on—for shadow accounts, a commitment. The result must be non-empty. - Invoke. The pool calls the target’s
privacy_invoke_with_computationwith[...compute_result, ...invoke_additional_data].
privacy_invoke_with_computation must accept calls only from the privacy pool—otherwise anyone could replay it and act as that user.
How shadow accounts work
The shadow account anonymizer contract implements compute-and-invoke. On mainnet it is deployed at0x04f3…88a7.
- Commitment —
privacy_compute(identity_key, dapp_name, nonce)returnsH(H(identity_key, dapp_name), nonce).dapp_namescopes accounts to one dApp;noncelets a user hold several independent accounts for the same dApp. - Deterministic address — the commitment is the deployment salt, so each commitment maps to one address that is known before the account exists.
- Deployed on first use — the first
privacy_invoke_with_computationfor a commitment deploys its shadow account; later calls reuse it. - Runs the calls — the anonymizer executes the transaction’s dApp calls through the shadow account, so the dApp sees the shadow account as the caller.
- Settles into open notes — for each open note, the anonymizer collects tokens from the shadow account according to a collect policy, then approves the pool to pull them into the note.
Each open note must receive a non-zero amount, and each token can appear in at most one open note per invoke.
Who controls a shadow account
Control passes through a chain of single-caller checks:- A shadow account has no keys and cannot sign; it executes calls only for its owner, the anonymizer.
- The anonymizer accepts
privacy_invoke_with_computationonly from the privacy pool. - The pool reaches a shadow account only through a proven transaction, which requires the user’s account signature and the private viewing key the identity key is derived from.
End-to-end flow
A typical shadow-account transaction, in the pool’s fixed action order:- Spend the notes that fund the interaction.
- Create an open note for each token you expect back.
- Withdraw the input tokens to the shadow account’s address. Skip this step when the account already holds what it needs—for example, when closing a position.
- Compute-and-invoke the anonymizer with the
dapp_name, thenonce, the dApp calls, and the open notes to settle. - The anonymizer deploys the shadow account if needed, runs the calls, and returns the collected amounts; the pool fills the open notes.
get_shadow_accounts view takes the partial commitment H(identity_key, dapp_name) and a nonce range (up to 1,024 per call), and returns each account’s address and whether it is deployed.
Privacy properties
Build with shadow accounts
Shadow accounts with the Privacy SDK
Derive commitments and addresses, then run
shadowAccounts(dappName).invoke(nonce, { calls, collectPolicy })Shadow accounts with the Wallet API
Drive shadow accounts from a dApp through a privacy-enabled wallet and starknet.js