From b626847931be409f5aca29442e0a0c4acebee06d Mon Sep 17 00:00:00 2001 From: Chris Smith <1408372+iamchrissmith@users.noreply.github.com> Date: Sat, 24 Feb 2024 12:56:45 -0700 Subject: [PATCH] fill out remaining handler functions --- README.md | 24 ++++++ test/invariant/DaiInvariants.t.sol | 8 +- test/invariant/handlers/DaiHandler.sol | 109 +++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c0b891..2ed25c7 100644 --- a/README.md +++ b/README.md @@ -42,3 +42,27 @@ These contracts are essentially wrappers around the contracts/functionality we w #### Regressions If we find a failing sequence, these are the unit tests that we can use to reproduce the failure and check our fix. + +### 2 - Handler Setup + +branch: `2-handler-setup` + +Important Concepts: + +#### Handler's purpose + +Your handler is the wrapper around the contract that you are testing. You should add all functions you want to be included in your testing to this contract (most likely all external functions that modify state). The test suite will call these functions in random order with random input. + +Calls to the contract you're testing should be wrapped in `try/catch` blocks so you can handle errors. + +#### Actors + +To make the test suite better simulate what will happen post deployment, you need a set of actors and destination addresses that will be the `msg.sender` and targets for your function calls. + +#### Error handling + +You will need to decide whether you are going to `bound` your inputs so that calls are successful or if you are going to add error exclusions. + +#### Requires/Reverts + +You can add handler function level assertions to these functions if you want to assert certain things are true during a specific call. This makes the handler functions act like fuzz tests within your invariant test suite. diff --git a/test/invariant/DaiInvariants.t.sol b/test/invariant/DaiInvariants.t.sol index ddbac21..45f8756 100644 --- a/test/invariant/DaiInvariants.t.sol +++ b/test/invariant/DaiInvariants.t.sol @@ -34,8 +34,14 @@ contract DaiInvariants is Test { // setup Actors in Handler _daiHandler.init(); - bytes4[] memory selectors = new bytes4[](1); + bytes4[] memory selectors = new bytes4[](7); selectors[0] = _daiHandler.transfer.selector; + selectors[1] = _daiHandler.transferFrom.selector; + selectors[2] = _daiHandler.mint.selector; + selectors[3] = _daiHandler.burn.selector; + selectors[4] = _daiHandler.approve.selector; + selectors[5] = _daiHandler.rely.selector; + selectors[6] = _daiHandler.deny.selector; targetSelector( FuzzSelector({ diff --git a/test/invariant/handlers/DaiHandler.sol b/test/invariant/handlers/DaiHandler.sol index 6376893..0ac4d46 100644 --- a/test/invariant/handlers/DaiHandler.sol +++ b/test/invariant/handlers/DaiHandler.sol @@ -60,6 +60,12 @@ contract DaiHandler is Test { dai.mint(_actor.addr, 1000 ether); } + Actor memory governance; + (governance.addr, governance.key) = makeAddrAndKey(string(abi.encodePacked("Governance"))); + actors.push(governance); + dsts.push(governance); + dai.rely(governance.addr); + Actor memory zero; (zero.addr, zero.key) = makeAddrAndKey(string(abi.encodePacked("Zero"))); zero.addr = address(0); @@ -84,6 +90,109 @@ contract DaiHandler is Test { } } + function transferFrom( + uint256 _actorIndex, + uint256 _srcIndex, + uint256 _dstIndex, + uint256 _wad + ) public useRandomActor(_actorIndex) resetErrors { + Actor memory src = _selectActor(_srcIndex); + Actor memory dst = _selectDst(_dstIndex); + try dai.transferFrom(src.addr, dst.addr, _wad) { + console.log("TransferFrom succeeded"); + } catch Error(string memory reason) { + if(dai.balanceOf(src.addr) < _wad) addExpectedError("Dai/insufficient-balance"); + if(dai.allowance(actor.addr, src.addr) < _wad) addExpectedError("Dai/insufficient-allowance"); + expectedError(reason); + } catch (bytes memory reason) { + console.log("TransferFrom failed: "); + console.logBytes(reason); + } + } + + function mint( + uint256 _actorIndex, + uint256 _dstIndex, + uint256 _wad + ) public useRandomActor(_actorIndex) resetErrors { + Actor memory dst = _selectDst(_dstIndex); + try dai.mint(dst.addr, _wad) { + console.log("Mint succeeded"); + } catch Error(string memory reason) { + if(dai.wards(actor.addr) == 0) addExpectedError("Dai/not-authorized"); + expectedError(reason); + } catch (bytes memory reason) { + console.log("Mint failed: "); + console.logBytes(reason); + } + } + + function burn( + uint256 _actorIndex, + uint256 _usrIndex, + uint256 _wad + ) public useRandomActor(_actorIndex) resetErrors { + Actor memory usr = _selectActor(_usrIndex); + try dai.burn(usr.addr, _wad) { + console.log("burn succeeded"); + } catch Error(string memory reason) { + if(dai.balanceOf(usr.addr) < _wad) addExpectedError("Dai/insufficient-balance"); + if(dai.allowance(usr.addr, actor.addr) < _wad) addExpectedError("Dai/insufficient-allowance"); + expectedError(reason); + } catch (bytes memory reason) { + console.log("burn failed: "); + console.logBytes(reason); + } + } + + function approve( + uint256 _actorIndex, + uint256 _usrIndex, + uint256 _wad + ) public useRandomActor(_actorIndex) resetErrors { + Actor memory usr = _selectActor(_usrIndex); + try dai.approve(usr.addr, _wad) { + console.log("approve succeeded"); + } catch Error(string memory reason) { + expectedError(reason); + } catch (bytes memory reason) { + console.log("approve failed: "); + console.logBytes(reason); + } + } + + function rely( + uint256 _actorIndex, + uint256 _guyIndex + ) public useRandomActor(_actorIndex) resetErrors { + Actor memory guy = _selectActor(_guyIndex); + try dai.rely(guy.addr) { + console.log("rely succeeded"); + } catch Error(string memory reason) { + if(dai.wards(actor.addr) == 0) addExpectedError("Dai/not-authorized"); + expectedError(reason); + } catch (bytes memory reason) { + console.log("rely failed: "); + console.logBytes(reason); + } + } + + function deny( + uint256 _actorIndex, + uint256 _guyIndex + ) public useRandomActor(_actorIndex) resetErrors { + Actor memory guy = _selectActor(_guyIndex); + try dai.deny(guy.addr) { + console.log("deny succeeded"); + } catch Error(string memory reason) { + if(dai.wards(actor.addr) == 0) addExpectedError("Dai/not-authorized"); + expectedError(reason); + } catch (bytes memory reason) { + console.log("deny failed: "); + console.logBytes(reason); + } + } + // Internal Helper Functions function _selectActor(uint256 _actorIndex) internal view returns (Actor memory actor_) { uint256 index = bound(_actorIndex, 0, actors.length - 1);