Ante Test Examples

Sample Ante Tests to dissect for explanation

A Note on Writing Ante Tests

Take care in defining your project's guarantees precisely.

Only choose guarantees that you believe will never fail.

It's this guarantee that you are making and potentially staking ETH on.

Example: WBTC Supply Doesn't Exceed 21 Million

In this test (AnteWBTCSupplyTest.sol) we write an Ante Test that checks the total outstanding supply of wrapped Bitcoin (WBTC) does not exceed 21 million, which is the known "maximum supply" of Bitcoin.

For more information on what wrapped Bitcoin is, please see https://wbtc.network/ (external link).

AnteWBTCSupplyTest.sol
// SPDX-License-Identifier: GPL-3.0-only

// ┏━━━┓━━━━━┏┓━━━━━━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━━━━━
// ┃┏━┓┃━━━━┏┛┗┓━━━━━━━━┃┏━━┛━━━━━━━━━━━━━━━━━━━━━━━
// ┃┗━┛┃┏━┓━┗┓┏┛┏━━┓━━━━┃┗━━┓┏┓┏━┓━┏━━┓━┏━┓━┏━━┓┏━━┓
// ┃┏━┓┃┃┏┓┓━┃┃━┃┏┓┃━━━━┃┏━━┛┣┫┃┏┓┓┗━┓┃━┃┏┓┓┃┏━┛┃┏┓┃
// ┃┃ ┃┃┃┃┃┃━┃┗┓┃┃━┫━┏┓━┃┃━━━┃┃┃┃┃┃┃┗┛┗┓┃┃┃┃┃┗━┓┃┃━┫
// ┗┛ ┗┛┗┛┗┛━┗━┛┗━━┛━┗┛━┗┛━━━┗┛┗┛┗┛┗━━━┛┗┛┗┛┗━━┛┗━━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

pragma solidity ^0.7.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../AnteTest.sol";

/// @title WBTC supply never exceeds 21 million test
/// @notice Ante Test to check that WBTC supply is always less than 21 million
contract AnteWBTCSupplyTest is AnteTest("Wrapped BTC (WBTC) supply doesn't exceed 21m") {
    // https://etherscan.io/address/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599#code
    address public immutable wBTCAddr;

    //21 million * 1e8 (for decimals), maximum total Bitcoin supply
    uint256 public constant THRESHOLD_SUPPLY = 21 * 1000 * 1000 * 1e8;

    IERC20 public wBTCToken;

    /// @param _wBTCAddr WBTC contract address (0x2260fac5e5542a773aa44fbcfedf7c193bc2c599 on mainnet)
    constructor(address _wBTCAddr) {
        protocolName = "WBTC";
        testedContracts = [_wBTCAddr];

        wBTCAddr = _wBTCAddr;
        wBTCToken = IERC20(_wBTCAddr);
    }

    /// @notice test to check WBTC token supply
    /// @return true if WBTC supply is less than 21 million
    function checkTestPasses() external view override returns (bool) {
        return (wBTCToken.totalSupply() <= THRESHOLD_SUPPLY);
    }
}

AnteWBTCSupplyTest.sol as of 2021-11-26

This example outlines a sample Ante Test that uses the AnteTest.sol abstract contract that inherits IAnteTest.sol for use.

An explanation for the various lines are as follows:

  • (Line 15) - Importing the contract AnteTest.sol

    • Future work: Will be packaged to a library for importing for ease of use

  • (Line 19) - Contract inherits from AnteTest using ... is AnteTest(...). It is here where the test name will be decided.

    • Recommended to have a human-readable description passed as in AnteTest() (e.g. "Wrapped BTC (WBTC) supply ...")

  • (Lines 21-26) - Implement the variables and logic as needed for the test.

    • In this example, we define the variable for the WBTC address, it is set in the constructor (Line 33), and for clarity we wrote it in the comments (Line 20).

    • Following which, we set our threshold limit which is 21M, the maximum total Bitcoin supply.

    • Using the IERC20 interface provided by @openzeppelin, we define WBTC from the address.

  • (Lines 30-31) - The part of the constructor that tells Ante the name of the tested protocol and the tested contracts (for front-end convenience).

  • (Lines 33-34) - Defines the WBTC address from the constructor, and then uses the IERC20 interface defined earlier to read the WBTC.

  • (Line 39) - The checkTestPasses() function here does the final return on whether the test passes or not, which should be expected to be true, as the "guarantee" behind the Ante Test.

    • In this example, the wrapped BTC total supply should not exceed the threshold we set earlier (21M)

    • This should be assumed true, unless something extraordinary happens.

In this example above, the Ante Test guarantees that the WBTC circulation never exceeds 21 million.

Example: WETH9 Minted WETH equals ETH Balance

In this test (AnteWETH9Test.sol) we check that the ETH deposited into the wrapped ETH (WETH9) contract does in fact equal the total number of WETH tokens (ERC20 compliant "ETH") circulating.

For more background around what is wrapped Ether we recommend reading https://weth.io/ (external link).

AnteWETH9Test.sol
// SPDX-License-Identifier: GPL-3.0-only

// ┏━━━┓━━━━━┏┓━━━━━━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━━━━━
// ┃┏━┓┃━━━━┏┛┗┓━━━━━━━━┃┏━━┛━━━━━━━━━━━━━━━━━━━━━━━
// ┃┗━┛┃┏━┓━┗┓┏┛┏━━┓━━━━┃┗━━┓┏┓┏━┓━┏━━┓━┏━┓━┏━━┓┏━━┓
// ┃┏━┓┃┃┏┓┓━┃┃━┃┏┓┃━━━━┃┏━━┛┣┫┃┏┓┓┗━┓┃━┃┏┓┓┃┏━┛┃┏┓┃
// ┃┃ ┃┃┃┃┃┃━┃┗┓┃┃━┫━┏┓━┃┃━━━┃┃┃┃┃┃┃┗┛┗┓┃┃┃┃┃┗━┓┃┃━┫
// ┗┛ ┗┛┗┛┗┛━┗━┛┗━━┛━┗┛━┗┛━━━┗┛┗┛┗┛┗━━━┛┗┛┗┛┗━━┛┗━━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

pragma solidity ^0.7.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../AnteTest.sol";

/// @title WETH9 issued fully backed by ETH test
/// @notice Ante Test to check WETH9 minted WETH matches deposited ETH in contract
contract AnteWETH9Test is AnteTest("Checks WETH9 issued WETH fully backed by ETH") {
    // https://etherscan.io/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    address public immutable wETH9Addr;

    IERC20 public wETH9Token;

    /// @param _wETH9Addr WETH9 contract address (0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 on mainnet)
    constructor(address _wETH9Addr) {
        wETH9Addr = _wETH9Addr;
        wETH9Token = IERC20(_wETH9Addr);

        protocolName = "WETH9";
        testedContracts = [_wETH9Addr];
    }

    /// @notice test to check WETH token supply against contract balance
    /// @return true if WETH9 token supply equals contract balance
    function checkTestPasses() external view override returns (bool) {
        return address(wETH9Token).balance == wETH9Token.totalSupply();
    }
}

AnteWETH9Test.sol as of 2021-11-26

Building on the previous example, note the important lines in the WETH9 Ante Test:

  1. (Line 15) - Import the AnteTest.sol abstract class

  2. (Line 19) - Name the test with a human-readable string "Checks WETH9..."

  3. (Line 21-23) - We define the needed variables here

    1. (Line 21) - Defines the address of the WETH9 contract to be set later in the constructor, additionally, added for clarity into the comments (Line 20).

    2. (Line 23) - We define the IERC20 interface as WETH9Token to later interact with it

  4. (Lines 27-28) - Defines the WETH9 address from the constructor, and then uses the IERC20 interface defined earlier to read the WETH9.

  5. (Lines 30-31) - The part of the constructor that tells Ante the name of the tested protocol and the tested contracts (for front-end convenience).

  6. (Line 37) - We define the invariant for WETH9 with a boolean check

    1. Left side: we get the ETH balance of the address of the created WETH9 ERC20 token

    2. Right side: we get the outstanding supply of WETH9Token

    3. Invariant: the ETH deposited into WETH9 should always equal the WETH supply

Example: ETH2DepositContract Balance "Rugs"

For context, in the lead-up to the launch of Ethereum2's beacon chain in late 2020, the Ethereum Foundation helped put together a "deposit contract" (with the ENS name DEPOSITCONTRACT.ETH that was paid for for 150 years and then had ownership burned) where people could, following a set of instructions, deposit ETH in multiples of 32 to spin up ETH2 validator nodes that earn Proof-of-Stake rewards for correctly helping to secure the Beacon Chain. For more information, please see https://ethereum.org/en/eth2/

In this test, we tongue-in-cheek check that the balance in DepositContract.eth does not drop by 99.99% (indicating that it is likely it has been compromised and a thief has stolen all of the funds).

Note that it is possible for a clever thief to steal all but 501 ETH, in which case this Ante Test would still pass. While that is clearly a failure case of this Ante Test, we hope that for illustrative purposes this example shows a way to use Ante.

AnteETH2DepositTest.sol
// SPDX-License-Identifier: GPL-3.0-only

// ┏━━━┓━━━━━┏┓━━━━━━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━━━━━
// ┃┏━┓┃━━━━┏┛┗┓━━━━━━━━┃┏━━┛━━━━━━━━━━━━━━━━━━━━━━━
// ┃┗━┛┃┏━┓━┗┓┏┛┏━━┓━━━━┃┗━━┓┏┓┏━┓━┏━━┓━┏━┓━┏━━┓┏━━┓
// ┃┏━┓┃┃┏┓┓━┃┃━┃┏┓┃━━━━┃┏━━┛┣┫┃┏┓┓┗━┓┃━┃┏┓┓┃┏━┛┃┏┓┃
// ┃┃ ┃┃┃┃┃┃━┃┗┓┃┃━┫━┏┓━┃┃━━━┃┃┃┃┃┃┃┗┛┗┓┃┃┃┃┃┗━┓┃┃━┫
// ┗┛ ┗┛┗┛┗┛━┗━┛┗━━┛━┗┛━┗┛━━━┗┛┗┛┗┛┗━━━┛┗┛┗┛┗━━┛┗━━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

pragma solidity ^0.7.0;

import "../AnteTest.sol";

/// @title ETH2 beacon contract doesn't lose 99.99% of its ETH test
/// @notice Ante Test to check that ETH2 beacon depositcontract.eth doesn't lose 99.99% of
/// its ETH (as of May 2021)
contract AnteETH2DepositTest is AnteTest("ETH2 beacon deposit contract doesn't lose 99.99% of its ETH") {
    // depositcontract.eth, verified on https://ethereum.org/en/eth2/deposit-contract/
    // https://etherscan.io/address/0x00000000219ab540356cBB839Cbe05303d7705Fa
    address public immutable depositContractAddr;

    // As of 20210524 with 4.88m ETH deposited, 500 ETH represents a ~ -99.99% drop
    uint256 public constant THRESHOLD_BALANCE = 500 * 1e18; //500 ETH

    /// @param _depositContractAddr ETH2 deposit address (0x00000000219ab540356cBB839Cbe05303d7705Fa on mainnet)
    constructor(address _depositContractAddr) {
        protocolName = "ETH2";
        depositContractAddr = _depositContractAddr;
        testedContracts = [_depositContractAddr];
    }

    /// @notice test to check balance of eth2 deposit address
    /// @return true if deposit address balance is over 500 ETH
    function checkTestPasses() external view override returns (bool) {
        return (depositContractAddr.balance >= THRESHOLD_BALANCE);
    }
}

AnteETH2DepositTest.sol as of 2021-11-26

Breakdown of key lines:

  1. (Lines 3-10) - ASCII Art we are very proud of that we definitely didn't rip off of DepositContract.eth :)

  2. (Lines 22-25) - We define key state variables

    1. (Line 22) - We define the variable for the address, this is set when deploying the Ante Test as defined in the constructor, additionally, we write it for clarity in the comments (Line 21)

    2. (Line 25) - We define the "Threshold" at which we consider a "failure" to have happened (500 ETH)

  3. (Lines 29-31) - The constructor that tells Ante the name of the tested protocol, sets the ETH2 contract address, and sets the tested contracts. Note we only set the tested contract address here!

  4. (Line 37) - We check that the Deposit Contract still has greater than or equal to THRESHOLD_BALANCE.

Example: EthDev Doesn't "Rug" Test

(Note: we use the term "rug" here casually -- as in, some large fraction of the balance in the contract vanishes from the address.)

The address described below is often labeled publicly as an "EthDev" (Ethereum Foundation's developer fund) address. Many online joke during market downturns that it is due to "EthDev" selling ETH or "EthDev Rugging." We do not support such rumors, but we figured it could make for an amusing illustrative example of what is possible with Ante.

This test checks that at least 1% of the ETH sitting in the "EthDev" address is still there. (Note: this is not meant to be an extremely robust Ante Test because EthDev's controlling parties could, for perfectly legitimate reasons, decide to move any or all of their ETH to a new address at any time.) Please view this as an Ante Test written purely for educational purposes.

AnteEthDevRugTest.sol
// SPDX-License-Identifier: GPL-3.0-only

// ┏━━━┓━━━━━┏┓━━━━━━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━━━━━
// ┃┏━┓┃━━━━┏┛┗┓━━━━━━━━┃┏━━┛━━━━━━━━━━━━━━━━━━━━━━━
// ┃┗━┛┃┏━┓━┗┓┏┛┏━━┓━━━━┃┗━━┓┏┓┏━┓━┏━━┓━┏━┓━┏━━┓┏━━┓
// ┃┏━┓┃┃┏┓┓━┃┃━┃┏┓┃━━━━┃┏━━┛┣┫┃┏┓┓┗━┓┃━┃┏┓┓┃┏━┛┃┏┓┃
// ┃┃ ┃┃┃┃┃┃━┃┗┓┃┃━┫━┏┓━┃┃━━━┃┃┃┃┃┃┃┗┛┗┓┃┃┃┃┃┗━┓┃┃━┫
// ┗┛ ┗┛┗┛┗┛━┗━┛┗━━┛━┗┛━┗┛━━━┗┛┗┛┗┛┗━━━┛┗┛┗┛┗━━┛┗━━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

pragma solidity ^0.7.0;

import "../AnteTest.sol";

/// @title ETHDev multisig doesn't rug test
/// @notice Ante Test to check if EthDev multisig "rugs" 99% of its ETH (as of May 2021)
contract AnteEthDevRugTest is AnteTest("EthDev MultiSig Doesnt Rug 99% of its ETH Test") {
    // https://etherscan.io/address/0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae
    address public immutable ethDevAddr;

    // 2021-05-24: EthDev has 394k ETH, so -99% is ~4k ETH
    uint256 public constant RUG_THRESHOLD = 4 * 1000 * 1e18;

    /// @param _ethDevAddr eth multisig address (0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae on mainnet)
    constructor(address _ethDevAddr) {
        protocolName = "ETH";
        ethDevAddr = _ethDevAddr;
        testedContracts = [_ethDevAddr];
    }

    /// @notice test to check balance of eth multisig
    /// @return true if eth multisig has over 4000 ETH
    function checkTestPasses() external view override returns (bool) {
        return ethDevAddr.balance >= RUG_THRESHOLD;
    }
}

AnteEthDevRugTest.sol as of 2021-11-26

  1. (Line 20) - Defines the EthDev address variable, this is set when deploying the Ante Test as defined in the constructor, additionally, we write it for clarity in the comments (Line 19)

  2. (Line 23) - Defines the RUG_THRESHOLD which we've set to approximately 1% of the ETH of the total amount in the address as of 2021-05-24.

  3. (Line 25-27) - The constructor that tells Ante the name of the tested protocol, sets the EthDev contract address, and sets the tested contracts.

  4. (Line 29) - Check the inequality holds using the threshold defined earlier.

Last updated