TACO ships two factory/lens pairs: one for AMM pools (PvpAMMFactory + PvpAMMLens) and one for cross-margin accounts (MarginAccountFactory + MarginAccountLens). Factories give you a canonical way to create and discover contracts; lenses give you a canonical, stateless way to inspect them.
PvpAMMFactory
PvpAMMFactory deploys upgradeable pools and registers them by (marketId, collateral). The registry is deterministic: pools are created with CREATE2 (salt = keccak256(marketId, collateral)) through a dedicated PvpAMMPoolDeployer, so apps can compute a pool address ahead of time with predictPoolAddress. Each deployment also wires up an LP token and fee-pool manager.
Lookups (all keyed by market and collateral):
getPool / getLPToken / isSupported
getMarketMetadata: pool, LP token, creator, creation source, verified flag
getMarketBond / isMarketBondSlashed
Three ways to create a market
- Governance (
createPool, owner-only): full control over implementation, oracle, risk params, and pool admin. As the owner you may pass any oracle.
- Permissionless with preset risk (
createPoolWithBond): anyone can create a market after locking the configured bond token (TACO). The pool reuses the governance-configured implementation, preset risk params, and pool admin.
- Permissionless with custom risk (
createPoolWithBondCustom): the creator supplies risk params, but every field must stay inside the inclusive [publicRiskMin, publicRiskMax] ranges configured by governance.
Both permissionless paths lock the same market bond and give the creator no pool roles.
The permissionless paths are deliberately constrained so an anonymous creator cannot wire a self-controlled price feed into an oracle-settled pool:
- Oracle must be registry-curated. The
oracle you pass must self-report this (marketId, collateral) (otherwise the call reverts OracleMismatch) and must equal oracleRegistry.getOracle(marketId, collateral); a zero or mismatched registry entry reverts UntrustedOracle. Because the registry is owner-only, only governance decides which feed backs a given market.
- Implementation version floor. The single public implementation that both bonded paths reuse must self-report a
version() at or above minPublicImplementationVersion (deploy-time default DEFAULT_MIN_PUBLIC_IMPLEMENTATION_VERSION = 16). The floor is enforced when governance configures that implementation (setPublicPoolCreationConfig), not on each market creation. Governance raises the floor with setMinPublicImplementationVersion, for example after a security fix, which takes effect once it re-points the public implementation.
- Custom risk must stay within governance bounds.
createPoolWithBondCustom requires governance to configure per-field ranges with setPublicRiskBounds. If the bounds are missing, it reverts PublicRiskBoundsNotConfigured; if any field is outside the inclusive range, it reverts RiskParamOutOfBounds rather than clamping the value. The pool’s normal cross-field risk invariants still run during initialization. When governance updates the bounds, the stored preset must also remain inside them (PresetRiskOutOfBounds otherwise).
The public paths are fail-closed. createPoolWithBond reverts PublicPoolCreationNotConfigured until governance has set the implementation, bond token, bond amount, lock time, pool admin, and oracle registry; both paths revert PublicPoolCreationPaused when permissionless creation is disabled. The custom-risk path checks its risk bounds first, so it can revert PublicRiskBoundsNotConfigured or RiskParamOutOfBounds before reaching the shared public-creation checks.
Bonded markets are created unverified. Governance can promote one with setMarketVerified (read via isMarketVerified), so frontends can surface a “verified” badge and filter unvetted markets. A creator reclaims their bond with withdrawMarketBond after the lock elapses; governance can slashMarketBond on any bonded market (intended for abusive ones; the on-chain guard only requires the bond to exist and be neither withdrawn nor already slashed).
PvpAMMLens and PvpAMMPreviewLens
PvpAMMLens is a stateless read surface reused across all compatible pools. Prefer it for consistent reads instead of calling pools directly. Write-action previews (trade quotes) live in a separate PvpAMMPreviewLens; the split keeps each contract under the EIP-170 code-size limit.
PvpAMMLens: read views
- Health & liquidatability:
getPositionHealth, getPositionView / getPositionRiskView (worth, health ratio, poolDirectLiquidatable), isPositionLiquidatable, getPositionFundingOwed.
- LP rewards & balances:
pendingLPRewards, getUserPoolAccountView, getUserLiquidityLotViews.
- Risk & funding config:
getRiskConfig, getFundingConfig.
- Fees:
getEffectiveFeeRate, previewTradingFee.
- Liquidity previews:
previewAddLiquidity / previewAddLiquidityTo, previewRemoveLiquidity (which mirrors the fee-share lock: a fee-sharing lot still inside its lock reverts FeePoolStillLocked rather than quoting a withdrawable amount).
- Pool state:
getPoolComposition, getFundingRateView, getPsi, getPrice, isDrainedWithActiveShares.
PvpAMMPreviewLens: write-action previews
previewOpenPosition, previewAddPositionCollateral, previewReducePositionLeverage, previewClosePayout.
MarginAccountFactory and MarginAccountLens
For cross-margin, MarginAccountFactory deploys a deterministic clone per (owner, collateral). createAccount returns the existing account on repeat calls; predictAccount / getAccount / isAccountFor resolve addresses without deploying. The factory also owns per-collateral cross-margin risk config (setCollateralConfig) and the trusted-router lifecycle.
MarginAccountLens.getAccountPreflight is the read-only companion for keepers and frontends: it returns account-level health plus per-position readability and health, and identifies any Pyth oracles that need a price refresh before a margin-sensitive call.
Why factories and lenses
- Factories give a canonical, deterministic way to create and discover pools and margin accounts.
- Lenses give a canonical, stateless way to inspect them.
- Together they cut per-app duplication and keep integrations aligned with the protocol.