This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.
Note: only the four selectors actually observed on-chain (wrap, settle, attestBitcoinStateProven, registerWrappedAuto) are shown as raw hashes; the remaining signatures are listed by name (derive the selector with cast sig).
The single proof entrypoint. Verifies one SP1 proof against the immutable PROGRAM_VKEY, decodes the versioned PublicValues, and applies every effect the guest attested: spend nullifiers, append note/lock/CDP leaves, consume deposits, run AMM swaps and LP moves, open/close/liquidate/top-up CDP positions, mint cBTC against reflected locks, record cross-chain burns, and pay withdrawals + settler fees. All amount/conservation/range checking happened in the guest; the contract enforces roots, one-shot gates, reserve floors, and the public boundary.
Membership may be proven against an Ethereum root or a relay-attested Bitcoin root (cross-lane). Bitcoin-homed value exits must record the source nullifier for reverse reflection, or bridge.
☑
Chain-bound: pv.chainBinding must equal keccak(chainid, address(this)), so a proof cannot be replayed onto another deployment.
The contract trusts the guest's conservation/range math entirely; on-chain checks are structural gates + defense-in-depth floors, not a re-derivation of the cryptography.
△
Settler fees (pv.fees) are paid to msg.sender — a third-party relayer model; a self-prover sets none.
Public deposit boundary. Escrows amount of the asset's underlying (transfer-in an external ERC-20, escrow native ETH via msg.value, or burn a pool-minted canonical ERC-20) and records a pending deposit keyed by depositId = keccak(assetId, value, commit), where commit = keccak(Cx‖Cy‖owner). The note is inserted into the tree only when a later settle consumes the deposit and proves the commitment opens to value = amount/unitScale.
Only the digest reaches the chain — the note's nullifier stays externally uncomputable, so the later spend is unlinkable.
☑
Amount must be a whole multiple of unitScale and fit u64 after scaling (AmountNotAligned / ValueOutOfRange) — binds the note's value to the escrow so it can never claim more than was deposited.
☑
Fee-on-transfer tokens are rejected by a realized-delta check (FeeOnTransferUnsupported).
The trustless Bitcoin-state import. Verifies an SP1 "reflection" proof against BITCOIN_RELAY_VKEY, then advances the reflected Bitcoin roots (pool note root → knownBitcoinRoot; spent-nullifier root; bridge-burn root), the reflection digest chain, cBTC backing/locks, and any etch-authenticated asset metadata. Roots are anchored to canonical Bitcoin via the BitcoinLightRelay and matured REFLECTION_CONFIRMATIONS (6) deep. No trusted oracle — the proof is the sole authority.
Append-only digest chain: priorDigest == knownReflectionDigest, height non-decreasing — a stale proof cannot roll the spent set backward.
☑
Non-zero sentinel roots enforced (ZeroBitcoinPoolRoot, StaleBitcoin*Root) so an empty tree can never be marked canonical.
☑
Freshness gates tie the folded fast-lane consume count and cross-out count to the current on-chain counters (ConsumedCountStale).
△
Soundness depends entirely on the off-chain reflection guest pinned by BITCOIN_RELAY_VKEY and on the BitcoinLightRelay's honesty about the canonical tip.
Public (non-shielded) liquidity provisioning. Lazily creates the canonical (assetA, assetB, feeBps) pool if absent, escrows both legs from public ERC-20/ETH funds, sets/updates reserves, and credits public LP shares to to. First add mints isqrt(vA·vB) with MINIMUM_LIQUIDITY locked; later adds are proportional (limiting-leg rule) with the off-ratio excess refunded.
Shares public here vs a confidential note via shieldShares; both share pools[poolId].totalShares.
☑
Reserves bounded to u64; excess leg refunded, never donated.
CONDITION
REVERT MESSAGE
Same asset / unregistered
SameAsset / NotRegistered
ETH mismatch
EthValueMismatch
Below min shares
SlippageExceeded
Dust first add
InsufficientLiquidity
functioncreatePairAndAddLiquidityPublic(bytes32assetA,bytes32assetB,uint32feeBps,uint256amountA,uint256amountB,uint256minSharesOut,uint64deadline,addressto)externalpayablenonReentrantreturns(uint256sharesMinted){/* see source */}
Public constant-product swap against the pool's public reserves (no privacy — the amount is revealed; the confidential OP_SWAP hides it). out = floor(reserveOut·vIn·γ / (reserveIn·10000 + vIn·γ)), γ = 10000 − feeBps; k can only increase.
functionswapPublic(bytes32assetIn,bytes32assetOut,uint32feeBps,uint256amountIn,uint256minAmountOut,uint64deadline,addressto)externalpayablenonReentrantreturns(uint256amountOut){/* see source */}
Burns public LP shares and withdraws proportional reserves to to. If owner != msg.sender, the caller must be an LP operator approved by owner. Can never remove the locked MINIMUM_LIQUIDITY.
ATTRIBUTE
VALUE
Selector
derive via cast sig
Access
External, nonReentrant
FLAG
OBSERVATION
△
Operator delegation is all-or-nothing: an approved operator may remove any amount to an operator-chosen to. Approve only trusted routers.
functionremoveLiquidityPublicFrom(bytes32assetA,bytes32assetB,uint32feeBps,uint256shares,uint256minAmountA,uint256minAmountB,uint64deadline,addressowner,addressto)externalnonReentrantreturns(uint256amountLo,uint256amountHi){/* see source */}
createPair(assetA, assetB, feeBps, rcptPrefix, rcptX, protocolFeeBps) creates an empty confidential AMM pool slot (one per canonical pair + fee; optional fee-switch recipient). createPairAndSettle(assetA, assetB, feeBps, publicValues, proofBytes, memos) lazily creates the slot then runs _settle in one tx so a single proof both founds and seeds the pool.
ATTRIBUTE
VALUE
Access
External; createPairAndSettle is nonReentrant
FLAG
OBSERVATION
☑
Front-run resistant: a front-run only registers the empty slot; the first funder becomes founder, so the pair cannot be captured.
☑
Swap fee capped at MAX_POOL_FEE_BPS (10%); protocolFeeBps < 10000.
Opt-in privacy for an LP position: burns the caller's public LP shares and records a pending deposit that the existing OP_WRAP settle consumes into a confidential LP-share note. totalShares is unchanged (a form change only); the note is spendable only via OP_LP_REMOVE.
ATTRIBUTE
VALUE
Access
External, nonReentrant
functionshieldShares(bytes32poolId,uint256shares,bytes32commit)externalnonReentrantreturns(bytes32depositId){/* burns public shares, registers deposit */}
Fund (amount > 0) or wind down (amount == 0) a farm controller's escrow-backed reward treasury. Funding pins one escrow reward asset per controller; recovery returns exactly the budget not backing outstanding reward notes to the funding controller.
ATTRIBUTE
VALUE
Access
External, nonReentrant
FLAG
OBSERVATION
☑
Recovery is gated to the controller that funded (msg.sender), and can never reach another farm's backing.
☑
Native/pool-minted assets are rejected as escrow reward assets (those are mint-mode).
functionfarmEscrow(addresscontroller,bytes32rewardAsset,uint256amount,addressto)externalnonReentrantreturns(uint256out){/* fund or recover, see source */}
Function: approveLpOperator(address operator)
Approves an all-or-nothing operator to removeLiquidityPublicFrom the caller's public LP shares. Pass address(0) to revoke.
ATTRIBUTE
VALUE
Access
External
FLAG
OBSERVATION
△
The setApprovalForAll model — the operator may remove any amount to any recipient. Approve only trusted contracts.
registerWrapped(underlying, unitScale, crossChainLink, name, symbol, decimals) — register an external ERC-20 as an escrow-backed confidential asset (crossChainLink must be 0).
registerWrappedAuto(underlying, crossChainLink) — same, reading the token's decimals() and deriving unitScale = 10^(decimals − min(decimals,8)) on-chain.
registerMinted(canonicalErc20, name, symbol, tacitDecimals) — register a local pool-minted asset whose canonical ERC-20 this pool mints/burns (mint authority must be this pool).
ATTRIBUTE
VALUE
Selector
registerWrappedAuto = 0x0339e3f3
Access
External, nonReentrant
FLAG
OBSERVATION
☑
A permissionless caller can never set a bridged cross-chain link: escrow assets reject a non-zero link (CrossChainEscrow); pool-minted links must match the token's committed ASSET_ID; native ETH's link is constructor-pinned.
☑
The bridged-resolution link that a bridged unwrap uses is set only by the guest-proven attest_meta path, so a scale/token cannot be poisoned.
Returns the asset registry tuple (registered, underlying, unitScale, crossChainLink, poolMinted, decimals). name/symbol are not stored — read them from the AssetRegistered event.
canonicalTokenFor resolves an asset id (native or shared Bitcoin-side) to the canonical ERC-20 the pool recognizes, or address(0) — the source of truth for "is this the real token?" (impostors are never pool-registered). localAssetOf maps a shared Bitcoin-side id to this pool's local registry key.
functioncanonicalTokenFor(bytes32assetId)externalviewreturns(address);functionlocalAssetOf(bytes32)externalviewreturns(bytes32);// public mapping getter
Function: shielded-pool & escrow views
Read-only accessors over pool state.
currentRoot() — the latest note-tree root.
nextLeafIndex() — total note leaves ever created.
escrow(bytes32 assetId) — escrowed underlying for an asset.
farmTreasury(address controller) — a farm's funded, undistributed budget.
functioncurrentRoot()externalviewreturns(bytes32);functionnextLeafIndex()externalviewreturns(uint256);functionescrow(bytes32)externalviewreturns(uint256);// public mapping getterfunctiondepositStatus(bytes32)externalviewreturns(uint8);// public mapping getterfunctionfarmTreasury(address)externalviewreturns(uint256);// public mapping getter
Function: cBTC views
Read-only cBTC (confidential Bitcoin) state, all advanced by reflection.
cbtcBackingSats() — Σ live self-custody cBTC.zk lock sats (the off-pool buffer reads it).