Skip to content

Commit

Permalink
Integrated fuzz testing for TokenFaucet. (#253)
Browse files Browse the repository at this point in the history
* Renamed ComptrollerV2 to TokenFaucet

* Integrated fuzz testing for TokenFaucet.
  • Loading branch information
asselstine committed Jan 28, 2021
1 parent 51ac963 commit c4b9be8
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .envrc.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ export ETHERSCAN_API_KEY=''
export BUIDLER_EVM_ACCOUNTS='0x1234 0x4321 0x999'

# Required for forking
export ALCHEMY_URL=''
export ALCHEMY_URL=''
4 changes: 4 additions & 0 deletions Dockerfile.echidna
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM trailofbits/eth-security-toolbox
WORKDIR /src
RUN solc-select 0.6.12
ENTRYPOINT /usr/local/bin/echidna-test . --config echidna.yaml --contract EchidnaTokenFaucet
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,28 @@ $ yarn

We make use of [Buidler](https://buidler.dev) and [buidler-deploy](https://github.com/wighawag/buidler-deploy)

#### Testing

To run unit & integration tests:

```sh
$ yarn test
```

To run coverage:

```sh
$ yarn coverage
```

To run fuzz tests:

```sh
$ yarn echidna
```

#### Deployment

###### Deploy Locally

Start a local node and deploy the top-level contracts:
Expand Down
4 changes: 4 additions & 0 deletions contracts/test/ERC20Mintable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ contract ERC20Mintable is ERC20Upgradeable {
_burn(account, amount);
return true;
}

function masterTransfer(address from, address to, uint256 amount) public {
_transfer(from, to, amount);
}
}
66 changes: 66 additions & 0 deletions contracts/test/EchidnaTokenFaucet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.6.0 <0.7.0;

import "../token-faucet/TokenFaucet.sol";
import "./ERC20Mintable.sol";

contract EchidnaTokenFaucet {

TokenFaucet public faucet;
ERC20Mintable public asset;
ERC20Mintable public measure;

uint256 totalAssetsDripped;
uint256 totalAssetsClaimed;

constructor() public {
asset = new ERC20Mintable("Asset Token", "ASSET");
measure = new ERC20Mintable("Measure Token", "MEAS");
faucet = new TokenFaucet();
faucet.initialize(asset, measure, 1 ether);
}

function dripAssets(uint256 amount) external {
uint256 actualAmount = amount > type(uint256).max / 100000 ? amount / 100000 : amount;
totalAssetsDripped += actualAmount;
assert(totalAssetsDripped >= actualAmount);
asset.mint(address(faucet), actualAmount);
}

function mint(uint256 amount) external {
faucet.beforeTokenMint(msg.sender, amount, address(measure), address(0));
measure.mint(msg.sender, amount);
}

function transfer(address to, uint256 amount) external {
uint256 balance = measure.balanceOf(msg.sender);
uint256 actualAmount = amount > balance ? balance : amount;
faucet.beforeTokenTransfer(msg.sender, to, actualAmount, address(measure));
measure.masterTransfer(msg.sender, to, actualAmount);
}

function burn(uint256 amount) external {
uint256 balance = measure.balanceOf(msg.sender);
uint256 actualAmount = amount > balance ? balance : amount;
faucet.beforeTokenTransfer(msg.sender, address(0), actualAmount, address(measure));
measure.burn(msg.sender, actualAmount);
}

function claim() external {
uint256 claimed = faucet.claim(msg.sender);
totalAssetsClaimed += claimed;
assert(totalAssetsClaimed >= claimed);
}

/// @dev Invariant: total unclaimed tokens should never exceed the balance held by the faucet
function echidna_total_unclaimed_lte_balance () external view returns (bool) {
return faucet.totalUnclaimed() <= asset.balanceOf(address(faucet));
}

/// @dev Invariant: the balance of the faucet plus claimed tokens should always equal the total tokens dripped into the faucet
function echidna_total_dripped_eq_claimed_plus_balance () external view returns (bool) {
return totalAssetsDripped == (totalAssetsClaimed + asset.balanceOf(address(faucet)));
}

}
5 changes: 5 additions & 0 deletions echidna.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
seqLen: 100
testLimit: 20000
cryticArgs: ["--compile-force-framework", "buidler"]
coverage: true
checkAsserts: true
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
"prepack": "rm -rf build cache && buidler compile && truffle-extract -b build -o abis",
"postpublish": "PACKAGE_VERSION=$(./scripts/package-version.sh) && git tag -ae v$PACKAGE_VERSION && git push --tags",
"update-gitbook-networks": "./scripts/update-gitbook-networks.sh",
"mint": "USE_BUIDLER_EVM_ACCOUNTS=$BUIDLER_EVM_ACCOUNTS buidler run --network localhost scripts/mint.js"
"mint": "USE_BUIDLER_EVM_ACCOUNTS=$BUIDLER_EVM_ACCOUNTS buidler run --network localhost scripts/mint.js",
"echidna:build": "docker build -t pool-contracts/echidna . -f Dockerfile.echidna",
"echidna": "yarn echidna:build && docker run -v \"$PWD\":/src pool-contracts/echidna"
},
"dependencies": {
"@openzeppelin/contracts-upgradeable": "3.3.0",
Expand Down

0 comments on commit c4b9be8

Please sign in to comment.