Skip to content

Security Model

Anvil256 — Security Model


1. Assets

AssetLocationFormal Protection
Unminted ANVL supplyIntrinsic to Anvil256.solMinted on valid κ<D\kappa < D only; totalSupply ≤ MAX_SUPPLY (I1)
Minted ANVLERC-20 balancesStandard ERC-20 transfer semantics
Protocol fee ETHfeeRecipient (immutable)address immutable feeRecipient; no setter (I2)
POL reserve ETHlpReserveEthWei50% of each protocol fee until deployed/dripped to official LP
POL reserve ANVLlpReserveTokenWei / official LP NFT10% of each miner reward, minted inside MAX_SUPPLY
Stuck-fee ETHstuckFeesWeiSweepable by anyone, permissionless
Operator private keysOperator’s machine onlyNever transmitted; not in scope
Difficulty statecurrentDifficulty, integralErrorWadD1D \geq 1 (I5); $
Miner temporal identityminerEpochCount[m] = γ(m)\gamma(m)Monotonic, non-transferable (I13)
NCT window stateminerWindow, uniqueCount, windowFreqCu[1,256]C_u \in [1,256] (I11); updated before external calls (§4.4)
Epoch entropyepochEntropy, lastMineBlockSet after difficulty adjustment; commits to post-adjustment D[n+1]D[n+1]

2. Adversary Classes

ClassFormal Capability
External minerRun any client; submit any (nonce, address) pair as mine() input
Precompute attackerCompute candidate κ\kappa values before ϵ[n]\epsilon[n] is known
Wallet rotation attackerGenerate fresh addresses; attempt to reduce σ(k)\sigma(k) below k×feek \times \texttt{fee}
NCT Sybil attackerOperate kk addresses to suppress C[n]C[n] while controlling >W/k>W/k hashrate
MEV searcherObserve mempool; reorder or front-run mine() transactions
Malicious RPCCensor or misrepresent eth_call / eth_getTransactionReceipt responses
Compromised Chainlink oracleReturn malformed latestRoundData()
Fee recipient compromiseDrain future fee accumulations
Post-deployment deployerNo privileges — contract is fully immutable post-constructor

3. Protocol Invariants

Definition 3.1 (Protocol Invariant). A predicate II over the contract state is a protocol invariant iff it holds in all reachable states — i.e., for all sequences of valid calldata starting from the deployment state.

IDInvariantFormal Statement
I1Supply captotalSupply(S) <= MAX_SUPPLY = 21,000,000e18 for all reachable states
I2Fee recipient immutabilityfeeRecipient is immutable; no setter exists
I3Deployer separationConstructor enforces feeRecipient != msg.sender and feeRecipient != address(0)
I4Oracle non-nullConstructor enforces _ethUsdFeed != address(0)
I5Difficulty floorD[n]1D[n] \geq 1 for all nn
I6Epoch monotonicitycurrentEpoch[n+1] = currentEpoch[n] + 1 after each valid mine()
I7Fee correctnessAfter mine(): feeRecipient delta + stuckFeesWei delta + lpReserveEthWei delta = currentFeeWei() exactly
I8Refund correctnessmsg.sender refunded max(msg.valuecurrentFeeWei(),0)\max(\texttt{msg.value} - \texttt{currentFeeWei}(), 0)
I9Halving terminusR(n)=0R(n) = 0 for n13,440,000n \geq 13{,}440{,}000; mine() reverts MiningEnded() unless MAX_SUPPLY is reached earlier
I10Anti-windupintegralErrorWad remains inside [-I_MAX * WAD, +I_MAX * WAD]
I11Window integrityCu[1,256]C_u \in [1, 256] after first mine; Cu={m:freq[m]>0}C_u = \lvert\{m : freq[m] > 0\}\rvert exactly
I12Cascade correctnessmine(ν) verifies κ(m,ν,n)<D[n]\kappa(m,\nu,n) < D[n] before any state mutation
I13γ\gamma-monotonicityminerEpochCount[m] is strictly non-decreasing; never decremented or transferred

4. Threat Analysis and Mitigations

4.1 Cryptographic Assumptions

Model. H=keccak256H = \texttt{keccak256} is modelled as a random oracle H:{0,1}{0,1}256\mathcal{H} : \{0,1\}^* \to \{0,1\}^{256} with uniform output distribution.

ThreatSecurity Bound
Preimage attack on HHPr[A(y)=x:H(x)=y]2256\Pr[\mathcal{A}(y) = x : H(x) = y] \leq 2^{-256} (preimage resistance)
Collision attack on HHBirthday bound: 2128\geq 2^{128} queries needed for collision with probability 1/2\geq 1/2
Length-extension attackNot applicable: Keccak uses sponge construction, not Merkle–Damgård

4.2 Cascade Precomputation — Formal Theorem

Theorem 4.1. Under the random oracle model, for any PPT adversary A\mathcal{A} and any epoch nn, the probability that A\mathcal{A} produces a valid nonce ν\nu^* strictly before blockhash(lastMineBlock[n1])\texttt{blockhash}(\texttt{lastMineBlock}[n-1]) is produced by the sequencer is:

Pr ⁣[κ(m,ν,n)<D[n]]  =  D[n]2256\Pr\!\bigl[\kappa(m,\nu^*,n) < D[n]\bigr] \;=\; \frac{D[n]}{2^{256}}

i.e., no better than an online random guess.

Proof. ϵ[n]=H(entropySourceD[n])\epsilon[n] = H(\texttt{entropySource} \,\|\, D[n]) where entropySource\texttt{entropySource} is derived from bn1=blockhash(lastMineBlock[n1])b_{n-1}^* = \texttt{blockhash}(\texttt{lastMineBlock}[n-1]), prevrandao\texttt{prevrandao}, and block.timestamp\texttt{block.timestamp} (PATCH-1: if the 256-block window has expired, prevrandao\texttt{prevrandao} and timestamp\texttt{timestamp} are used alone, emitting EntropyFallback). In all paths, bn1b_{n-1}^* is produced at an unknown future time. In the random oracle model, ϵ[n]\epsilon[n] is uniformly distributed over {0,1}256\{0,1\}^{256}, independent of all values computable before bn1b_{n-1}^* is known.

For any function ff computable by A\mathcal{A} before time tt^*:

PrϵU({0,1}256) ⁣[H(H(ιf(ι))ϵ)<D]  =  D2256\Pr_{\epsilon \sim \mathcal{U}(\{0,1\}^{256})}\!\bigl[H(H(\iota \,\|\, f(\iota)) \,\|\, \epsilon) < D\bigr] \;=\; \frac{D}{2^{256}}

since H(ϵ)H(\cdot \,\|\, \epsilon) with uniform ϵ\epsilon produces a uniform output independent of its first argument. No precomputed set {H(ιν):νV}\{H(\iota \,\|\, \nu) : \nu \in \mathcal{V}\} reduces this probability. \square

Corollary 4.2. ASIC precomputation of rainbow tables over all possible (ι,ν)(\iota, \nu) pairs provides no advantage, because the outer pass H(ϵ[n])H(\cdot \,\|\, \epsilon[n]) requires ϵ[n]\epsilon[n] — a mandatory per-epoch network fetch.

4.3 Wallet Rotation Attack — Complete Analysis

AttackFormal Mitigation
Fresh address \Rightarrow fresh cheap challengeτ(m,n)=H(m0g)\tau(m',n) = H(m' \,\|\, 0 \,\|\, g). Distinct from all mm with γ>0\gamma > 0, but difficulty D[n]D[n] is identical. No cost reduction.
kk fresh addresses \Rightarrow suppress NCTEach address must mine at \0.10toregisterinwindow.Cost:to register in window. Cost:\sigma(k) = k \times $0.10$ (Theorem 1.9, MATH.md). Equals genuine mining cost.
Accumulate γ\gamma on one address then rotateγ\gamma is non-transferable (I13). Accumulated history stays with original address.
Replay high-γ\gamma address’s τ\tau from another addressτ\tau includes msg.sender\texttt{msg.sender} directly. mmτ(m,n)τ(m,n)m \neq m' \Rightarrow \tau(m,n) \neq \tau(m',n) (collision resistance of HH).

Theorem 4.3 (Rotation Equivalence). For all k1k \geq 1 and window cycles:

σ(k)  =  k×fee\sigma(k) \;=\; k \times \texttt{fee}

The Sybil cost per apparent independent miner equals the legitimate mining cost. No adversary benefits from wallet rotation. \square

4.4 NCT Attacks

AttackMitigation
Sybil: fake distribution with kk wallets\sigma(k) = k \times \0.10$ (Corollary 1.10). No advantage.
Window stuffing: saturate buffer to reset CuC_uSame \0.10/\text{slot}$ cost floor; no free writes.
Reverse Sybil: concentrate to harm competitorsNCT penalty unct[0,0.3]u_{\text{nct}}\in[0,0.3] raises difficulty for everyone, including the concentrator.
Manipulate CuC_u via reentrancynonReentrant modifier; CuC_u and windowFreq updated atomically before any external calls.

Concentration equilibrium. The NCT update function:

D[n+1]=D[n]exp ⁣(upi[n]+min ⁣(0.1(C[n]1),0.3))D[n+1] = D[n] \cdot \exp\!\Bigl(u_{\text{pi}}[n]+\min\!\bigl(0.1(C[n]-1),\,0.3\bigr)\Bigr)

achieves its minimum over strategies Cu[1,256]C_u \in [1,256] at Cu=256C_u = 256 (since unctu_{\text{nct}} is minimised, i.e., zero). This is the unique Nash equilibrium.

4.5 Replay and Cross-Miner Attacks

AttackFormal Exclusion
Resubmit nonce in next epochϵ[n+1]ϵ[n]\epsilon[n+1] \neq \epsilon[n] with probability 1D/22561 - D/2^{256} (random oracle independence)
Steal nonce solved by miner mm and submit as mm'τ(m,n)τ(m,n)\tau(m',n) \neq \tau(m,n) for mmm \neq m' \Rightarrow ι(m,n)ι(m,n)\iota(m',n) \neq \iota(m,n) \Rightarrow valid nonce for mm is invalid for mm'
Replay across deploymentsgenesisBlockhash\texttt{genesisBlockhash} differs across deployments; τ\tau is genesis-bound
Replay across chain forksContract address differs; τ\tau is address-included

Proposition 4.4. For mmm \neq m', Pr[κ(m,ν,n)=κ(m,ν,n)]2256\Pr[\kappa(m,\nu,n) = \kappa(m',\nu,n)] \leq 2^{-256} (collision resistance of HH, applied at the τ\tau-layer). \square

4.6 Oracle Attack Surface

The fee formula fee_wei=1025/p\texttt{fee\_wei} = 10^{25} / p depends on the Chainlink ETH/USD feed. Attack surface and mitigations:

ScenarioOn-Chain ResponseOff-Chain Response
Stale price ($t - \texttt{updatedAt}> 24;\text{hours}$)
answeredInRound < roundIdRevert StaleOracle (O5)
p0p \leq 0Revert InvalidOracle
updatedAt>block.timestamp\texttt{updatedAt} > \texttt{block.timestamp}Revert OracleClockSkew
Inflated pp (oracle compromise)fee_wei=1025/p0\texttt{fee\_wei} = 10^{25}/p \to 0: mine cheapens, not blocksClient-side MAX_FEE_USD aborts if \texttt{fee\_wei}/p_\ > \text{cap}$
Deflated pp (oracle compromise)fee_wei\texttt{fee\_wei} inflates: mine becomes expensiveClient-side MAX_FEE_USD aborts submission
Round ID sequence gap (phase boundary)Not checked — gap check produces false positives

Risk. Oracle compromise can cause temporary fee spikes or drops. It cannot cause loss of minted ANVL, cannot modify D[n]D[n], and cannot violate I1–I13.

4.7 Difficulty Controller — Stability Under Adversarial Input

The controller operates on lnD\ln D. Its stability properties under adversarial hashrate perturbations:

PerturbationBoundMechanism
Hashrate flash spike ×100\times 100DD can at most ×4\times 4 per periodOuter sat(D[n]exp(u),D/4,4D)\mathbf{sat}(D[n]\cdot\exp(u),\,D/4,\,4D) envelope
Sustained hashrate increasePI integrator accumulates error; convergesLyapunov stable (MATH.md §3.3)
Integrator windup$I
NCT overflowCwad256×WADC_{\text{wad}} \leq 256 \times \text{WAD}; tax0.3WAD\text{tax} \leq 0.3\,\text{WAD}Cu1C_u \geq 1 (I11) bounds CC above
Difficulty collapse to zeroD1D \geq 1 alwaysFloor enforced in PIController.step (I5)

4.8 Fee Mechanics — Accounting Invariant

Invariant I7 (Fee Accounting). After every successful mine():

ΔFrecipient+ΔFstuck+ΔFLP  =  currentFeeWei()\Delta F_{recipient}+\Delta F_{stuck}+\Delta F_{LP} \;=\; \texttt{currentFeeWei}()

where devFee is 50% of the protocol fee and is forwarded to feeRecipient (or parked in stuckFeesWei on failure), and lpFee is 50% and accumulates in lpReserveEthWei.

Proof sketch. The mine() function:

  1. Reads f=currentFeeWei()f = \texttt{currentFeeWei}()
  2. Asserts msg.valuef\texttt{msg.value} \geq f; refunds excess (msg.valuef)(\texttt{msg.value} - f) to msg.sender
  3. Splits: fLP=f×5,000/10,000=f/2f_{\text{LP}} = f \times 5{,}000 / 10{,}000 = f/2; fdev=ffLP=f/2f_{\text{dev}} = f - f_{\text{LP}} = f/2
  4. Accumulates fLPf_{\text{LP}} into lpReserveEthWei (always succeeds, no external call)
  5. Attempts feeRecipient.call{value: f_dev}(""):
    • On success: feeRecipient.balance += f_dev. ΔstuckFeesWei=0\Delta\texttt{stuckFeesWei} = 0.
    • On failure: stuckFeesWei += f_dev. ΔfeeRecipient.balance=0\Delta\texttt{feeRecipient.balance} = 0.

In all branches: ΔfeeRecipient.balance+ΔstuckFeesWei=fdev\Delta\texttt{feeRecipient.balance} + \Delta\texttt{stuckFeesWei} = f_{\text{dev}} and ΔlpReserveEthWei=fLP\Delta\texttt{lpReserveEthWei} = f_{\text{LP}}, so their sum equals ff. stuckFeesWei is sweepable permissionlessly to protect against DoS on feeRecipient. \square

Note. The stuckFeesWei mechanism applies only to devFee. The LP share goes directly to lpReserveEthWei via in-contract accounting and is never subject to a reverting external call.

4.9 MEV and Sequencer Reordering

Each mine() is statistically independent. The valid nonce for miner mm is invalid for mmm' \neq m (Proposition 4.4). Therefore:

  • Front-running is unprofitable: a front-runner cannot use miner mm‘s nonce; they must solve the puzzle for their own address.
  • Reordering only transfers the slot: if two valid mine() calls from m1m_1 and m2m_2 compete, reordering awards the epoch to one of them — no additional value is extractable.
  • No arbitrage exists: unlike DEX swaps, mine() contains no price-sensitive state to exploit.

Formal: \nexists MEV strategy with positive expected value beyond the mining reward itself, which requires solving the Cascade PoW.

4.10 Supply Chain — Build Integrity

LayerGuaranteeMechanism
Source codePublic, reviewedGitHub; audit report published
BinaryReproducible buildDocker image pins compiler versions; sha256sums.txt matches across builds
Release signingCosign + Sigstore Rekorcosign verify-blob with GitHub OIDC identity
ContractBytecode verifiedBasescan source verification; bytecode hash matches signed release

5. Out of Scope

  • Sequencer-level censorship on Base validators.
  • Chainlink’s internal oracle security model and aggregator architecture.
  • Operator wallet hygiene and private key storage.
  • Power or infrastructure failures on the operator’s machine.
  • Consensus-layer forks of Base

6. Invariants That Are Never In Scope for Modification

The following properties are on-chain invariants of the immutable contract. No future action can modify them:

  admin_key,  upgrade_proxy,  governance_token\nexists\;\text{admin\_key},\quad \nexists\;\text{upgrade\_proxy},\quad \nexists\;\text{governance\_token} devPremine=0,genesisLPSeed=1  ANVL,feeRecipientdeployer  (on-chain, constructor)\texttt{devPremine}=0,\quad \texttt{genesisLPSeed}=1\;\mathrm{ANVL},\quad \texttt{feeRecipient} \neq \texttt{deployer}\;\text{(on-chain, constructor)}   admin mint,  token migration mechanism\nexists\;\text{admin mint},\quad \nexists\;\text{token migration mechanism}

7. Responsible Disclosure

Vulnerability reports: security@anvil256.xyz (PGP key published on website). Coordinated disclosure window: 90 days from acknowledgement.