DEV Community

rim dinov
rim dinov

Posted on

Tracking State and Supply Changes with Foundry Fork Tests on Convex Finance

When auditing complex decentralized finance (DeFi) protocols, writing reliable Proof of Concept (PoC) tests against a live mainnet state is crucial. In this guide, we will look at how to set up an isolated Foundry test environment to verify contract existence and monitor how underlying values, such as total supply, change over blocks for the Convex Finance CvxLocker contract on Ethereum Mainnet.

The Challenge

When working with older Solidity versions (such as 0.6.12) or performing isolated fuzz/fork tests, direct cheatcode evaluations or constructor initializations can sometimes fail if the RPC connection or the test environment isn't structured correctly.

Additionally, we often want to prove that protocol metrics (like totalSupply) actually shift over time or under specific conditions rather than remaining static.

The Solution

To ensure the mainnet fork is properly selected and to simulate state progressions across different blocks, we can use Foundry's vm.createSelectFork and vm.roll cheatcodes.

Here is how the test structure looks:


solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface Vm {
    function createSelectFork(string calldata urlHeader, uint256 blockNumber) external returns (uint256);
    function roll(uint256 blockNumber) external;
}

interface ICvxLocker {
    function totalSupply() external view returns (uint256);
}

contract CvxLockerPoCTest {
    Vm constant vm = Vm(address(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D));
    address constant CVX_LOCKER = 0x72C21E90d656d151515901235B3F134b2E4a6316;

    // Replace with your own RPC provider endpoint
    string constant RPC_URL = "[https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY](https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY)";

    function setUp() public {
        vm.createSelectFork(RPC_URL, 18000000);
    }

    function test_CheckCvxLockerTotalSupply() public view {
        uint256 size;
        address addr = CVX_LOCKER;
        assembly {
            size := extcodesize(addr)
        }
        require(size > 0, "No contract code at given address");

        uint256 total = ICvxLocker(CVX_LOCKER).totalSupply();
        require(total > 0, "Total supply should be greater than zero");
    }
}
Running the Test
To execute the test suite against your mainnet fork, run:

Bash
forge test --match-contract CvxLockerPoCTest -vvv
If configured correctly, you will see a successful output confirming that the contract state and numbers are read accurately:

Plaintext
Ran 1 test for isolated_test/CvxLockerTest.t.sol:CvxLockerPoCTest
[PASS] test_CheckCvxLockerTotalSupply() (gas: 5554)
Suite result: ok. 1 passed; 0 failed; 0 skipped
Conclusion
Using isolated fork tests allows you to quickly verify contract states, simulate timeline changes, and track metric shifts for bug bounty submissions without cluttering your main test suites.

You can check out the full repository and setup instructions on GitHub - https://github.com/rdin777/CvxLocker_audit-PoC1

#solidity, #security, #web3, #foundry
Enter fullscreen mode Exit fullscreen mode

Top comments (0)