my question is simple. I'm Auditing the Ethernaut22 Contract: here for code https://ethernaut.openzeppelin.com/level/0x9CB391dbcD447E645D6Cb55dE6ca23164130D008
Ethernaut22.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract Ethernaut22 is Ownable{
address public token1;
address public token2;
constructor() {}
function setTokens(address _token1, address _token2) public onlyOwner {
token1 = _token1;
token2 = _token2;
}
function addLiquidity(address token_address, uint amount) public onlyOwner {
IERC20(token_address).transferFrom(msg.sender, address(this), amount);
}
function swap(address from, address to, uint amount) public {
require((from == token1 && to == token2) || (from == token2 && to == token1), "Invalid tokens");
require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap");
uint swapAmount = getSwapPrice(from, to, amount);
IERC20(from).transferFrom(msg.sender, address(this), amount);
IERC20(to).approve(address(this), swapAmount);
IERC20(to).transferFrom(address(this), msg.sender, swapAmount);
}
function getSwapPrice(address from, address to, uint amount) public view returns(uint){
return((amount * IERC20(to).balanceOf(address(this)))/IERC20(from).balanceOf(address(this)));
}
function approve(address spender, uint amount) public {
Ethernaut22Token(token1).approve(msg.sender, spender, amount);
Ethernaut22Token(token2).approve(msg.sender, spender, amount);
}
function balanceOf(address token, address account) public view returns (uint){
return IERC20(token).balanceOf(account);
}
}
contract Ethernaut22Token is ERC20 {
address private _dex;
constructor(
address dexInstance,
string memory name,
string memory symbol,
uint256 initialSupply
)ERC20(name, symbol){
_mint(msg.sender, initialSupply);
_dex = dexInstance;
}
function approve(address owner, address spender, uint256 amount) public {
require(owner != _dex, "InvalidApprover");
super._approve(owner, spender, amount);
}
}
There are many errors in this code, but i would like to understand something from my echidna fuzzing
This is the code:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "../Ethernaut22.sol";
import "@crytic/properties/contracts/util/Hevm.sol";
//Fuzzing Ethernaut22 Contract
//
contract fuzzerEthernaut22 is Ethernaut22{
address constant deployer = address(0x20000);
address constant dist = address(0x30000);
address token1Address;
address token2Address;
constructor(){
token1Address = address(new Ethernaut22Token(address(this), "Token1", "TKN1", 110));
token2Address = address(new Ethernaut22Token(address(this), "Token2", "TKN2", 110));
Ethernaut22Token(token1Address).transfer(deployer, 110);
Ethernaut22Token(token2Address).transfer(deployer, 110);
}
function checkOwner()public{
address actualOwner = owner();
assert(owner() == deployer);
}
function checkBalance()public{
uint amount1 = Ethernaut22Token(token1Address).balanceOf(deployer);
assert(amount1 == 110);
}
function check2Balance()public{
uint amount2 = Ethernaut22Token(token2Address).balanceOf(deployer);
assert(amount2 == 110);
}
----ERROR HERE-----
//False-Negative?
function check_access_from_another_address_on_set_tokens_function()public{
hevm.prank(dist);
setTokens(token1Address, token2Address);
assert(token1 == address(0));
assert(token2 == address(0));
}
}
and this is also my config.yaml file
testMode: assertion
testLimit: 50000
deployer: "0x20000"
sender: ["0x20000" ,"0x30000"]
psender: "0x20000"
cryticArgs: ['--solc-remaps', '@=node_modules/@']
filterFunctions: ["fuzzerEthernaut22.transferOwnership(address)", "fuzzerEthernaut22.renounceOwnership()"]
filterBlacklist: true
If i run Echidna via my config.yaml i got an error in function check_access_from_another_address_on_set_tokens_function()public{}
Scope of the property is to check the possibility to set tokens if sender it's not the owner contract.
If token 1 and token 2 are equals to 0, this means, dist, it's not been able to set tokens because is not the owner.
The error i got is:
approve(address,uint256): passed!
token2(): passed!
addLiquidity(address,uint256): passed!
check_access_from_another_address_on_set_tokens_function(): failed!
Call sequence:
check_access_from_another_address_on_set_tokens_function()
Event sequence: Panic(1): Using assert.
owner(): passed!
check2Balance(): passed!
getSwapPrice(address,address,uint256): passed!
checkBalance(): passed!
setTokens(address,address): passed!
checkOwner(): passed!
token1(): passed!
swap(address,address,uint256): passed!
balanceOf(address,address): passed!
AssertionFailed(..): passed!
Unique instructions: 3129
Unique codehashes: 2
Corpus size: 18
Seed: 1397477069376912808
It's looks like, i can't recognize my hevm.prank(dist) to call function from another address, so the sender is still the deployer.
At the same time, following the echidna documentation, should be typical case of false negative coming from the use of Hevm.
Can someone explain me why i got this? Thanks in advice, have a nice day
Maybe i just found a workaround to my issue.
my config.yaml
I just avoid using of hevm because it's like i can't recognized that. In config.yaml i just setted sender as address 0x10000 and deployer as 0x20000
So using Ownable.sol and setted owner() as deployer(0x20000) if i try to call function setTokens(address, address)public onlyOwner{...} via different address i just receive a revert.
this is the result calling echidna via config.yaml and sender setted on 0x10000
this is the result calling echidna via config.yaml and sender setted on 0x20000
Hoping this workaround should be usefull to someone.
Please let me know if there is something wrong(specially for Hevm using)