# Shinobi Cash > Cross-chain privacy protocol powered by Privacy Pools and zero-knowledge proofs ## FAQ Frequently asked questions about Shinobi Cash. ### General #### What is Shinobi Cash? Shinobi Cash is a cross-chain privacy protocol that enables private transactions across the Ethereum ecosystem. It uses Privacy Pools and zero-knowledge proofs to let you deposit from any chain and withdraw to any chain while maintaining privacy. #### Is this a mixer? Shinobi Cash provides mixer-like privacy outcomes, but uses a different cryptographic model — one that preserves ownership proofs and enables compliance without surveillance. #### Is it safe to use? :::warning[Testnet Only] Shinobi Cash is currently on testnet and has NOT been audited. Use testnet ETH only. Do not deposit real funds. ::: #### What chains are supported? Currently on testnet: * **Arbitrum Sepolia** — Pool chain (where the privacy pool lives) * **Base Sepolia** — Origin chain (deposit/withdraw) More chains planned for mainnet. ### Privacy #### How private are my transactions? When you withdraw: * **Hidden**: Which deposit you're spending, your deposit history, your identity * **Revealed**: That you have a valid deposit, the withdrawal amount, the recipient address #### Can my deposits be linked to my withdrawals? Not by default. The zero-knowledge proof verifies you own a valid deposit without revealing which one. However: * Withdrawing immediately after depositing reduces anonymity * Withdrawing the exact same amount you deposited can be correlated * Using the same recipient address multiple times creates links #### What's an anonymity set? The anonymity set is all the deposits your withdrawal could be from. Larger is better. Shinobi Cash's cross-chain approach creates a larger anonymity set than per-chain solutions. ### Compliance #### How does compliance work? Association Set Providers (ASPs) review deposits and approve compliant ones. Your withdrawal proof must show membership in both the deposit tree AND the ASP tree. #### What if my deposit is rejected? You can "ragequit" — withdraw by revealing you were the depositor. This recovers your funds but removes privacy. #### Do I need to KYC? No. Compliance is enforced at the protocol level via ASPs. You prove membership in a compliant set without revealing your identity. ### Technical #### How long do transactions take? * **Same-chain deposit**: \~1-2 minutes * **Cross-chain deposit**: \~2-5 minutes (solver fill) * **Same-chain withdrawal**: \~1-2 minutes (+ proof generation) * **Cross-chain withdrawal**: \~2-5 minutes (+ proof generation) Proof generation takes 5-15 seconds in your browser. #### Why is proof generation slow? Groth16 SNARK proofs are computationally intensive. The 5-15 second generation time is normal. Keep your browser tab active during generation. #### Do I need ETH in my wallet for gas? No. Withdrawals are relayed via standard ERC-4337 bundlers. A paymaster sponsors the gas upfront, and the cost is deducted from your withdrawal amount (relay fee). No custom relayer infrastructure required. #### What happens if a cross-chain transaction fails? If a solver doesn't fill your intent before the deadline: * Your escrowed funds are returned to the pool as a refund commitment * You can withdraw the refund like any other deposit ### Fees #### What are the fees? :::info[Testnet Configuration] All fees are configurable. These are current testnet values and may change for mainnet. ::: **Deposits:** * Compliance fee: 1% * Solver fee: 5% (cross-chain only) **Withdrawals:** * Relay fee: Up to 15% (covers gas) * Solver fee: 5% (cross-chain only) #### Why are fees high? Fees cover: * Gas costs (sponsored by paymasters) * Solver incentives (for cross-chain settlement) * Protocol sustainability Fees may decrease as the protocol scales. #### Why are cross-chain operations slow? Solver availability on testnet is limited. Cross-chain deposits and withdrawals depend on solvers to fill intents. On mainnet, more solvers will provide faster settlement. ### Troubleshooting #### My deposit isn't showing up 1. Wait for transaction confirmation (\~1-2 blocks) 2. Wait for indexer sync (\~30 seconds) 3. For cross-chain, wait for solver fill (\~1-5 minutes) 4. Try refreshing the page #### Withdrawal failed Common causes: * Note already spent (check another session didn't use it) * ASP approval pending (wait a bit longer) * Proof generation interrupted (try again, keep tab active) #### Transaction stuck For cross-chain transactions, solvers usually fill within minutes. If expired: * Funds return as a refund commitment * Refund appears in your notes * Withdraw the refund normally ### More Questions? * **[Telegram](https://t.me/+tRxvbyeFuDNmMzUx)** — Join the community * **[GitHub](https://github.com/shinobi-cash)** — Report issues ## Quick Start Get started with Shinobi Cash in minutes. ### Prerequisites * A wallet (MetaMask, Rabby, etc.) * Testnet ETH on Arbitrum Sepolia or Base Sepolia :::tip[Get Testnet ETH] Search for "Arbitrum Sepolia faucet" or "Base Sepolia faucet" to find current working faucets. ::: ### Step 1: Connect Your Wallet 1. Visit [testnet.shinobi.cash](https://testnet.shinobi.cash) 2. Click **"Sign in with Wallet"** 3. Connect your wallet and sign the authentication message This signature derives your private keys for the privacy pool — your wallet never has direct access to your deposits. ### Step 2: Make a Deposit 1. Navigate to the **Deposit** tab 2. Select your source chain (Arbitrum Sepolia or Base Sepolia) 3. Enter the amount of ETH to deposit 4. Review the fees and confirm **What happens:** * Your ETH is deposited into the privacy pool * A commitment is added to the Merkle tree * For cross-chain deposits, a solver fills your intent on the pool chain ### Step 3: Wait for Activation After depositing, your funds need to be "activated" before you can withdraw: 1. **On-chain confirmation** — Transaction included in a block 2. **Indexer sync** — Our indexer detects your deposit 3. **ASP approval** — Association Set Provider verifies compliance This typically takes a few minutes. ### Step 4: Withdraw Privately 1. Navigate to the **Withdraw** tab 2. Select a note with available balance 3. Enter the recipient address (can be any address, any supported chain) 4. Select the destination chain 5. Review fees and confirm **What happens:** * A zero-knowledge proof is generated in your browser * The proof verifies you own a valid deposit without revealing which one * For cross-chain withdrawals, a solver fills your intent on the destination chain * Gas is sponsored — you don't need ETH on the destination chain ### Understanding Your Notes In the **Notes** tab, you'll see your deposits organized as "note chains": | Status | Meaning | | ------------- | -------------------------------------- | | **Available** | Ready to withdraw | | **Pending** | Waiting for activation or ASP approval | | **Spent** | Already withdrawn | Each withdrawal creates a "change note" for the remaining balance, similar to how cash transactions work. ### Fees :::info[Testnet Configuration] All fees are configurable. The values below are current testnet settings and may change for mainnet. Solver availability on testnet is limited, so cross-chain operations may be slower than expected. ::: #### Deposit Fees * **Compliance Fee**: 1% (goes to the protocol) * **Solver Fee**: 5% (cross-chain deposits only) #### Withdrawal Fees * **Relay Fee**: Up to 15% (covers gas sponsorship) * **Solver Fee**: 5% (cross-chain withdrawals only) ### Next Steps * **[Privacy Pools](/concepts/privacy-pools)** — Understand how the technology works * **[Cross-Chain Architecture](/concepts/cross-chain)** — Learn about OIF and solvers * **[FAQ](/faq)** — Common questions answered ## @shinobi-cash/constants :::info[Coming Soon] Documentation under development. API not yet stable. ::: ### Overview Contract addresses, ABIs, and chain configurations. ### Source Code [GitHub](https://github.com/shinobi-cash/shinobi.cash-website/tree/main/packages/constants) ## @shinobi-cash/core :::info[Coming Soon] Documentation under development. API not yet stable. ::: ### Overview Core cryptographic primitives and proof generation for Shinobi Cash. ### Source Code [GitHub](https://github.com/shinobi-cash/shinobi.cash-website/tree/main/packages/core) ## @shinobi-cash/data :::info[Coming Soon] Documentation under development. API not yet stable. ::: ### Overview Indexer client and query utilities. ### Source Code [GitHub](https://github.com/shinobi-cash/shinobi.cash-website/tree/main/packages/data) ## SDK :::info[Coming Soon] SDK documentation is under development. The API is not yet stable. ::: ### Packages | Package | Purpose | | ------------------------- | ------------------------------------------ | | `@shinobi-cash/core` | Cryptographic primitives, proof generation | | `@shinobi-cash/constants` | Contract addresses, ABIs, chain configs | | `@shinobi-cash/data` | Indexer client, queries | ### Status The SDK is in early development. APIs may change significantly before mainnet. ### Source Code * [GitHub](https://github.com/shinobi-cash/shinobi.cash-website/tree/main/packages) ## Depositing How to deposit ETH into Shinobi Cash. :::note[Audience] This page is for **Users**. No protocol knowledge required. ::: ### Overview Depositing adds your funds to the privacy pool, creating a commitment that only you can later withdraw. ### Same-Chain Deposit When depositing on the pool chain (Arbitrum Sepolia): 1. **Navigate to Deposit** — Select the Deposit tab 2. **Enter Amount** — Minimum 0.01 ETH 3. **Review Fees** — 1% compliance fee 4. **Confirm** — Sign the transaction in your wallet Your deposit is added directly to the pool's Merkle tree. ### Cross-Chain Deposit When depositing from another chain (e.g., Base Sepolia): :::info[Testnet Note] Fees are configurable (values below are testnet settings). Solver availability on testnet is limited, so cross-chain deposits may take longer. ::: 1. **Select Source Chain** — Choose where your funds are 2. **Enter Amount** — Minimum 0.01 ETH 3. **Review Fees**: * 1% compliance fee * 5% solver fee (for cross-chain) 4. **Confirm** — Sign the transaction #### What Happens Behind the Scenes ``` Your wallet → DepositEntrypoint → InputSettler (escrows funds) │ Solver detects intent ◄───┘ │ ▼ DepositOutputSettler → Pool (commitment inserted) │ Solver claims escrowed funds ``` The solver fronts the capital on the destination chain and claims your escrowed funds after proving the fill. ### After Depositing #### Deposit States | Status | Meaning | | ------------------------- | -------------------------------------------------- | | **Confirming** | Transaction submitted, awaiting block confirmation | | **Confirmed** | On-chain, waiting for indexer | | **Indexed** | Visible in your notes | | **Pending (Cross-chain)** | Waiting for solver to fill | | **Pending (ASP)** | Waiting for compliance approval | | **Available** | Ready to withdraw | #### Activation Time Deposits need to be "activated" before withdrawal: * Same-chain: \~1-2 minutes (indexer sync + ASP) * Cross-chain: \~2-5 minutes (solver fill + indexer + ASP) ### Tips :::tip[Privacy Best Practice] Wait for more deposits after yours before withdrawing. A larger anonymity set means stronger privacy. ::: :::warning[Minimum Amount] The minimum deposit is 0.01 ETH. Smaller amounts are rejected. ::: ### Troubleshooting #### Deposit stuck in "Pending" * **Cross-chain**: Wait for solver — usually fills within minutes * **ASP pending**: Testnet ASP auto-approves; should resolve quickly #### Transaction failed * Check you have enough ETH for gas + deposit amount * Ensure you're on the correct network ### What Can Go Wrong? | Issue | Impact | Recovery | | ----------------------------- | ---------------------------- | ---------------------------------------------------------- | | **ASP rejects deposit** | Cannot withdraw normally | Ragequit (recover funds, lose privacy) | | **Solver doesn't fill** | Cross-chain deposit delayed | Wait for another solver, or intent expires and refunds | | **Browser crash mid-deposit** | Transaction may not submit | Check wallet history; retry if needed | | **Wrong amount** | Funds in pool at that amount | Withdraw in parts or wait for more deposits at same amount | ### Next Steps * **[Withdrawing](/guides/withdraw)** — How to withdraw privately * **[Privacy Pools](/concepts/privacy-pools)** — Understand the technology ## Withdrawing How to withdraw ETH privately from Shinobi Cash. :::note[Audience] This page is for **Users**. No protocol knowledge required. ::: ### Overview Withdrawing uses a zero-knowledge proof to verify you own a deposit without revealing which one. The recipient can be any address on any supported chain. ### Withdrawal Steps 1. **Navigate to Withdraw** — Select the Withdraw tab 2. **Select a Note** — Choose a note with available balance 3. **Enter Amount** — Up to the note's full balance 4. **Enter Recipient** — Any valid Ethereum address 5. **Select Destination Chain** — Where funds should arrive 6. **Review Fees** — Relay fee + solver fee (if cross-chain) 7. **Confirm** — Proof generates in your browser, then submits ### Proof Generation When you confirm a withdrawal, your browser generates a Groth16 SNARK proof: ``` Generating proof... (~5-15 seconds) ``` This proof verifies: * You know the secret for a valid commitment * The commitment is in the state tree * The commitment is in the ASP tree (compliant) * The nullifier hasn't been spent **Keep the tab active** — Proof generation uses your browser's compute resources. ### No Wallet Gas Required Shinobi Cash uses **ERC-4337 Account Abstraction** to relay withdrawals: * You don't need ETH in your wallet for gas * Transaction is relayed via standard ERC-4337 bundlers (no custom relayer) * A paymaster sponsors the gas upfront * Gas cost is deducted from your withdrawal amount (relay fee) This means you can withdraw to a fresh address without needing to fund it with gas first. ### Same-Chain Withdrawal When withdrawing on the pool chain (Arbitrum Sepolia): 1. Proof validates in the pool contract 2. Funds transfer directly to recipient 3. Paymaster sponsors gas **Fees**: Relay fee only (covers gas) ### Cross-Chain Withdrawal When withdrawing to another chain (e.g., Base Sepolia): 1. Proof validates on pool chain 2. Intent created with escrowed funds 3. Solver fills on destination chain 4. Recipient receives funds 5. Solver claims escrow after proving fill **Fees**: Relay fee + 5% solver fee #### What Happens Behind the Scenes ``` Your browser generates ZK proof │ ▼ Entrypoint validates → Pool spends nullifier → InputSettler (escrow) │ Solver detects ◄────┘ │ ▼ WithdrawalOutputSettler → Recipient gets ETH │ Solver claims escrow ``` ### Change Notes If you don't withdraw the full balance, a **change note** is created: ``` Original Note: 1.0 ETH Withdrawal: 0.3 ETH Change Note: 0.7 ETH (minus fees) ``` This mirrors how cash transactions work — you get change back. ### Fee Structure :::info[Testnet Values] Fees are configurable. Values below are testnet settings. Solver availability on testnet is limited. ::: | Fee | Amount | When | | -------------- | --------- | ------------------- | | **Relay Fee** | Up to 15% | Always (covers gas) | | **Solver Fee** | 5% | Cross-chain only | The relay fee is dynamic based on gas prices. Maximum 15%. ### Withdrawal States | Status | Meaning | | -------------- | ------------------------------ | | **Preparing** | Generating ZK proof | | **Submitting** | Transaction being sent | | **Confirming** | Waiting for block confirmation | | **Confirmed** | On-chain, waiting for indexer | | **Indexed** | Complete | ### Tips :::tip[Privacy Best Practice] Withdraw to a fresh address that has no connection to your depositing address. ::: :::tip[Partial Withdrawals] You can make multiple partial withdrawals from a single deposit. Each creates a change note. ::: ### Troubleshooting #### Proof generation is slow * Keep the browser tab active and focused * Close other resource-heavy tabs * Normal time: 5-15 seconds #### Transaction failed * Check the note is still available (not spent elsewhere) * Verify recipient address is valid * Ensure ASP approval is complete #### Cross-chain withdrawal stuck * Solvers usually fill within minutes * If expired, funds return as a refund commitment * Refund can be withdrawn normally ### What Can Go Wrong? | Issue | Impact | Recovery | | ------------------------------------- | --------------------------- | -------------------------------------------------- | | **Browser crash during proof gen** | Proof not generated | Retry; no funds lost | | **Transaction reverts** | Withdrawal fails | Check error; retry with valid note | | **Solver doesn't fill (cross-chain)** | Withdrawal delayed | Intent expires → refund commitment created | | **Wrong recipient address** | Funds sent to wrong address | **Unrecoverable** — double-check before confirming | | **Note already spent** | Transaction fails | Note was used in another session/device | :::warning[Recipient Address] Always double-check the recipient address. Withdrawals to wrong addresses **cannot be recovered**. ::: ### Next Steps * **[Privacy Pools](/concepts/privacy-pools)** — Understand the cryptography * **[Cross-Chain Architecture](/concepts/cross-chain)** — How OIF works * **[FAQ](/faq)** — Common questions ## ShinobiCashEntrypoint :::note[Audience] This page is for **Developers and Security Reviewers**. ::: The central orchestrator for cross-chain operations on the pool chain (Arbitrum Sepolia). ### Overview `ShinobiCashEntrypoint` inherits from: * `Entrypoint` (privacy-pools-core) * `ShinobiCashCrosschainState` (cross-chain configuration) * `IShinobiCashCrossChainHandler` (handler interface) It coordinates all cross-chain deposits and withdrawals, manages settler addresses, and handles refunds for failed intents. ### State Variables ```solidity // Settler addresses address public withdrawalInputSettler; // For withdrawal intents address public depositOutputSettler; // For deposit fills // Per-chain destination configuration mapping(uint256 => WithdrawalChainConfig) public withdrawalChainConfig; // Precommitment replay prevention mapping(uint256 => bool) public usedPrecommitments; ``` ### WithdrawalChainConfig Configuration for each supported destination chain: ```solidity struct WithdrawalChainConfig { bool isConfigured; // Whether this destination is active uint32 fillDeadline; // Default fill deadline (seconds from now) uint32 expiry; // Default expiry (seconds from now) address withdrawalOutputSettler; // ShinobiWithdrawalOutputSettler on destination address withdrawalFillOracle; // Output oracle on destination chain address fillOracle; // Fill oracle for validating fills (dest → origin) } ``` ### Key Functions #### crosschainWithdrawal Main entry point for cross-chain withdrawals. Validates the ZK proof and creates an OIF intent. ```solidity function crosschainWithdrawal( IPrivacyPool.Withdrawal calldata _withdrawal, CrossChainProofLib.CrossChainWithdrawProof calldata _proof, uint256 _scope ) external nonReentrant ``` **Flow:** 1. Validate `withdrawalInputSettler` is configured 2. Validate withdrawn amount > 0 3. Validate processooor is this entrypoint 4. Fetch pool by scope, validate exists 5. Decode `CrossChainRelayData` from withdrawal.data 6. Validate destination chain is configured 7. Validate relay fee ≤ max allowed 8. Execute `pool.crosschainWithdraw()` (validates ZK proof) 9. Calculate fees (relay + solver) 10. Create OIF intent with configured destination settings 11. Transfer relay fee to feeRecipient 12. Open intent on `ShinobiInputSettler` (escrows funds) 13. Emit `CrossChainWithdrawalIntentRelayed` event #### crosschainDeposit Handles verified cross-chain deposits. Called by `ShinobiDepositOutputSettler` after oracle validation. ```solidity function crosschainDeposit( address _depositor, uint256 _amount, uint256 _precommitment ) external payable nonReentrant onlyDepositOutputSettler ``` **Flow:** 1. Validate caller is the configured `depositOutputSettler` 2. Validate precommitment hasn't been used (replay prevention) 3. Mark precommitment as used 4. Insert commitment into the pool's Merkle tree 5. Emit deposit event #### handleRefund Handles refunds for failed cross-chain withdrawals. Called by `ShinobiInputSettler` when an intent expires. ```solidity function handleRefund( uint256 _refundCommitmentHash, uint256 _amount, uint256 _scope ) external payable onlyWithdrawalInputSettler ``` **Flow:** 1. Validate caller is the configured `withdrawalInputSettler` 2. Fetch pool by scope 3. Call `pool.handleRefund()` to insert refund commitment 4. User can later withdraw the refund normally #### setWithdrawalChainConfig Configure a destination chain for cross-chain withdrawals. ```solidity function setWithdrawalChainConfig( uint256 _chainId, address _outputSettler, address _outputOracle, address _fillOracle, uint32 _fillDeadline, uint32 _expiry ) external onlyRole(_OWNER_ROLE) ``` ### Access Control | Function | Access | | --------------------------- | -------------------------------- | | `crosschainWithdrawal` | Public (anyone with valid proof) | | `crosschainDeposit` | `onlyDepositOutputSettler` | | `handleRefund` | `onlyWithdrawalInputSettler` | | `setWithdrawalChainConfig` | `onlyRole(_OWNER_ROLE)` | | `setWithdrawalInputSettler` | `onlyRole(_OWNER_ROLE)` | | `setDepositOutputSettler` | `onlyRole(_OWNER_ROLE)` | ### Events ```solidity event CrossChainWithdrawalIntentRelayed( bytes32 indexed orderId, address indexed recipient, uint256 destinationChainId, uint256 amount, uint256 solverFee ); event CrossChainDepositProcessed( address indexed depositor, uint256 amount, uint256 precommitment ); ``` ### Security Considerations 1. **Proof validation** — ZK proof is validated in the pool before intent creation 2. **Replay prevention** — Precommitments are marked as used 3. **Access control** — Only authorized settlers can call deposit/refund handlers 4. **Reentrancy protection** — All entry points use `nonReentrant` ### Source Code [View on GitHub](https://github.com/shinobi-cash/shinobi.cash-contracts/blob/main/src/core/ShinobiCashEntrypoint.sol) ### Related * **[Privacy Pool](/contracts/pool)** — Pool contract that validates proofs * **[OIF Settlers](/contracts/settlers)** — Settlement contracts * **[Cross-Chain Architecture](/concepts/cross-chain)** — How cross-chain works ## Smart Contracts Overview of Shinobi Cash smart contract architecture. ### Contract Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ Pool Chain (Arbitrum) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ ShinobiCashEntrypoint│ │ ShinobiCashPool │ │ │ │ (orchestrator) │───►│ (privacy pool) │ │ │ └─────────────────────┘ └─────────────────────┘ │ │ │ │ │ │ │ │ ┌────────▼────────┐ ┌─────────────────────────┐ │ │ │ShinobiInputSettler│ │ShinobiDepositOutputSettler│ │ │ │ (escrow/refund) │ │ (deposit fills) │ │ │ └─────────────────┘ └─────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ Paymasters (ERC-4337) │ │ │ │ SimpleShinobiCashPoolPaymaster │ │ │ │ CrossChainWithdrawalPaymaster │ │ │ └─────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ │ Origin Chain (Base) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────────────────────┐ │ │ │ShinobiCrosschainDepositEntrypoint│ │ │ │ (deposit interface) │ │ │ └───────────────────────────────┘ │ │ │ │ │ ┌────────▼────────┐ ┌─────────────────────────┐ │ │ │ShinobiInputSettler│ │ShinobiWithdrawalOutputSettler│ │ │ │ (escrow/refund) │ │ (withdrawal fills) │ │ │ └─────────────────┘ └─────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ### Core Contracts #### ShinobiCashEntrypoint The central orchestrator on the pool chain. Handles: * Cross-chain withdrawal intent creation * Cross-chain deposit processing * Refund handling for failed intents [Learn more →](/contracts/entrypoint) #### ShinobiCashPool Extended Privacy Pool with cross-chain support. Handles: * Commitment insertion (deposits) * Nullifier spending (withdrawals) * ZK proof verification (9-signal cross-chain proofs) [Learn more →](/contracts/pool) ### OIF Settlers #### ShinobiInputSettler Manages intent creation and escrow on origin chains: * Escrows funds when intent is opened * Releases to solver after fill proof * Refunds on expiry #### ShinobiDepositOutputSettler Handles deposit fills on the pool chain: * **Mandatory** intentOracle validation * Calls `crosschainDeposit()` on entrypoint #### ShinobiWithdrawalOutputSettler Handles withdrawal fills on destination chains: * **Optimistic** settlement (ZK proof already validated) * Simple ETH transfer to recipient [Learn more →](/contracts/settlers) ### Paymasters ERC-4337 paymasters that enable withdrawals without wallet gas: #### SimpleShinobiCashPoolPaymaster Sponsors gas for same-chain withdrawals. Embeds ZK proof validation. #### CrossChainWithdrawalPaymaster Sponsors gas for cross-chain withdrawals. Validates 9-signal proofs. [Learn more →](/contracts/paymasters) ### Key Addresses (Testnet) :::info[Testnet Addresses] See the [constants package](https://github.com/shinobi-cash/shinobi.cash-website/tree/main/packages/constants) for current deployed addresses. ::: ### Dependencies | Package | Purpose | | ---------------------- | -------------------------------- | | `privacy-pools-core` | Base privacy pool implementation | | `oif-contracts` | Open Intent Framework | | `account-abstraction` | ERC-4337 implementation | | `@zk-kit/lean-imt.sol` | Incremental Merkle Tree | | `poseidon-solidity` | Poseidon hash function | ### Next Steps * **[Entrypoint](/contracts/entrypoint)** — Central orchestrator details * **[Privacy Pool](/contracts/pool)** — Pool contract details * **[OIF Settlers](/contracts/settlers)** — Settlement contracts * **[Paymasters](/contracts/paymasters)** — Gas sponsorship ## Paymasters :::note[Audience] This page is for **Developers and Security Reviewers**. ::: ERC-4337 paymasters that enable private withdrawals without requiring wallet gas. ### Overview Shinobi Cash paymasters: * **Sponsor gas upfront** — Users don't need ETH in their wallet * **Validate ZK proofs** — Embedded proof validation, no external calls * **Deduct relay fees** — Gas cost covered by the withdrawal amount * **Use standard AA infra** — Works with any ERC-4337 bundler ### Contracts | Contract | Purpose | | -------------------------------- | ----------------------------------------- | | `SimpleShinobiCashPoolPaymaster` | Same-chain withdrawals (8-signal proofs) | | `CrossChainWithdrawalPaymaster` | Cross-chain withdrawals (9-signal proofs) | ### SimpleShinobiCashPoolPaymaster #### Gas Limits ```solidity uint256 public constant POST_OP_GAS_LIMIT = 100_000; uint256 public constant MIN_CALL_GAS_LIMIT = 550_000; uint256 public constant MIN_PAYMASTER_VERIFICATION_GAS = 400_000; ``` #### Validation Flow The paymaster validates UserOperations in `_validatePaymasterUserOp`: ``` 1. Check expectedSmartAccount is configured 2. Verify UserOp from expected smart account 3. Ensure no initCode (account already deployed) 4. Check gas limits are sufficient: - postOp gas limit >= POST_OP_GAS_LIMIT (100,000) - call gas limit >= MIN_CALL_GAS_LIMIT (550,000) - paymaster verification gas >= MIN_PAYMASTER_VERIFICATION_GAS (400,000) 5. Extract SimpleAccount.execute(target, value, data) 6. Call internal relay() to validate withdrawal: a. Verify processooor is SHINOBI_CASH_ENTRYPOINT b. Decode RelayData, verify feeRecipient is this paymaster c. Verify scope matches ETH_CASH_POOL.SCOPE() d. Validate ZK proof (context, tree depths, roots, nullifier, Groth16) e. Store values in transient storage 7. Validate economics: expectedFee >= maxCost 8. Return context with userOpHash, recipient, expectedFee ``` #### Embedded Relay Method The paymaster calls itself to validate the withdrawal without executing it: ```solidity /// Called by paymaster itself to validate withdrawal /// External function that checks msg.sender == address(this) function relay( IPrivacyPool.Withdrawal calldata withdrawal, ProofLib.WithdrawProof calldata proof, uint256 scope ) external { if (msg.sender != address(this)) revert UnauthorizedCaller(); // Validate processooor, feeRecipient, scope // Verify ZK proof via _validateWithdrawCall() // Store in transient storage for economic checks assembly { tstore(0, withdrawnValue) tstore(1, relayFeeBPS) tstore(2, withdrawalRecipient) } } ``` #### Transient Storage (EIP-1153) The paymaster uses transient storage for gas efficiency: ```solidity // Store in validation assembly { tstore(0, withdrawnValue) tstore(1, relayFeeBPS) tstore(2, withdrawalRecipient) } // Read in postOp uint256 withdrawnValue; assembly { withdrawnValue := tload(0) } // Clear after use assembly { tstore(0, 0) } ``` #### PostOp Handling After execution, `_postOp` refunds excess fees to the user: ```solidity function _postOp( PostOpMode mode, bytes calldata context, uint256 actualGasCost, uint256 actualUserOpFeePerGas ) internal override { // Calculate actual cost uint256 actualCost = actualGasCost + (POST_OP_GAS_LIMIT * actualUserOpFeePerGas); // Refund excess to recipient uint256 excess = expectedFee - actualCost; if (excess > 0) { (bool success, ) = recipient.call{value: excess}(""); // Handle refund result } } ``` ### CrossChainWithdrawalPaymaster Similar to `SimpleShinobiCashPoolPaymaster` but validates 9-signal cross-chain proofs. #### Gas Limits Higher limits for cross-chain operations: ```solidity uint256 public constant MIN_CALL_GAS_LIMIT = 687_500; uint256 public constant MIN_PAYMASTER_VERIFICATION_GAS = 500_000; ``` #### Key Differences 1. Uses `CrossChainWithdrawalVerifier` instead of standard verifier 2. Validates 9-signal proofs (includes refundCommitmentHash) 3. Higher gas limits for intent creation ### Fee Structure #### RelayData ```solidity struct RelayData { address feeRecipient; // Paymaster address (receives relay fee) uint256 relayFeeBPS; // Basis points (e.g., 1500 = 15%) } ``` #### Economics Validation ```solidity // Calculate expected fee from withdrawal uint256 expectedFee = (withdrawnValue * relayFeeBPS) / 10000; // Validate it covers gas cost if (expectedFee < maxCost) revert InsufficientRelayFee(); ``` ### Security Considerations #### 1. Expected Smart Account Only UserOps from a pre-configured smart account are accepted: ```solidity if (userOp.sender != expectedSmartAccount) revert UnauthorizedAccount(); ``` #### 2. No InitCode Prevents deployment cost attacks: ```solidity if (userOp.initCode.length != 0) revert InitCodeNotAllowed(); ``` #### 3. Embedded Proof Validation The paymaster validates proofs internally rather than trusting external calls: ```solidity // Call self to validate (protected by msg.sender check) (bool success, ) = address(this).call( abi.encodeCall(this.relay, (withdrawal, proof, scope)) ); ``` #### 4. Economics Validation Ensures the relay fee covers actual gas costs: ```solidity if (expectedFee < maxCost) revert InsufficientRelayFee(); ``` ### Why Embedded Validation? The paymaster embeds ZK proof validation rather than trusting the pool contract because: 1. **Security** — Bundlers could submit malicious UserOps 2. **Atomicity** — Validation must happen before execution 3. **Gas efficiency** — Transient storage avoids re-validation in postOp ### Source Code * [SimpleShinobiCashPoolPaymaster](https://github.com/shinobi-cash/shinobi.cash-contracts/blob/main/src/paymaster/SimpleShinobiCashPoolPaymaster.sol) * [CrossChainWithdrawalPaymaster](https://github.com/shinobi-cash/shinobi.cash-contracts/blob/main/src/paymaster/CrossChainWithdrawalPaymaster.sol) ### Related * **[Entrypoint](/contracts/entrypoint)** — Withdrawal orchestration * **[Privacy Pool](/contracts/pool)** — Proof validation * **[Trust Assumptions](/concepts/trust-assumptions)** — Bundler trust model ## ShinobiCashPool :::note[Audience] This page is for **Developers and Security Reviewers**. ::: Extended Privacy Pool with cross-chain withdrawal support and refund handling. ### Overview `ShinobiCashPool` is an abstract contract that extends the base `PrivacyPool` from privacy-pools-core. It adds: * Cross-chain withdrawal with 9-signal proofs * Refund commitment handling for failed intents ### Inheritance ``` PrivacyPool (privacy-pools-core) └── ShinobiCashPool (abstract) └── ShinobiCashPoolSimple (native ETH implementation) ``` ### State Variables ```solidity // Cross-chain proof verifier ICrossChainWithdrawalProofVerifier public immutable CROSS_CHAIN_WITHDRAWAL_VERIFIER; ``` The pool also inherits state from `PrivacyPool`: * Merkle tree for commitments * Nullifier registry (spent nullifiers) * Root history for proof validation * ASP (Association Set Provider) root ### Key Functions #### crosschainWithdraw Process a cross-chain withdrawal with an enhanced 9-signal proof. ```solidity function crosschainWithdraw( Withdrawal memory _withdrawal, CrossChainProofLib.CrossChainWithdrawProof memory _proof ) external ``` **Parameters:** * `_withdrawal` — Withdrawal request (recipient, amount, processooor, data) * `_proof` — 9-signal Groth16 proof **Validation Flow:** 1. Validate processooor (msg.sender matches expected) 2. Validate context matches `hash(withdrawal, SCOPE) % SNARK_SCALAR_FIELD` 3. Validate tree depths within bounds 4. Validate state root is in recent history (`ROOT_HISTORY_SIZE`) 5. Validate ASP root is the latest 6. Verify Groth16 proof via `CROSS_CHAIN_WITHDRAWAL_VERIFIER` 7. Spend nullifier (mark as used) 8. Insert new commitment (change note) 9. Transfer withdrawn value to processooor 10. Emit `CrosschainWithdrawn` event #### handleRefund Insert a refund commitment for a failed cross-chain withdrawal. ```solidity function handleRefund( uint256 _refundCommitmentHash, uint256 _amount ) external payable onlyEntrypoint ``` **Parameters:** * `_refundCommitmentHash` — The refund commitment (9th proof signal) * `_amount` — Amount being refunded **Flow:** 1. Validate caller is the entrypoint 2. Validate `msg.value` matches `_amount` 3. Insert `_refundCommitmentHash` into the Merkle tree 4. User can later withdraw this commitment normally ### 9-Signal Proof Structure Cross-chain withdrawals use an enhanced proof with 9 public signals: ```solidity struct CrossChainWithdrawProof { uint256[2] pA; uint256[2][2] pB; uint256[2] pC; uint256[9] pubSignals; } ``` | Index | Signal | Description | | ----- | ----------------------- | --------------------------------- | | 0 | `newCommitmentHash` | Change note commitment | | 1 | `existingNullifierHash` | Spent deposit nullifier | | 2 | `refundCommitmentHash` | Fallback if intent fails | | 3 | `withdrawnValue` | Amount being withdrawn | | 4 | `stateRoot` | Merkle root of deposits | | 5 | `stateTreeDepth` | Depth of state tree | | 6 | `ASPRoot` | Merkle root of ASP set | | 7 | `ASPTreeDepth` | Depth of ASP tree | | 8 | `context` | Binding context (prevents replay) | The 9th signal (`refundCommitmentHash`) is the key addition for cross-chain — it enables fund recovery if the intent expires without being filled. ### Standard vs Cross-Chain Proofs | Feature | Standard (8 signals) | Cross-Chain (9 signals) | | ----------------- | ---------------------- | ----------------------- | | Change note | ✅ | ✅ | | Nullifier | ✅ | ✅ | | Refund commitment | ❌ | ✅ | | Used for | Same-chain withdrawals | Cross-chain withdrawals | ### ShinobiCashPoolSimple The concrete implementation for native ETH: ```solidity contract ShinobiCashPoolSimple is ShinobiCashPool { function _pull(address, uint256 _amount) internal override { // Pull ETH from msg.sender (via msg.value) } function _push(address _to, uint256 _amount) internal override { // Push ETH to recipient (bool success, ) = _to.call{value: _amount}(""); require(success, "ETH transfer failed"); } } ``` ### Context Validation The context binds the proof to specific withdrawal parameters: ```solidity uint256 expectedContext = uint256( keccak256(abi.encode(_withdrawal, SCOPE)) ) % SNARK_SCALAR_FIELD; if (proof.context() != expectedContext) revert ContextMismatch(); ``` This prevents proof replay across different withdrawals or pools. ### Root History The pool maintains a history of recent Merkle roots: ```solidity uint32 public constant ROOT_HISTORY_SIZE = 30; mapping(uint32 => uint256) public roots; uint32 public currentRootIndex; ``` Proofs can use any root from the last 30 insertions, allowing for concurrent proof generation. ### Security Considerations 1. **Nullifier uniqueness** — Each nullifier can only be spent once 2. **Root validation** — State root must be in recent history 3. **ASP enforcement** — ASP root must be the latest (no stale proofs) 4. **Context binding** — Proofs cannot be replayed 5. **Entrypoint-only refunds** — Only the entrypoint can insert refund commitments ### Source Code [View on GitHub](https://github.com/shinobi-cash/shinobi.cash-contracts/blob/main/src/core/ShinobiCashPool.sol) ### Related * **[Entrypoint](/contracts/entrypoint)** — Orchestrator that calls the pool * **[Privacy Pools](/concepts/privacy-pools)** — Cryptographic foundations * **[OIF Settlers](/contracts/settlers)** — Cross-chain settlement ## OIF Settlers :::note[Audience] This page is for **Developers and Security Reviewers**. ::: Open Intent Framework settlers manage cross-chain intent creation, escrow, and settlement. ### Overview Shinobi Cash uses three settler contracts: | Contract | Chain | Purpose | | -------------------------------- | --------------- | ------------------------------------- | | `ShinobiInputSettler` | Origin | Escrows funds, handles claims/refunds | | `ShinobiDepositOutputSettler` | Pool (Arbitrum) | Fills deposit intents | | `ShinobiWithdrawalOutputSettler` | Destination | Fills withdrawal intents | ### Intent Structure ```solidity struct ShinobiIntent { // Base OIF StandardOrder Fields address user; // Intent creator (verified on origin) uint256 nonce; // Unique identifier component uint256 originChainId; // Chain where intent was created uint32 expires; // Expiry timestamp for refunds uint32 fillDeadline; // Deadline for filling address fillOracle; // Oracle for fill proof validation uint256[2][] inputs; // Input tokens [tokenId, amount][] MandateOutput[] outputs;// Outputs to fill on destination // Shinobi Extensions address intentOracle; // Oracle for intent proof validation bytes refundCalldata; // Custom refund logic } ``` ### ShinobiInputSettler Manages intent creation and escrow on origin chains. #### State ```solidity address public immutable entrypoint; // Only caller for open() mapping(bytes32 => OrderStatus) public orderStatus; enum OrderStatus { None, // Order doesn't exist Deposited, // Funds escrowed, awaiting fill or expiry Claimed, // Solver filled and claimed Refunded // Intent expired and refunded } ``` #### Functions ##### open Create an intent and escrow funds. Only callable by the entrypoint. ```solidity function open(ShinobiIntent calldata intent) external payable ``` ##### finalise Release escrowed funds to the solver after proving the fill. ```solidity function finalise( ShinobiIntent calldata intent, SolveParams[] calldata solveParams, bytes32 destination ) external ``` **Oracle Validation:** ``` For each output: 1. Check fill timestamp <= fillDeadline 2. Build payloadHash = keccak256(solver | orderId | timestamp | output) 3. Pack into proofSeries: [chainId, oracle, settler, payloadHash] 4. Call fillOracle.efficientRequireProven(proofSeries) ``` ##### refund Refund expired intent. Permissionless — anyone can trigger for expired intents. ```solidity function refund(ShinobiIntent calldata intent) external ``` #### State Machine ``` ┌─────────────┐ │ None │ └──────┬──────┘ │ open() ┌──────▼──────┐ │ Deposited │ └──────┬──────┘ ┌──────────┴──────────┐ │ │ finalise() refund() │ │ ┌──────▼──────┐ ┌──────▼──────┐ │ Claimed │ │ Refunded │ └─────────────┘ └─────────────┘ ``` ### ShinobiDepositOutputSettler Handles deposit fills on the pool chain. #### Key Difference: Mandatory Intent Validation Deposits **require** intentOracle validation to prevent depositor address spoofing: ```solidity address public immutable intentOracle; // MUST be used for all deposits function fill(ShinobiIntent calldata intent) external payable nonReentrant { // 1. Validate deposits have exactly one output if (intent.outputs.length != 1) revert InvalidOutput(); // 2. Validate correct destination chain if (intent.outputs[0].chainId != block.chainid) revert InvalidChain(); // 3. Validate fill deadline if (block.timestamp > intent.fillDeadline) revert FillDeadlinePassed(); // 4. CRITICAL: Validate intent uses configured oracle if (intent.intentOracle != intentOracle) revert IntentOracleMismatch(); // 5. Compute unique order identifier bytes32 orderId = intent.orderIdentifier(); // 6. CRITICAL: Validate intent proof via oracle if (!IInputOracle(intentOracle).isProven( intent.originChainId, bytes32(uint256(uint160(intentOracle))), bytes32(uint256(uint160(address(this)))), orderId )) revert IntentNotProven(); // 7. Fill output with callback to crosschainDeposit() _fillOutput(orderId, intent.outputs[0], msg.sender); } ``` **Why intentOracle is required:** Without this, an attacker could create fake intents claiming any depositor address. The oracle proves the intent originated from a legitimate user on the origin chain. ### ShinobiWithdrawalOutputSettler Handles withdrawal fills on destination chains. #### Key Difference: Optimistic Settlement Withdrawals use **optimistic** settlement — NO intent proof validation: ```solidity address public immutable fillOracle; // Validated for consistency only function fill(ShinobiIntent calldata intent) external payable nonReentrant { // 1. Validate has outputs if (intent.outputs.length == 0) revert InvalidOutput(); // 2. Validate correct destination chain if (intent.outputs[0].chainId != block.chainid) revert InvalidChain(); // 3. Validate fill deadline if (block.timestamp > intent.fillDeadline) revert FillDeadlinePassed(); // 4. Validate fillOracle for consistency if (intent.fillOracle != fillOracle) revert FillOracleMismatch(); // 5. Compute unique order identifier bytes32 orderId = intent.orderIdentifier(); // NO intentOracle validation - ZK proof already validated on origin // 6. Fill each output with simple ETH transfer for (uint256 i = 0; i < intent.outputs.length; i++) { _fillOutput(orderId, intent.outputs[i], msg.sender); } } ``` **Why intentOracle is NOT required:** The ZK proof on the origin chain already validated: * User owns a valid deposit (nullifier) * User knows the secret (commitment membership) * Withdrawal parameters are bound to the proof (context) The intent was created by the trusted `ShinobiCashEntrypoint` after proof validation. ### Oracle System Two oracles ensure secure cross-chain settlement: | Oracle | Direction | Purpose | Required For | | -------------- | ------------- | --------------------------- | ------------------- | | `fillOracle` | Dest → Origin | Proves the fill happened | All intents (claim) | | `intentOracle` | Origin → Dest | Proves intent is legitimate | Deposits only | ### Security Comparison | Feature | Deposits | Withdrawals | | ----------------- | ---------------------------------- | ------------------------- | | Intent validation | Required (intentOracle) | Not required | | Why | Depositor address could be spoofed | ZK proof validates user | | Fill validation | fillOracle | fillOracle | | Refund mechanism | Standard ETH refund | Refund commitment in pool | ### Source Code * [ShinobiInputSettler](https://github.com/shinobi-cash/shinobi.cash-contracts/blob/main/src/oif/ShinobiInputSettler.sol) * [ShinobiDepositOutputSettler](https://github.com/shinobi-cash/shinobi.cash-contracts/blob/main/src/oif/ShinobiDepositOutputSettler.sol) * [ShinobiWithdrawalOutputSettler](https://github.com/shinobi-cash/shinobi.cash-contracts/blob/main/src/oif/ShinobiWithdrawalOutputSettler.sol) ### Related * **[Entrypoint](/contracts/entrypoint)** — Creates intents * **[Cross-Chain Architecture](/concepts/cross-chain)** — How OIF works * **[Trust Assumptions](/concepts/trust-assumptions)** — Solver trust model ## Compliance How Shinobi Cash enables privacy without enabling illicit activity. :::note[Audience] This page is for **Users, Developers, and Regulators** seeking to understand how Shinobi Cash balances privacy with compliance. ::: :::info[TL;DR] Association Set Providers (ASPs) review deposits and approve compliant ones. Withdrawals prove ASP membership **without revealing which deposit**. Privacy is preserved; compliance is verifiable. ::: ### The Compliance Problem Traditional mixers face a fundamental tension: * **Full privacy** → Can't prove funds are clean → Regulatory issues * **Full transparency** → No privacy → Defeats the purpose ### Association Set Providers (ASPs) Shinobi Cash solves this with **Association Set Providers** — trusted entities that curate sets of "compliant" deposits. #### How ASPs Work 1. **Deposit occurs** — Commitment added to the state tree 2. **ASP reviews** — Provider analyzes the deposit source 3. **Approval/Rejection** — If compliant, commitment added to ASP tree 4. **Withdrawal proof** — User proves membership in both state AND ASP trees #### What This Enables **Prove compliance without revealing identity:** > "My funds came from a compliant source (I'm in the ASP set), but I won't tell you which specific deposit is mine." This is the key innovation — compliance without surveillance. #### What Compliance Does NOT Mean * ❌ ASPs do **not** know which withdrawal belongs to which deposit * ❌ ASPs **cannot** retroactively deanonymize past withdrawals * ❌ ASPs **cannot** seize or redirect funds * ❌ ASPs **cannot** see your wallet address or identity ASPs only see: "This deposit source appears compliant" → approve/reject. ### ASP Approval States | State | Meaning | Can Withdraw? | | ------------ | -------------------- | ------------- | | **Pending** | Awaiting ASP review | No | | **Approved** | In the compliant set | Yes | | **Rejected** | Not approved by ASP | Only ragequit | #### Ragequit If your deposit is rejected by the ASP, you can still recover your funds through a "ragequit" — a withdrawal that reveals you were the depositor. This ensures users are never trapped, but removes privacy for rejected deposits. ### Zero-Knowledge Compliance The withdrawal proof includes ASP membership: ``` Proof verifies: 1. Commitment exists in state tree (valid deposit) 2. Commitment exists in ASP tree (compliant deposit) 3. You know the secret (ownership) ``` The verifier learns that your deposit is compliant but not which deposit it is. ### Current Implementation :::info[Testnet Note] On testnet, a mock ASP automatically approves all deposits. Production will use real ASP providers with actual compliance review. ::: ### Benefits #### For Users * **Privacy preserved** — No one knows which deposit is yours * **Regulatory comfort** — Can prove funds are clean if needed * **Self-custody** — You control your keys and funds #### For Regulators * **Compliance visible** — All withdrawals prove ASP membership * **Bad actors excluded** — Rejected deposits can't mix with compliant ones * **Audit trail** — ASP decisions are on-chain #### For Society * **Financial privacy** — A human right, not just for criminals * **Legitimate use cases** — Salary privacy, donations, business transactions * **Balanced approach** — Privacy AND accountability ### Learn More * **[Privacy Pools Paper](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4563364)** — The research behind ASPs * **[Privacy Pools](/concepts/privacy-pools)** — Core cryptographic primitives * **[Smart Contracts](/contracts/)** — Implementation details ## Cross-Chain Architecture How Shinobi Cash enables privacy across multiple chains. :::note[Audience] This page is for **Developers and Security Reviewers**. For user guides, see [Deposit](/guides/deposit) and [Withdraw](/guides/withdraw). ::: :::info[TL;DR] One pool lives on Arbitrum. Deposits and withdrawals from other chains are expressed as **intents** and fulfilled by **solvers**. Funds are always escrowed or provably recoverable. A malicious solver can delay but **cannot steal**. ::: ### Key Terms | Term | Meaning | | --------------------- | -------------------------------------------------- | | **Intent** | A signed request to move funds cross-chain | | **Solver** | An off-chain actor that fulfills intents for a fee | | **Escrow** | Funds locked until intent is filled or expires | | **Refund Commitment** | Fallback if intent expires — funds return to pool | ### The Problem Traditional privacy solutions deploy separate pools on each chain. This creates fragmented anonymity sets — each pool has fewer users, weakening privacy. ### The Solution Shinobi Cash maintains a **single privacy pool** on one chain (Arbitrum Sepolia) while accepting deposits and withdrawals from any supported chain. ``` ┌─────────────────┐ ┌─────────────────┐ │ Base Sepolia │ │ Arbitrum Sepolia│ │ (Origin) │ │ (Pool Chain) │ │ │ │ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ Deposit │──┼─────┼─►│ Privacy │ │ │ │ Entrypoint│ │ │ │ Pool │ │ │ └───────────┘ │ │ └───────────┘ │ │ │ │ │ │ ┌───────────┐ │ │ │ │ │ Withdrawal│◄─┼─────┼──│ Withdrawal │ │ │ │ Output │ │ │ │ Intent │ │ │ └───────────┘ │ │ │ └─────────────────┘ └─────────────────┘ ``` ### Open Intent Framework (OIF) Cross-chain operations use the **Open Intent Framework** — a standard for expressing user intents that solvers fulfill. #### Intent Structure ```solidity struct ShinobiIntent { address user; // Intent creator uint256 nonce; // Unique identifier uint256 originChainId; // Source chain uint32 expires; // Expiry timestamp uint32 fillDeadline; // Deadline for filling address fillOracle; // Validates fills uint256[2][] inputs; // Escrowed tokens MandateOutput[] outputs;// Actions on destination // Shinobi extensions address intentOracle; // Validates intent existence bytes refundCalldata; // Custom refund logic } ``` #### Solvers **Solvers** are off-chain actors that monitor intents and fulfill them: 1. Detect new intent on origin chain 2. Execute the output on destination chain 3. Prove the fill via oracle 4. Claim escrowed funds on origin chain Solvers earn fees for providing this service. ### Cross-Chain Deposit Flow ``` User (Base) → DepositEntrypoint → InputSettler (escrow) │ Solver monitors ◄───┘ │ ▼ DepositOutputSettler (Arbitrum) → Entrypoint → Pool │ Fill proof via oracle │ ▼ InputSettler releases funds to solver ``` **Key Security**: Deposits require **intentOracle validation** to verify the depositor address came from a legitimate user on the origin chain. ### Cross-Chain Withdrawal Flow ``` User generates ZK proof │ ▼ Entrypoint (Arbitrum) → Pool validates proof → InputSettler (escrow) │ Solver monitors ◄───┘ │ ▼ WithdrawalOutputSettler (Base) → User receives ETH │ Fill proof via oracle │ ▼ InputSettler releases funds to solver ``` **Key Security**: Withdrawals use **optimistic settlement** — the ZK proof on the origin chain already validated the user's ownership, so no additional intent proof is needed. ### Oracle System Two oracles ensure secure cross-chain settlement: | Oracle | Direction | Purpose | | ---------------- | ------------- | ----------------------------------------------- | | **fillOracle** | Dest → Origin | Proves the solver filled the intent | | **intentOracle** | Origin → Dest | Proves the intent is legitimate (deposits only) | ### Refund Mechanism If a cross-chain withdrawal intent expires without being filled: 1. The escrowed funds return to the pool 2. A **refund commitment** is inserted (from the 9th proof signal) 3. User can withdraw the refund as a normal privacy pool withdrawal This ensures funds are never lost, even if solvers fail to fill. ### Learn More * **[Privacy Pools](/concepts/privacy-pools)** — Core cryptographic primitives * **[OIF Settlers](/contracts/settlers)** — Smart contract implementation * **[Compliance](/concepts/compliance)** — How ASPs work with cross-chain ## Privacy Pools Understanding the core cryptographic primitives behind Shinobi Cash. :::note[Audience] This page is for **Developers and Security Reviewers**. For user guides, see [Deposit](/guides/deposit) and [Withdraw](/guides/withdraw). ::: :::info[TL;DR] Deposits create **commitments** in a Merkle tree. Withdrawals reveal **nullifiers** that prove ownership without revealing which deposit. Zero-knowledge proofs verify everything on-chain without leaking information. ::: ### Key Terms | Term | Meaning | | --------------- | ------------------------------------------------------------------------ | | **Commitment** | A hash representing your deposit, added to the Merkle tree | | **Nullifier** | A unique value revealed during withdrawal to prevent double-spend | | **Merkle Tree** | Data structure that allows proving membership without revealing position | | **ZK Proof** | Cryptographic proof that verifies claims without revealing inputs | ### Overview Privacy Pools enable private transactions by breaking the on-chain link between deposits and withdrawals. Users deposit funds into a shared pool and later withdraw using zero-knowledge proofs that verify ownership without revealing which deposit is theirs. ### Concrete Example ``` Alice deposits 1 ETH: → Commitment C₁ added to Merkle tree → Alice stores secret S₁ locally Later, Alice withdraws 0.3 ETH: → Generates ZK proof: "I know secret for some commitment in the tree" → Reveals nullifier N₁ (derived from S₁) → Receives 0.3 ETH to recipient address → Change note: 0.7 ETH commitment C₂ created On-chain, observers see: → Deposit: someone added C₁ → Withdrawal: someone spent N₁, created C₂ → No link between C₁ and N₁ visible ``` ### Key Concepts #### Commitments When you deposit, a **commitment** is created and added to a Merkle tree: ``` commitment = hash(nullifier, secret, amount, ...) ``` This commitment is public, but the underlying values (nullifier, secret) are known only to you. #### Nullifiers When you withdraw, you reveal a **nullifier** derived from your deposit: ``` nullifier = hash(secret, depositIndex) ``` The nullifier proves you own a valid deposit without revealing which one. Once revealed, the nullifier is marked as "spent" to prevent double-spending. #### Zero-Knowledge Proofs Withdrawals require a **Groth16 SNARK proof** that verifies: 1. You know the secret for a valid commitment in the tree 2. The nullifier is correctly derived from that commitment 3. The commitment is in a compliant Association Set (ASP membership) The proof reveals nothing about which deposit you're spending. ### Merkle Trees Shinobi Cash uses two Merkle trees: #### State Tree Contains all deposit commitments. The root changes with each deposit. #### ASP Tree Contains commitments approved by Association Set Providers. Only deposits in the ASP tree can be withdrawn (ensuring compliance). ### Proof Signals #### Standard Withdrawal (8 signals) ``` [0] newCommitmentHash // Change note commitment [1] existingNullifierHash // Spent deposit nullifier [2] withdrawnValue // Amount withdrawn [3] stateRoot // Merkle root of deposits [4] stateTreeDepth // Depth of state tree [5] ASPRoot // Merkle root of ASP set [6] ASPTreeDepth // Depth of ASP tree [7] context // Binding context (prevents replay) ``` #### Cross-Chain Withdrawal (9 signals) Adds one additional signal: ``` [2] refundCommitmentHash // Fallback if intent fails ``` This enables recovery if the cross-chain settlement fails. ### Privacy Guarantees | What's Hidden | What's Revealed | | ----------------------------- | ----------------------------- | | Which deposit you're spending | That you have a valid deposit | | Your deposit history | The nullifier (once spent) | | Connection to your identity | The withdrawal amount | | | The recipient address | ### Learn More * **[Cross-Chain Architecture](/concepts/cross-chain)** — How privacy works across chains * **[Compliance](/concepts/compliance)** — Association Set Providers explained * **[Smart Contracts](/contracts/)** — Implementation details ## Threat Model :::note[Audience] This page is for **Security Reviewers, Auditors, and Advanced Users** evaluating Shinobi Cash's security properties. ::: This document describes the adversaries Shinobi Cash considers, the attacks they can mount, and how the protocol mitigates them. ### Adversaries #### Malicious ASP (Association Set Provider) **What they control:** Approval/rejection of deposits into the compliant set. | Can Do | Cannot Do | | ----------------------------------- | --------------------------------- | | Censor deposits (refuse to approve) | Steal deposited funds | | Delay approval indefinitely | Deanonymize past withdrawals | | Reject legitimate deposits | Access user secrets or nullifiers | **Mitigation:** * Rejected deposits can be recovered via "ragequit" (reveals depositor, but funds are safe) * ASP decisions are on-chain and publicly auditable * Future: multiple competing ASPs possible #### Malicious Solver **What they control:** Filling cross-chain intents. | Can Do | Cannot Do | | ------------------------------- | ------------------------------------ | | Delay or refuse to fill intents | Steal escrowed funds | | Front-run other solvers | Redirect funds to wrong recipient | | Go offline | Forge proofs or bypass ZK validation | **Mitigation:** * Solvers are **permissionless** — anyone can run one * Economic incentives (solver fees) drive correct behavior * Expired intents trigger **refund commitments** — funds return to pool * No single solver can block the system #### Malicious Indexer **What they control:** Serving deposit/withdrawal data to the UI. | Can Do | Cannot Do | | ---------------------------------------- | ---------------------------- | | Withhold data (notes don't appear in UI) | Forge deposits or proofs | | Serve stale data | Steal funds | | Degrade UX | Prevent on-chain withdrawals | **Mitigation:** * Indexer is a **read-only UX layer** — not trusted for correctness * Users can generate proofs from on-chain data directly * Multiple indexers can exist (not a single point of failure) #### Malicious UI **What they control:** The web interface users interact with. | Can Do | Cannot Do | | -------------------------------------- | ----------------------------------- | | Phish users | Access funds without user signature | | Display incorrect data | Forge ZK proofs | | Trick users into signing malicious txs | Bypass wallet confirmation | **Mitigation:** * UI is **explicitly untrusted** * All sensitive operations require wallet signatures * Users can self-host or use CLI tools * Proofs are generated client-side, never sent to servers #### Malicious User (Attacker) **What they try:** Double-spend, forge proofs, deanonymize others. | Attack | Mitigation | | ------------------------- | ------------------------------------------------------ | | Double-spend same deposit | Nullifiers prevent reuse; once revealed, marked spent | | Forge withdrawal proof | Groth16 SNARK verification on-chain | | Deanonymize other users | ZK proofs reveal nothing about which deposit was spent | | Front-run withdrawals | Context binding in proofs prevents replay | ### Attack Scenarios #### Scenario 1: Solver Collusion **Attack:** All solvers collude to refuse filling withdrawal intents. **Impact:** Withdrawals delayed until intent expiry. **Outcome:** Escrowed funds return to pool as refund commitments. User can withdraw refund normally. No fund loss. #### Scenario 2: ASP Censorship **Attack:** ASP refuses to approve any deposits. **Impact:** New deposits cannot join the compliant set. **Outcome:** Users can ragequit (recover funds with privacy loss). Protocol can add alternative ASPs. #### Scenario 3: Indexer Downtime **Attack:** Indexer goes offline or serves empty responses. **Impact:** UI cannot display notes; users cannot easily discover their deposits. **Outcome:** No fund loss. Users can query on-chain events directly or wait for indexer recovery. #### Scenario 4: Proof Replay **Attack:** Attacker captures a valid proof and tries to replay it. **Impact:** None. **Outcome:** Context binding (hash of withdrawal data + scope) makes proofs non-replayable. Same proof cannot be used for different withdrawal. ### What Shinobi Cash Does NOT Protect Against * **Compromised wallet** — If your private key is stolen, attacker can sign withdrawals * **User error** — Withdrawing to wrong address, losing recovery phrase * **Contract bugs** — Smart contracts are **not yet audited**; bugs could cause fund loss * **Regulatory action** — Protocol cannot prevent legal enforcement against users * **Timing correlation** — Depositing and immediately withdrawing same amount reduces privacy ### Security Assumptions Summary | Component | Trusted For | Not Trusted For | | ------------------- | ------------------ | ------------------------- | | ZK Proofs | Correctness (math) | — | | Smart Contracts | Correctness (code) | — (unaudited) | | ASPs | — | Availability, fairness | | Solvers | — | Availability, honesty | | Indexer | — | Correctness, availability | | UI | — | Anything | | Bundlers (ERC-4337) | Tx relay | Privacy (public mempool) | ### Related Pages * **[Trust Assumptions](/concepts/trust-assumptions)** — Bullet-point summary * **[Compliance](/concepts/compliance)** — How ASPs work * **[Cross-Chain Architecture](/concepts/cross-chain)** — Solver and intent system ## Trust Assumptions :::note[Audience] This page is for **Security Reviewers and Advanced Users** who want a quick reference of what Shinobi Cash trusts and doesn't trust. ::: A bullet-point summary of Shinobi Cash's trust model. For detailed analysis, see the [Threat Model](/concepts/threat-model). ### What Shinobi Cash Trusts #### Smart Contracts * ✅ Contracts execute correctly as written * ⚠️ **Contracts are NOT YET AUDITED** — bugs may exist * ✅ ERC-4337 EntryPoint is battle-tested (used by many protocols) #### Zero-Knowledge Proofs * ✅ Groth16 cryptography is sound * ✅ Circuit constraints correctly encode withdrawal rules * ✅ Trusted setup was performed correctly (uses established ceremony) #### Ethereum Consensus * ✅ Blocks are final after sufficient confirmations * ✅ Transactions cannot be reversed post-finality ### What Shinobi Cash Does NOT Trust #### Association Set Providers (ASPs) * ❌ ASPs may censor deposits (refuse approval) * ❌ ASPs may delay indefinitely * ❌ ASPs may apply inconsistent criteria * ✅ But: ASPs **cannot steal funds** * ✅ But: ASPs **cannot deanonymize withdrawals** * ✅ But: Censored users can **ragequit** (recover funds) #### Solvers * ❌ Solvers may go offline * ❌ Solvers may delay filling intents * ❌ Solvers may prioritize profitable intents * ✅ But: Solvers **cannot steal escrowed funds** * ✅ But: Expired intents **return as refund commitments** * ✅ But: Solvers are **permissionless** — anyone can run one #### Indexer * ❌ Indexer may withhold data * ❌ Indexer may serve stale information * ❌ Indexer may go offline * ✅ But: Indexer **cannot forge proofs or deposits** * ✅ But: Users can query **on-chain data directly** #### User Interface * ❌ UI may be compromised * ❌ UI may display incorrect information * ❌ UI may attempt phishing * ✅ But: All operations require **wallet signature** * ✅ But: Users can **self-host** or use CLI * ✅ But: Proofs are generated **client-side** #### Bundlers (ERC-4337) * ❌ Bundlers see transaction data (not private) * ❌ Bundlers may censor transactions * ✅ But: Bundlers **cannot steal funds** * ✅ But: Multiple bundlers exist (Pimlico, Alchemy, etc.) ### The Security Contract **If you use Shinobi Cash correctly:** | Guarantee | Condition | | ---------------------------------------- | -------------------------------------- | | Funds cannot be stolen | Unless contracts have bugs (unaudited) | | Withdrawals cannot be linked to deposits | Unless you leak information yourself | | Cross-chain funds cannot be lost | Refund mechanism ensures recovery | | No one can withdraw your funds | Unless your wallet is compromised | ### Key Risks to Understand :::warning[Unaudited Contracts] Smart contracts have **not been audited**. Use testnet only. Do not deposit real funds. ::: :::warning[Privacy Hygiene] Your privacy depends on your behavior: * Don't withdraw immediately after depositing * Don't withdraw exact deposit amounts * Use fresh recipient addresses ::: :::warning[ASP Dependency] Withdrawals require ASP approval. If an ASP rejects your deposit, you can only ragequit (recover funds but lose privacy). ::: ### Related Pages * **[Threat Model](/concepts/threat-model)** — Detailed adversary analysis * **[Compliance](/concepts/compliance)** — How ASPs work * **[Privacy Pools](/concepts/privacy-pools)** — Cryptographic foundations