Security Design
In-depth coverage of the security patterns and invariants enforced across the Fira Protocol smart contract stack.
Access control
Ownership model
All privileged Fira contracts inherit BoringOwnableUpgradeable, which provides:
Single
ownerwith a two-step transfer (transferOwnership→claimOwnership) to prevent accidental misassignment.onlyOwnermodifier gating admin functions (pause, parameter updates, upgrades).
Router selector guards
The FiraRouterV4 uses a diamond-like facet dispatch table. Each facet registers its function selectors during initialize(). Calls to unregistered selectors revert with Errors.RouterInvalidAction. This ensures that only explicitly whitelisted facets can be invoked through the Router.
Minting privileges
BondToken and CouponToken restrict mintBC() and redeemBC() to the YieldContractFactory address set at initialization, preventing arbitrary minting.
The LiquidityInjector holds a separate minting privilege on BondToken via the LIQUIDITY_INJECTOR role, constrained by the injectLiquidityLimit cap.
Reentrancy protection
Fira applies the nonReentrant modifier (OpenZeppelin ReentrancyGuard) to all external state-mutating functions across:
FWBase
deposit, redeem, claimRewards
FiraMarket
mint, burn, swapExactBtForFw, swapFwForExactBt
CouponToken
mintBC, redeemBC, redeemInterest, redeemInterestAndTransfer
LiquidityInjector
injectLiquidity, withdrawLiquidity
RehypothecationModule
rebalance
The Router's _safeTransferWithFeeExtraction and callback patterns (flash swaps) are additionally protected by the facet-level lock state in ActionStorageV4.
Pause controls
FW token contracts (FWBase, USDCFW) inherit OpenZeppelin's PausableUpgradeable:
pause()— Owner freezes all deposit and redeem operations.unpause()— Owner resumes normal operations.whenNotPausedmodifier — Applied todeposit(),redeem(), and related transfer functions.
Pause does not affect BT/CT trading on FiraMarket. Market operations remain available to allow users to exit positions even during an FW pause.
Upgrade patterns
Initializable contracts
All upgradeable contracts use OpenZeppelin's Initializable with initializer / reinitializer(n) modifiers to prevent double-initialization attacks.
Router versioning
The Router facet system supports additive upgrades:
Deploy a new facet contract.
Call
Router.addToSelectorMapping(selectors, facetAddress)to register new selectors.Existing selectors can be overwritten to point to the new facet, enabling in-place upgrades without redeploying the Router proxy.
Old facets remain deployed on-chain but become unreachable once their selectors are reassigned.
Storage layout safety
Upgradeable contracts follow the "storage gap" convention and use dedicated storage slots (e.g., ActionStorageV4.STORAGE_SLOT) to avoid slot collisions across upgrades.
Oracle defence-in-depth
The Fira oracle pipeline stacks multiple validation layers:
Each layer can reject stale or invalid data independently. ChainlinkOracleV2 enforces a configurable heartbeat and reverts with StalePrice if the feed has not updated within the window.
Fixed-point arithmetic safety
All yield and pricing math uses the PMath and LogExpMath libraries with 18-decimal fixed-point representation. Key invariants:
No division by zero — All divisors are checked or guaranteed non-zero by prior validation.
Overflow protection — Solidity 0.8.x built-in overflow checks apply;
LogExpMathadditionally validates input ranges before exponentiation.Rounding direction — Deposit/mint operations round against the user (ceiling); redeem/burn operations round in favor of the protocol (floor), preventing rounding exploits.
Contract size management
Several Fira contracts approach the EIP-170 24 KB limit. The codebase uses two mitigation strategies:
Library extraction — Heavy logic is moved to external libraries (e.g.,
MarketMathCore,MarketApproxPtInLib) that are deployed separately and linked viaDELEGATECALL.Split-code deployment — The
FiraMarketfactory uses a two-part deployment (MarketMathCore+FiraMarketV3) to keep each deployment unit under the size limit.
Invariants summary
FW exchange rate monotonically non-decreasing
FWBase._afterUpdateBCIndex + rehyp rebalance
BT total supply = minted via Factory + LI inject
BondToken.mintBC access control
CT interest accrual ≥ 0
CouponToken._updateAndDistributeInterest
LP pool BT + FW reserves ≥ total LP supply
FiraMarket mint/burn invariant checks
Rehyp ratio stays within [phiMin, phiMax]
RehypothecationModule.rebalance bounds check
Lending market solvency
LendingMarket liquidation + oracle health checks
Last updated