Deposit

- The user computes an output UTXO and deposits the desired asset into the Shielded Pool contract.
- The contract inserts this UTXO as a leaf in the Merkle tree and records a new Merkle root.
- The contract emits a Shield event together with the updated root.
- At the same time, the user signs the deposit metadata with their viewing key and submits it to the TEE indexer. This signature proves that “this deposit is a request intended by the user,” preventing the risk of incorrect deposit information being transmitted due to MITM attacks or client tampering.
- The TEE indexer compares the signed request with the blockchain transaction contents to verify consistency. Only if the two pieces of information match is the deposit recorded as the user’s UTXO. This structure prevents the indexer from accepting an incorrect client request and assigning an incorrect UTXO to the user.
Transfer

- The user selects the UTXO to use as input, generates transaction metadata, and signs the metadata hash. This signature prevents the prover or indexer from arbitrarily constructing a transaction.
- The user constructs private inputs such as the Merkle proof, token, value, and nullifier key, and sends them to the TEE prover. These inputs are not exposed externally and are used only inside attested TEE instances.
-
The TEE prover generates a Groth16 proof and relays the transaction to the contract along with the input values such as the nullifier, output UTXO, and metadata hash.
The ZK proof includes facts such as:
- The user actually owns the input UTXO
- The Merkle membership of the input UTXO is valid
- The nullifier associated with the UTXO is correctly computed
- Both the input and output UTXOs are correctly computed, and the value is exactly conserved
-
The contract verifies the submitted proof and then applies the state transition.
- It records the corresponding nullifier as spent
- It inserts the output UTXO into the Merkle tree and records a new root
- After proof generation and onchain processing are completed, the TEE prover sends the necessary information from the private input provided by the client to the TEE indexer and requests an update. This is used for indexing the recipient UTXO, providing balance and transaction history information, and audit processes. This information is transferred only through attested TEE-to-TEE communication and is not exposed externally.
- Specific token type / recipient address / amount information required for withdrawal
- Only the input UTXO is nullified, and no additional Merkle tree leaf is created
Forced withdrawal
Forced withdrawal provides a trust-minimized escape hatch that allows users to reclaim their funds even if the TEE becomes unavailable or behaves maliciously (e.g., censoring transactions). Since only designated TEE instances are normally allowed to submit transactions to the shielded pool, the protocol includes this fallback mechanism to ensure full self-custody under all circumstances. The forced withdrawal process operates as follows:- The user scans onchain events and reconstructs their own UTXO set. Using public events emitted by the shielded pool contract, the user identifies which UTXOs belong to them. This step does not require TEE assistance.
- The user locally generates a ZK proof asserting ownership of their UTXOs. The same proving circuit is used, but the proof is created entirely offchain by the user rather than by the TEE prover.
-
The user constructs a forced-withdrawal transaction and submits it directly onchain.
- This transaction bypasses the TEE and bypasses the relayer model.
- The proof asserts that the user legitimately owns the UTXOs and is withdrawing them.
-
The contract verifies the proof and processes the withdrawal.
- The input UTXOs are nullified.
- No new UTXOs are created; only a withdrawal to a public EOA address is allowed. This restriction preserves compliance properties and prevents users from generating untracked notes through the fallback path.
- The user receives the entire balance of their shielded holdings. Since the fallback path cannot create new UTXOs, the user must withdraw all associated UTXOs in full.