Ante
Ante v0.6
Search
K

Interfaces

Every Ante Test needs to implement the IAnteTest.sol interface to work with Ante. To simplify the test writing process, we've provided an abstract class AnteTest.sol that any Ante Test can inherit.

Interface IAnteTest.sol

IAnteTest.sol
// SPDX-License-Identifier: GPL-3.0-only
// ┏━━━┓━━━━━┏┓━━━━━━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━━━━━
// ┃┏━┓┃━━━━┏┛┗┓━━━━━━━━┃┏━━┛━━━━━━━━━━━━━━━━━━━━━━━
// ┃┗━┛┃┏━┓━┗┓┏┛┏━━┓━━━━┃┗━━┓┏┓┏━┓━┏━━┓━┏━┓━┏━━┓┏━━┓
// ┃┏━┓┃┃┏┓┓━┃┃━┃┏┓┃━━━━┃┏━━┛┣┫┃┏┓┓┗━┓┃━┃┏┓┓┃┏━┛┃┏┓┃
// ┃┃ ┃┃┃┃┃┃━┃┗┓┃┃━┫━┏┓━┃┃━━━┃┃┃┃┃┃┃┗┛┗┓┃┃┃┃┃┗━┓┃┃━┫
// ┗┛ ┗┛┗┛┗┛━┗━┛┗━━┛━┗┛━┗┛━━━┗┛┗┛┗┛┗━━━┛┗┛┗┛┗━━┛┗━━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
pragma solidity ^0.8.0;
/// @title The interface for the Ante V0.6 Ante Test
/// @notice The Ante V0.6 Ante Test wraps test logic for verifying fundamental invariants of a protocol
interface IAnteTest {
/// @notice Emitted when the test author is changed
/// @param previousAuthor The address of the previous author
/// @param newAuthor The address of the new author
event TestAuthorChanged(address indexed previousAuthor, address indexed newAuthor);
/// @notice Function containing the logic to set the AnteTest state and call checkTestPasses
/// @param _state The encoded data required to set the test state
/// @return A single bool indicating if the Ante Test passes/fails
function setStateAndCheckTestPasses(bytes memory _state) external returns (bool);
/// @notice Function containing test logic to inspect the protocol invariant
/// @dev This should usually return True
/// @return A single bool indicating if the Ante Test passes/fails
function checkTestPasses() external returns (bool);
/// @notice Returns the author of the Ante Test
/// @dev This overrides the auto-generated getter for testAuthor as a public var
/// @return The address of the test author
function testAuthor() external view returns (address);
/// @notice Sets the author of the Ante Test
/// @dev This can only be called by the current author, which is the deployer initially
/// @param _testAuthor The address of the test author
function setTestAuthor(address _testAuthor) external;
/// @notice Returns the name of the protocol the Ante Test is testing
/// @dev This overrides the auto-generated getter for protocolName as a public var
/// @return The name of the protocol in string format
function protocolName() external view returns (string memory);
/// @notice Returns a single address in the testedContracts array
/// @dev This overrides the auto-generated getter for testedContracts [] as a public var
/// @param i The array index of the address to return
/// @return The address of the i-th element in the list of tested contracts
function testedContracts(uint256 i) external view returns (address);
/// @notice Returns the name of the Ante Test
/// @dev This overrides the auto-generated getter for testName as a public var
/// @return The name of the Ante Test in string format
function testName() external view returns (string memory);
/// @notice Returns a string of comma delimited types used for setting the AnteTest state
/// @return The types of the state variables
function getStateTypes() external pure returns (string memory);
/// @notice Returns a string of comma delimited names used for setting the AnteTest state
/// @return The names of the state variables
function getStateNames() external pure returns (string memory);
}
IAnteTest.sol as of 2023-01-19
All Ante Tests must follow the interface outlined in IAnteTest.sol to work with Ante. The interface outlines 8 main components:
  • setStateAndCheckTestPasses(bytes): this wrapper function takes a state parameter, updates the Ante Test's internal state and calls checkTestPasses() in a single transaction. (Not all tests need state, most don't!)
  • checkTestPasses(): the core function that checks if the invariant holds. This is expected to return True. A False return value indicates a failure. NB: Reverts are not considered test failures.
  • testAuthor: this overrides the auto-generated getter and allows testAuthor to be used as a public variable.
  • setTestAuthor: this allows the current author to set the testAuthor to a different address.
  • protocolName: this overrides the auto-generated getter and allows the optional parameter protocolName to be used as a public variable.
  • testedContracts: this overrides the auto-generated getter for the optional public variable testedContracts.
  • testName: this overrides the auto-generated getter and allows testName to be used as a public variable.
  • getStateTypes: this returns a string of comma-delimited types for Ante Tests that need state. This defines the expected format of the data encoded in the bytes parameter passed in to setStateAndCheckTestPasses(bytes)
  • getStateNames: this returns a string of comma-delimited names for Ante Tests that need state. These names should describe what state values the test expects and are displayed as input labels to the test verifier in the Ante app. NB: There must a name label for each corresponding type.
To aid in working with the IAnteTest.sol interface, we've created an AnteTest.sol abstract class that any Ante Test can inherit.

Abstract Class AnteTest.sol

1
// SPDX-License-Identifier: GPL-3.0-only
2
3
// ┏━━━┓━━━━━┏┓━━━━━━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━━━━━
4
// ┃┏━┓┃━━━━┏┛┗┓━━━━━━━━┃┏━━┛━━━━━━━━━━━━━━━━━━━━━━━
5
// ┃┗━┛┃┏━┓━┗┓┏┛┏━━┓━━━━┃┗━━┓┏┓┏━┓━┏━━┓━┏━┓━┏━━┓┏━━┓
6
// ┃┏━┓┃┃┏┓┓━┃┃━┃┏┓┃━━━━┃┏━━┛┣┫┃┏┓┓┗━┓┃━┃┏┓┓┃┏━┛┃┏┓┃
7
// ┃┃ ┃┃┃┃┃┃━┃┗┓┃┃━┫━┏┓━┃┃━━━┃┃┃┃┃┃┃┗┛┗┓┃┃┃┃┃┗━┓┃┃━┫
8
// ┗┛ ┗┛┗┛┗┛━┗━┛┗━━┛━┗┛━┗┛━━━┗┛┗┛┗┛┗━━━┛┗┛┗┛┗━━┛┗━━┛
9
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
10
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
11
12
pragma solidity ^0.8.0;
13
14
import "./interfaces/IAnteTest.sol";
15
16
/// @title Ante V0.6 Ante Test smart contract
17
/// @notice Abstract inheritable contract that supplies syntactic sugar for writing Ante Tests
18
/// @dev Usage: contract YourAnteTest is AnteTest("String descriptor of test") { ... }
19
abstract contract AnteTest is IAnteTest {
20
/// @inheritdoc IAnteTest
21
address public override testAuthor;
22
/// @inheritdoc IAnteTest
23
string public override testName;
24
/// @inheritdoc IAnteTest
25
string public override protocolName;
26
/// @inheritdoc IAnteTest
27
address[] public override testedContracts;
28
29
/// @dev testedContracts and protocolName are optional parameters which should
30
/// be set in the constructor of your AnteTest
31
/// @param _testName The name of the Ante Test
32
constructor(string memory _testName) {
33
testAuthor = msg.sender;
34
testName = _testName;
35
}
36
37
/// @inheritdoc IAnteTest
38
function setStateAndCheckTestPasses(bytes memory _state) external override returns (bool) {
39
if (_state.length > 0) {
40
_setState(_state);
41
}
42
return checkTestPasses();
43
}
44
45
/// @notice Returns the testedContracts array of addresses
46
/// @return The list of tested contracts as an array of addresses
47
function getTestedContracts() external view returns (address[] memory) {
48
return testedContracts;
49
}
50
51
/// @inheritdoc IAnteTest
52
function setTestAuthor(address _testAuthor) external {
53
require(msg.sender == testAuthor, "Only the current testAuthor can set a new test author");
54
require(_testAuthor != address(0), "ANTE: Test author cannot be the zero address");
55
address previousAuthor = testAuthor;
56
testAuthor = _testAuthor;
57
58
emit TestAuthorChanged(previousAuthor, _testAuthor);
59
}
60
61
/// @inheritdoc IAnteTest
62
function getStateTypes() external pure virtual override returns (string memory) {
63
return "";
64
}
65
66
/// @inheritdoc IAnteTest
67
function getStateNames() external pure virtual override returns (string memory) {
68
return "";
69
}
70
71
/// @inheritdoc IAnteTest
72
function checkTestPasses() public virtual override returns (bool passes) {}
73
74
/// @notice Function containing the logic to set the AnteTest state
75
function _setState(bytes memory) internal virtual {}
76
}
AnteTest.sol as of 2023-01-28
The AnteTest.sol is an abstract contract that once inherited, can be used to write any Ante Test. Just pass in a descriptive name for the test, designated by _testName. The optional parameters protocolName and testedContracts can be set in the constructor of your Ante Test.

Deriving from AnteTest.sol

You can use it by declaring your Ante Test like the following:
import "@antefinance/contracts/interfaces/AnteTest.sol";
contract AnteNewProtocolTest is AnteTest("String descriptor of test") {
// create Ante Test variables & insert logic here
// you can omit your constructor if not defining these optional parameters
constructor () {
protocolName = "My Protocol";
testedContracts = [0x000000000000000000000000000000000000fEeD];
}
function checkTestPasses() public view override returns (bool) {
// insert logic here to check the My Protocol invariant
}
}

Stateful tests

When writing an Ante Test that needs internal state values, you can write:
1
import "@antefinance/contracts/interfaces/AnteTest.sol";
2
3
contract AnteNewProtocolTestWithState is AnteTest("String descriptor of test") {
4
// declare any variables needed to test the invariant here
5
string public myStringStateVariable;
6
uint public myUintStateVariable;
7
8
// you can omit your constructor if not defining these optional parameters
9
constructor () {
10
protocolName = "My Protocol";
11
testedContracts = [0x000000000000000000000000000000000000fEeD];
12
}
13
14
// decode the encoded bytes and set the state variables
15
function _setState(bytes memory _state) internal override {
16
(string _myStringStateVariable, uint _myUintStateVariable) = abi.decode(
17
_state,
18
(string, uint)
19
);
20
myStringStateVariable = _myStringStateVariable;
21
myUintStateVariable = _myUintStateVariable;
22
}
23
24
function checkTestPasses() public view override returns (bool) {
25
// insert logic here to check the My Protocol invariant
26
}
27
28
// tell verifiers which encoded types they need to pass in
29
function getStateTypes() external pure override returns (string memory) {
30
return "string,uint";
31
}
32
33
// tell verifiers what state variable names they need to set values for
34
function getStateNames() external pure override returns (string memory) {
35
return "myStringStateVariable,myUintStateVariable";
36
}
37
}
We'll see in the next section, how to use this abstract contract in some Ante Test examples.