ERC-4626 Vaults
Add a synchronous vault: describe its shares, check its dependencies, register supply and redeem, and keep a share withdrawal path.
Start with assets and shares#
An ERC-4626 deposit spends an amount of underlying and receives shares. Redemption spends shares and receives underlying. Store the share count as the private balance; display its estimated underlying value separately. Vault accounting can change that estimate, so calculate it from a preview instead of assuming a one-to-one rate.
ReviewedFixedShareAdapter connects one Pool to one vault and underlying token. Its recipient and call targets are fixed at deployment. SUPPLY is operation 0, REDEEM is operation 1, and actionData is zero. The original fork runbook uses USDC vaults. The manifest path also supports the Sepolia Aave WETH pair and its native-ETH variant, each through that network's own release configuration.
Check token behavior and vault permissions#
Test that underlying and shares transfer the full requested amount and that their balances do not rebase. Check decimals, donation handling, rounding, receiver restrictions, pause controls and direct share transfers. Asynchronous withdrawals, debt positions, transfer fees, reward claims and arbitrary vault calls need a different adapter design.
List the vault's proxies and other dependencies. A proxy can keep the same runtime code while changing implementation. Record its storage slot, implementation address and implementation code hash, together with bounded identity calls. If the manifest declares an implementation getter, include it in the adapter's identity pins too. A proxy without such a getter needs a reviewer decision because an adapter cannot read another contract's storage.
Create the vault manifest#
The fuyu-queue-erc4626-manifest-v1 manifest names the chain, Pool address/runtime/domain, adapter runtime/guardDigest, tokens, operations, quote policy, ordered code pins and identity calls, display risks and screened share exit. nativeUnderlying: true uses address(0) for native ETH on the Pool side while the adapter trades the configured wrapped token. The parser rejects extra fields and a mismatched digest.
Get the digest from your release catalog. The parser canonicalizes the manifest and hashes that value with Keccak-256; a file SHA-256 or runtime code hash is a different value. Keep the guard arrays in order, since the adapter's guardDigest includes their order. Returning a digest beside an API manifest does not make it trusted.
// Repository helpers from apps/privacy/src/queue-erc4626-manifest.ts
import {
parseReviewedErc4626Manifest,
reviewedErc4626AdapterGuards,
reviewedErc4626AdapterGuardDigest,
reviewedErc4626RoutePair,
} from './queue-erc4626-manifest';
// approvedDigest must come from your reviewed release, not the API.
const manifest = parseReviewedErc4626Manifest(rawManifest, approvedDigest);
const { extraPins, identityPins } = reviewedErc4626AdapterGuards(manifest);
const guardDigest = reviewedErc4626AdapterGuardDigest(manifest);
const routePair = reviewedErc4626RoutePair(manifest);
// Deploy/review against these ordered guards, then authenticate chain state.Check the venue before the vault call#
The adapter records the chain and Pool, vault and underlying code hashes. Before executing, it checks those hashes, any extra code pins and the expected results of its staticcalls. It then measures input pulled from the Pool and output returned there. The supply allowance is cleared after use. An unexpected transfer, leftover balance or short output reverts the action.
The API also checks proxy storage slots, route registration and capacity at a canonical block. The browser repeats the checks before proving, and the relay checks again before submission. This matters for proxies whose implementation is visible through storage but has no onchain getter the adapter can call.
Check capacity, then estimate the whole action#
For supply, pair previewDeposit with maxDeposit for the Pool receiver. For redemption, use previewRedeem and either maxRedeem or the manifest's exact-owner diagnostic. A preview gives a conversion amount. Check cash availability and recipient permissions separately before offering the quote.
The exact-owner redeem diagnostic uses the manifest's gas limit. It skips the adapter's preceding share transfer and the rest of the Pool call, so follow it with full action estimation. Put a positive final minimum in the proof and recheck expiry after proving. The transaction will credit what actually arrived at the Pool.
| Quote field | Purpose |
|---|---|
| supplyCapacity: max-deposit | Checks how much the Pool receiver may deposit |
| redeemCapacity | Chooses max-redeem or the exact-owner-redeem diagnostic |
| redeemCallGasLimit | Limits gas for that diagnostic |
| maxAgeBlocks | Limits how old the quote may be before proof and relay |
| maxSlippageBps | Caps the reduction from the quoted output |
Register both directions#
Admit the share token, then register supply and redeem under their adapter/operation/input/output tuples. FUYU_QUEUE_ERC4626_REVIEWS_FILE loads absolute manifest paths and their approved digests from a separate release file. queue-erc4626-publish.cjs prints a dry run unless the disposable-fork transaction gates are enabled. It refuses to re-enable a configured route that governance revoked.
Compile the same approved digests into the frontend through FUYU_QUEUE_ERC4626_FRONTEND_DIGESTS. The optional registry prototype instead lets a frontend authenticate one registry and load later vault digests onchain. Operators still update the catalog/config and restart the API. ExitOnly requires action-route revocation while leaving share withdrawal available. The registry and publisher workflows remain test-only.
# Read-only publisher plan; run in the protocol checkout.
# The release file contains independently reviewed absolute manifest paths.
FUYU_QUEUE_ERC4626_REVIEWS_FILE=/absolute/reviewed-release.json \
node apps/privacy/runtime/queue-erc4626-publish.cjsHandle a failed route or withdraw shares#
For VenueChanged, inspect which dependency or identity call changed and review it again. InsufficientOutput means the Pool received less than the proof's minimum. InexactInput and InexactConsumption mean the token or vault moved balances differently from the supported transfer model. Calling execute from an EOA gives OnlyPrivacyPool; submit the private action through the Pool.
Existing shares can leave through the ordinary screened exit after the action route is disabled. Check the destination's receiver rules before proving. The recipient gets shares and must redeem them through the vault; cash availability can still delay that redemption. Test this fallback and recover the withdrawn/share balances from authenticated history before listing the venue.