PROPELOO

CONTINUOUS SMART CONTRACT SECURITY

Build contracts that hold under adversarial conditions.

PROPELOO engineers smart contract security from the ground up — designing contracts to be secure by construction, not just audited after the fact. Security is not a review you schedule before mainnet. It is an architectural constraint you enforce from the first line of Solidity. We embed security engineering into every phase of contract development.

The most expensive smart contract bugs were designed in — not coded in. No code review would have caught them.

The distinction between a smart contract audit and smart contract security engineering is the distinction between finding vulnerabilities and not creating them. The Ronin bridge hack ($625M) was a validator set configuration mistake — invisible to a code review. The Nomad bridge hack ($190M) was an incorrect initialization that made every message valid — a single line that passed code review. The Euler Finance hack ($200M) exploited a flawed interaction between two correctly-audited functions. Security engineering means designing protocols that cannot be in invalid states, testing invariants that must always hold, and questioning the economic assumptions before writing the first line of code. PROPELOO applies security engineering throughout development — not as a final gate.

Security engineering across the contract lifecycle.

Security at the design phase costs hours. Security at the audit phase costs weeks. Security after an exploit costs everything.

System Layers

  • Security Design Layer: Threat modelling, invariant definition, attack surface mapping before implementation begins
  • Secure Development Layer: Secure coding patterns, checks-effects-interactions, access control design, safe math
  • Testing & Verification Layer: Unit tests, fuzz testing, invariant testing, formal verification for critical properties
  • Static Analysis Layer: Slither, Mythril, Semgrep — automated vulnerability detection on every commit
  • Audit Preparation Layer: Documentation, test coverage, invariant documentation, PoC reproduction for third-party audit

Core Technical Capabilities

  • Security-First Design

    Pre-implementation security design: threat model the contract system, define invariants that must always hold, identify trust boundaries and external dependencies, and map the economic attack surface before writing code.

  • Secure Coding Patterns

    Checks-effects-interactions enforcement, reentrancy guards, pull-over-push payment patterns, safe arithmetic, access control with role-based permissions, and circuit breakers for emergency pause.

  • Invariant & Fuzz Testing

    Echidna property testing for protocol invariants — properties that must hold regardless of input sequence. Foundry fuzz testing for function-level edge cases. Medusa for stateful fuzzing of complex state machines.

  • Formal Verification

    Certora Prover or Halmos for mathematical proof of critical properties — proving that a function cannot overflow, that access control cannot be bypassed, or that a protocol invariant holds for all possible inputs.

  • Continuous Static Analysis

    Slither and Semgrep integrated into CI pipeline — every commit triggers automated vulnerability detection. Custom Slither detectors for protocol-specific vulnerability patterns. Mythril for symbolic execution of individual functions.

  • Incident Response Architecture

    Emergency pause mechanisms, circuit breakers on anomalous volume, upgrade paths for critical fixes, guardian multi-sig for rapid response and communication plans for security incidents.

How we think about smart contract security.

Security is not a property of code — it is a property of systems. Individually correct contracts can interact in ways that create critical vulnerabilities. System-level security requires system-level thinking.

  • Define invariants before writing code

    An invariant is a property that must always be true — "total shares issued never exceeds total deposits," "only the owner can withdraw," "price can never be manipulated within a single block." Defining invariants before implementation gives you a specification to test against and a mental model that identifies when a code change is dangerous. Invariants that are not explicitly defined are invariants that get broken.

    Axiom:

  • Test what can go wrong, not what should go right

    Unit tests verify that the happy path works. Fuzz testing and invariant testing find the inputs that break your assumptions. The difference is the difference between testing that a function returns the correct value for ten expected inputs and testing that it handles all possible inputs correctly. For financial contracts, the ten expected inputs are the easy case. The adversarial inputs are what matter.

    Axiom:

  • Composability risk is system risk

    A contract that accepts LP tokens as collateral inherits the price risk of the underlying AMM. A protocol that depends on Chainlink inherits Chainlink's liveness risk. A governance system that can be flash-loan attacked inherits the capital availability of every lending protocol on the same chain. Every external dependency is a trust assumption. Map them all before deployment.

    Axiom:

  • Emergency response must be pre-designed

    When a critical vulnerability is discovered or exploited, you have minutes to hours before losses compound. Emergency pause mechanisms must be designed before deployment — not improvised during an incident. The pause function must be callable quickly (multi-sig with low threshold), must stop the specific dangerous operations (not everything), and must have a documented upgrade path for the fix.

    Axiom:

The security decisions that define contract safety.

These architectural choices determine your attack surface before a single line of business logic is written.

  • Upgradeable vs immutable contracts?

    Impact: Immutable contracts for simple, well-specified systems (ERC-20 tokens, simple escrow). Upgradeable contracts for complex protocols where post-deployment fixes may be required — but the upgrade mechanism itself must be audited and governed with a timelock.

    • Immutable — maximum trustlessness, no post-deployment fixes, audit once and deploy
    • Transparent proxy (EIP-1967) — upgradeable, storage layout collision risk, well-understood
    • UUPS proxy — gas-efficient, upgrade logic in implementation, requires careful implementation
    • Beacon proxy — upgrade all instances simultaneously, best for factory patterns
  • Access control pattern?

    Impact: Never use a single EOA as owner for contracts holding significant value. Multi-sig minimum for all admin functions. Timelock-gated governance for parameter changes that could affect user funds.

    • Ownable (single owner) — simple, single point of failure, appropriate for simple contracts
    • AccessControl (role-based) — granular permissions, multiple roles, best for complex systems
    • Multi-sig (Gnosis Safe) — distributed key, no single point of failure, best for owner roles
    • Governor + timelock — fully on-chain governance, decentralised, slow response
  • Reentrancy protection?

    Impact: ReentrancyGuard as default on all state-changing functions that make external calls. CEI pattern as a second layer of defence. These are not alternatives — use both.

    • ReentrancyGuard on all external call functions — safe default, slight gas overhead
    • Checks-effects-interactions (CEI) pattern — no extra gas, requires discipline to maintain
    • Pull-over-push payment pattern — eliminates reentrancy risk in payment flows
    • No protection — acceptable only for view functions and functions with no state changes
  • Oracle architecture?

    Impact: Never use spot price from an on-chain pool for any security-critical calculation in a contract you control. This is not a theoretical risk — it is the mechanism behind dozens of the largest DeFi exploits.

    • Chainlink price feeds — manipulation resistant, audited, most pairs available
    • TWAP from on-chain pool — manipulation resistant over time, lags spot
    • Multi-source with deviation check — redundancy, circuit breaker on divergence
    • Spot price from own pool — never acceptable, trivially manipulable via flash loan
  • Emergency response mechanism?

    Impact: For any protocol with significant TVL: pause function callable by multi-sig (not full governance — too slow) with timelock-gated governance for parameter changes. Circuit breakers on anomalous volume add an automatic layer without requiring human intervention.

    • Pause function with multi-sig — fast response, centralisation risk
    • Guardian council + governance — distributed pause authority, slightly slower
    • Automatic circuit breakers — trigger on anomalous volume/price, no human required
    • No emergency mechanism (immutable) — maximum decentralisation, no fix path
  • Formal verification scope?

    Impact: For protocols with >$10M expected TVL: Certora Prover for critical invariants (access control cannot be bypassed, supply caps cannot be exceeded) is justified. Full formal verification is reserved for the most critical infrastructure (bridges, custody systems).

    • No formal verification — relies entirely on testing and audit
    • Critical invariants only (Certora) — mathematically prove the most dangerous properties
    • Full specification (K Framework) — complete formal model, extreme cost and complexity
    • Symbolic execution (Halmos) — automated property checking, intermediate cost/coverage

What PROPELOO secures.

  • Security Design Review

    Pre-implementation security architecture review — threat model, invariant definition, trust boundary mapping and attack surface analysis before any code is written.

  • Secure DeFi Protocol Development

    Protocol development with security engineering embedded throughout — invariant testing from day one, static analysis in CI, economic attack modelling and audit preparation.

  • Invariant & Fuzz Testing Suite

    Comprehensive Echidna + Foundry fuzz testing suite for existing contracts — invariant definition, property testing and edge case coverage that unit tests miss.

  • Emergency Response Architecture

    Design and implementation of pause mechanisms, circuit breakers, multi-sig guardian setup and incident response runbooks for live protocols.

  • Upgrade Security Review

    Security review of proxy upgrades — storage slot collision detection, initialisation order verification, access control on upgrade functions and re-audit of changed code.

  • Pre-audit Hardening

    Code hardening before third-party audit — fixing automated scanner findings, improving test coverage, adding NatSpec documentation and preparing invariant documentation for auditors.

The smart contract security toolchain.

Security at every phase requires different tools. The combination produces coverage that no single tool provides.

  • Static Analysis

    Stack: Slither, Mythril, Semgrep (custom rules), 4naly3er, Aderyn

  • Fuzz & Property Testing

    Stack: Echidna, Medusa, Foundry (fuzz), Halmos (symbolic)

  • Formal Verification

    Stack: Certora Prover, K Framework, SMTChecker (built-in), Halmos

  • Development & Testing

    Stack: Foundry, Hardhat, OpenZeppelin Contracts, OpenZeppelin Defender

  • Economic Modelling

    Stack: Tenderly fork simulation, Python attack PoC scripts, Foundry mainnet fork, Dune Analytics

  • Monitoring

    Stack: OpenZeppelin Sentinel, Forta Network, Tenderly Alerts, Custom event monitoring

The vulnerability classes we engineer against.

Each requires prevention at a different layer — design, implementation or testing.

  • Reentrancy (all variants)

    Single-function reentrancy, cross-function reentrancy, cross-contract reentrancy and read-only reentrancy each require different detection approaches. Read-only reentrancy — where the attacker reenters a view function that is used to calculate a price or ratio — is missed by most automated tools. ReentrancyGuard and checks-effects-interactions are both required; they complement rather than substitute for each other.

  • Price Oracle Manipulation

    Any security-critical calculation that reads a price from a manipulable source is an attack vector. Spot price from an on-chain AMM can be moved by a flash loan in the same block. TWAP oracles reduce this risk but introduce staleness. Chainlink provides manipulation-resistant feeds for major assets. The design rule: never use spot price for liquidation, minting or collateral valuation.

  • Access Control Failures

    Missing access control on privileged functions, initialisation functions that can be called by anyone on proxy contracts, admin functions that should be multi-sig-gated left as single-owner, and role assignment logic that can be manipulated. Access control review must cover every external and public function — not just the obviously sensitive ones.

  • Integer Arithmetic

    Solidity 0.8+ prevents basic overflow/underflow but unchecked blocks re-enable it. Division before multiplication causes precision loss. Incorrect rounding direction (rounding in favour of the user rather than the protocol) accumulates losses. Compound interest calculations with fixed-point arithmetic must be validated against reference implementations.

  • Flashloan Attack Vectors

    Any protocol assumption that can be violated by temporarily holding a large amount of any token is a flash loan vulnerability. This includes: price readings from on-chain sources, governance snapshots at the current block, collateral calculations mid-transaction, and single-block liquidity assumptions. Map all flash loan attack surfaces explicitly during design.

  • Upgrade & Proxy Vulnerabilities

    Storage slot collision between proxy and implementation contracts, uninitialised proxy contracts, delegatecall to user-controlled addresses, selfdestruct in implementation contracts, and upgrade functions accessible without timelocks. Upgradeable contracts have a significantly larger attack surface than immutable contracts. The upgrade mechanism must be as carefully designed as the protocol itself.

Security engineering across the development lifecycle.

  1. 01. Security Design

    Threat model the system, define protocol invariants, map trust boundaries, identify external dependencies and document the attack surface before implementation begins.

  2. 02. Secure Implementation

    Contract development with security patterns enforced: CEI pattern, ReentrancyGuard, access control, safe arithmetic, and Slither in CI from commit one.

  3. 03. Invariant Testing

    Echidna property testing suite for all protocol invariants. Foundry fuzz testing for function edge cases. Medusa for stateful fuzzing of complex state machines.

  4. 04. Economic Attack Modelling

    Flash loan simulation, oracle manipulation PoC, governance attack analysis and liquidity drain scenarios using Tenderly fork testing.

  5. 05. Static Analysis

    Full Slither run, Mythril symbolic execution, Semgrep custom rules and 4naly3er review. All findings triaged and addressed before audit.

  6. 06. Audit Preparation

    NatSpec documentation, invariant documentation, test coverage report, architecture overview and known issue documentation for third-party auditors.

  7. 07. Post-audit Monitoring

    OpenZeppelin Sentinel or Forta alerts for anomalous on-chain behaviour, emergency response playbooks and guardian multi-sig setup.

Frequently Asked Questions

What is the difference between security engineering and a security audit?

A security audit reviews finished code for vulnerabilities. Security engineering embeds security throughout development — designing contracts with explicit invariants, testing those invariants with fuzz testing, running static analysis on every commit, and modelling economic attacks before implementation. The output of security engineering is contracts that are hard to exploit by construction. The output of an audit is a list of vulnerabilities in existing code. Both are needed; security engineering reduces what the audit finds.

What are invariants and why do they matter?

A protocol invariant is a property that must always be true regardless of what operations have been performed — "the sum of all user balances equals the total supply," "the collateral value of every vault always exceeds its debt," "only the governance timelock can change protocol parameters." Defining invariants explicitly before implementation gives you a testable specification. Echidna and Foundry fuzz testing can then systematically try to violate them. Invariants that are not explicitly defined are invariants that get broken during development.

What is read-only reentrancy?

Standard reentrancy attacks reenter a state-modifying function to drain funds or manipulate state. Read-only reentrancy is subtler: the attacker reenters a view function — which reads state that is temporarily in an inconsistent state during a transaction. If another protocol uses that view function to calculate a price or ratio, it reads an incorrect value. This has been exploited against protocols built on top of Curve, where the virtual price was readable mid-transaction in an inconsistent state. Most automated tools miss this class.

When should we use formal verification?

Formal verification is justified when the cost of a wrong answer is catastrophic and the property to be verified is well-defined. Specifically: for protocols expecting >$50M TVL, for bridge contracts that custody assets across chains, for stablecoin peg mechanisms, and for access control properties where bypass has irreversible consequences. Certora Prover is the practical tool for most teams — it can prove properties like "only the owner can mint" and "total shares never exceed total deposits" with mathematical certainty.

How do you prevent flash loan attacks?

Flash loan attacks exploit temporary capital availability within a single block. Prevention: never use spot price from an on-chain pool for security-critical calculations, use TWAP oracles or Chainlink, take governance snapshots at past blocks rather than current balance, use re-entrancy guards to prevent mid-transaction state manipulation, and add rate limiting or volume circuit breakers. The economic design is as important as the technical implementation — if the profit from a flash loan attack exceeds the cost, someone will execute it.

What monitoring should we have post-deployment?

At minimum: alerts on anomalous contract function calls (unusual volume, calls from new addresses with large transactions), price deviation alerts (if your protocol involves pricing), governance proposal alerts (any proposal submitted), and emergency pause event monitoring. OpenZeppelin Defender Sentinel, Forta Network and Tenderly Actions all provide this. The goal is to know about a potential exploit before users do — ideally with enough time to pause the protocol before losses become catastrophic.

What is the Certora Prover?

Certora Prover is a formal verification tool for Solidity smart contracts. You write "specifications" in Certora Verification Language (CVL) that describe properties your contract must satisfy — for example, "after any call to withdraw(), the caller's balance decreases by exactly the withdrawn amount." The Prover mathematically verifies that these properties hold for all possible inputs and all possible states. It is the only tool that can provide certainty rather than confidence. Major protocols including Aave, Compound and MakerDAO use Certora Prover.

How much test coverage is enough?

100% line coverage is necessary but not sufficient. A contract with 100% line coverage can have critical vulnerabilities that are never triggered by the test inputs the developer wrote. The correct measure is: 100% line coverage + invariant testing with Echidna covering all protocol invariants + fuzz testing for all functions with numeric inputs + edge case coverage for boundary values. Coverage is a floor, not a ceiling. The goal is to test all the ways things can go wrong, not just the ways things should go right.