-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
776 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,5 +13,5 @@ foundry/broadcast | |
.pnp.* | ||
lcov.info | ||
yarn.lock | ||
|
||
|
||
build | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
contract HigherOrder { | ||
address public commander; | ||
|
||
uint256 public treasury; | ||
|
||
function registerTreasury(uint8 treasury_slot) public { | ||
assembly { | ||
sstore(treasury_slot, calldataload(4)) | ||
} | ||
} | ||
|
||
function claimLeadership() public { | ||
if (treasury > 255) commander = msg.sender; | ||
else revert("Only members of the Higher Order can become Commander"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import { Level } from "./00_Ethernaut.sol"; | ||
|
||
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; | ||
} | ||
} | ||
|
||
contract StakeFactory is Level { | ||
address _dweth = address(0); | ||
|
||
function createInstance(address) public payable override returns (address) { | ||
return address(new Stake(address(_dweth))); | ||
} | ||
|
||
function validateInstance(address payable _instance, address _player) public view override returns (bool) { | ||
Stake instance = Stake(_instance); | ||
return _instance.balance != 0 && instance.totalStaked() > _instance.balance && instance.UserStake(_player) == 0 | ||
&& instance.Stakers(_player); | ||
} | ||
} | ||
|
||
contract StakeHack { | ||
function attack(Stake _target) public payable { | ||
_target.StakeETH{ value: msg.value }(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import { Ownable } from "@openzeppelin/contracts-v4.7.3/access/Ownable.sol"; | ||
import { Level } from "./00_Ethernaut.sol"; | ||
|
||
// SlockDotIt ECLocker factory | ||
contract Impersonator is Ownable { | ||
uint256 public lockCounter; | ||
ECLocker[] public lockers; | ||
|
||
event NewLock(address indexed lockAddress, uint256 lockId, uint256 timestamp, bytes signature); | ||
|
||
constructor(uint256 _lockCounter) { | ||
lockCounter = _lockCounter; | ||
} | ||
|
||
function deployNewLock(bytes memory signature) public onlyOwner { | ||
// Deploy a new lock | ||
ECLocker newLock = new ECLocker(++lockCounter, signature); | ||
lockers.push(newLock); | ||
emit NewLock(address(newLock), lockCounter, block.timestamp, signature); | ||
} | ||
} | ||
|
||
contract ECLocker { | ||
uint256 public immutable lockId; | ||
bytes32 public immutable msgHash; | ||
address public controller; | ||
mapping(bytes32 => bool) public usedSignatures; | ||
|
||
event LockInitializated(address indexed initialController, uint256 timestamp); | ||
event Open(address indexed opener, uint256 timestamp); | ||
event ControllerChanged(address indexed newController, uint256 timestamp); | ||
|
||
error InvalidController(); | ||
error SignatureAlreadyUsed(); | ||
|
||
/// @notice Initializes the contract the lock | ||
/// @param _lockId uinique lock id set by SlockDotIt's factory | ||
/// @param _signature the signature of the initial controller | ||
constructor(uint256 _lockId, bytes memory _signature) { | ||
// Set lockId | ||
lockId = _lockId; | ||
|
||
// Compute msgHash | ||
bytes32 _msgHash; | ||
assembly { | ||
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 28 bytes | ||
mstore(0x1C, _lockId) // 32 bytes | ||
_msgHash := keccak256(0x00, 0x3c) //28 + 32 = 60 bytes | ||
} | ||
msgHash = _msgHash; | ||
|
||
// Recover the initial controller from the signature | ||
address initialController = address(1); | ||
assembly { | ||
let ptr := mload(0x40) | ||
mstore(ptr, _msgHash) // 32 bytes | ||
mstore(add(ptr, 32), mload(add(_signature, 0x60))) // 32 byte v | ||
mstore(add(ptr, 64), mload(add(_signature, 0x20))) // 32 bytes r | ||
mstore(add(ptr, 96), mload(add(_signature, 0x40))) // 32 bytes s | ||
pop( | ||
staticcall( | ||
gas(), // Amount of gas left for the transaction. | ||
initialController, // Address of `ecrecover`. | ||
ptr, // Start of input. | ||
0x80, // Size of input. | ||
0x00, // Start of output. | ||
0x20 // Size of output. | ||
) | ||
) | ||
if iszero(returndatasize()) { | ||
mstore(0x00, 0x8baa579f) // `InvalidSignature()`. | ||
revert(0x1c, 0x04) | ||
} | ||
initialController := mload(0x00) | ||
mstore(0x40, add(ptr, 128)) | ||
} | ||
|
||
// Invalidate signature | ||
usedSignatures[keccak256(_signature)] = true; | ||
|
||
// Set the controller | ||
controller = initialController; | ||
|
||
// emit LockInitializated | ||
emit LockInitializated(initialController, block.timestamp); | ||
} | ||
|
||
/// @notice Opens the lock | ||
/// @dev Emits Open event | ||
/// @param v the recovery id | ||
/// @param r the r value of the signature | ||
/// @param s the s value of the signature | ||
function open(uint8 v, bytes32 r, bytes32 s) external { | ||
address add = _isValidSignature(v, r, s); | ||
emit Open(add, block.timestamp); | ||
} | ||
|
||
/// @notice Changes the controller of the lock | ||
/// @dev Updates the controller storage variable | ||
/// @dev Emits ControllerChanged event | ||
/// @param v the recovery id | ||
/// @param r the r value of the signature | ||
/// @param s the s value of the signature | ||
/// @param newController the new controller address | ||
function changeController(uint8 v, bytes32 r, bytes32 s, address newController) external { | ||
_isValidSignature(v, r, s); | ||
controller = newController; | ||
emit ControllerChanged(newController, block.timestamp); | ||
} | ||
|
||
function _isValidSignature(uint8 v, bytes32 r, bytes32 s) internal returns (address) { | ||
address _address = ecrecover(msgHash, v, r, s); | ||
require(_address == controller, InvalidController()); | ||
|
||
bytes32 signatureHash = keccak256(abi.encode([uint256(r), uint256(s), uint256(v)])); | ||
require(!usedSignatures[signatureHash], SignatureAlreadyUsed()); | ||
|
||
usedSignatures[signatureHash] = true; | ||
|
||
return _address; | ||
} | ||
} | ||
|
||
contract ImpersonatorFactory is Level { | ||
function createInstance(address _player) public payable override returns (address) { | ||
_player; | ||
Impersonator impersonator = new Impersonator(1336); | ||
bytes memory signature = abi.encode( | ||
[ | ||
uint256( | ||
11_397_568_185_806_560_130_291_530_949_248_708_355_673_262_872_727_946_990_834_312_389_557_386_886_033 | ||
), | ||
uint256( | ||
54_405_834_204_020_870_944_342_294_544_757_609_285_398_723_182_661_749_830_189_277_079_337_680_158_706 | ||
), | ||
uint256(27) | ||
] | ||
); | ||
impersonator.deployNewLock(signature); | ||
return address(impersonator); | ||
} | ||
|
||
function validateInstance(address payable _instance, address _player) public view override returns (bool) { | ||
_player; | ||
Impersonator instance = Impersonator(_instance); | ||
ECLocker locker = instance.lockers(0); | ||
return locker.controller() == address(0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import { Level } from "./00_Ethernaut.sol"; | ||
|
||
contract MagicAnimalCarousel { | ||
uint16 public constant MAX_CAPACITY = type(uint16).max; | ||
uint256 constant ANIMAL_MASK = uint256(type(uint80).max) << 160 + 16; | ||
uint256 constant NEXT_ID_MASK = uint256(type(uint16).max) << 160; | ||
uint256 constant OWNER_MASK = uint256(type(uint160).max); | ||
|
||
uint256 public currentCrateId; | ||
mapping(uint256 crateId => uint256 animalInside) public carousel; | ||
|
||
error AnimalNameTooLong(); | ||
|
||
constructor() { | ||
carousel[0] ^= 1 << 160; | ||
} | ||
|
||
function setAnimalAndSpin(string calldata animal) external { | ||
uint256 encodedAnimal = encodeAnimalName(animal) >> 16; | ||
uint256 nextCrateId = (carousel[currentCrateId] & NEXT_ID_MASK) >> 160; | ||
|
||
require(encodedAnimal <= uint256(type(uint80).max), AnimalNameTooLong()); | ||
carousel[nextCrateId] = (carousel[nextCrateId] & ~NEXT_ID_MASK) ^ (encodedAnimal << 160 + 16) | ||
| ((nextCrateId + 1) % MAX_CAPACITY) << 160 | uint160(msg.sender); | ||
|
||
currentCrateId = nextCrateId; | ||
} | ||
|
||
function changeAnimal(string calldata animal, uint256 crateId) external { | ||
address owner = address(uint160(carousel[crateId] & OWNER_MASK)); | ||
if (owner != address(0)) { | ||
require(msg.sender == owner); | ||
} | ||
uint256 encodedAnimal = encodeAnimalName(animal); | ||
if (encodedAnimal != 0) { | ||
// Replace animal | ||
carousel[crateId] = (encodedAnimal << 160) | (carousel[crateId] & NEXT_ID_MASK) | uint160(msg.sender); | ||
} else { | ||
// If no animal specified keep same animal but clear owner slot | ||
carousel[crateId] = (carousel[crateId] & (ANIMAL_MASK | NEXT_ID_MASK)); | ||
} | ||
} | ||
|
||
function encodeAnimalName(string calldata animalName) public pure returns (uint256) { | ||
require(bytes(animalName).length <= 12, AnimalNameTooLong()); | ||
return uint256(bytes32(abi.encodePacked(animalName)) >> 160); | ||
} | ||
} | ||
|
||
contract MagicAnimalCarouselFactory is Level { | ||
function createInstance(address _player) public payable override returns (address) { | ||
_player; | ||
MagicAnimalCarousel magicAnimalCarousel = new MagicAnimalCarousel(); | ||
return address(magicAnimalCarousel); | ||
} | ||
|
||
function validateInstance(address payable _instance, address _player) public override returns (bool) { | ||
_player; | ||
MagicAnimalCarousel instance = MagicAnimalCarousel(_instance); | ||
// Store a goat in the box | ||
string memory goat = "Goat"; | ||
instance.setAnimalAndSpin(goat); | ||
|
||
// Goat should be mutated | ||
uint256 currentCrateId = instance.currentCrateId(); | ||
uint256 animalInBox = instance.carousel(currentCrateId) >> 176; | ||
uint256 goatEnc = uint256(bytes32(abi.encodePacked(goat))) >> 176; | ||
return animalInBox != goatEnc; | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | ||
|
||
contract BatchTransfer { | ||
function BatchTransferERC20( | ||
ERC20 token, | ||
address[] calldata toAddressList, | ||
uint256[] calldata toAmountList | ||
) | ||
external | ||
{ | ||
require(toAddressList.length == toAmountList.length, "Params not match"); | ||
for (uint256 idx = 0; idx < toAddressList.length; idx++) { | ||
require( | ||
token.transferFrom(msg.sender, toAddressList[idx], toAmountList[idx]), "Transfer ERC20 Token Failed" | ||
); | ||
} | ||
} | ||
|
||
function BatchTransferERC721( | ||
ERC721 token, | ||
address[] calldata toAddressList, | ||
uint256[] calldata toTokenIDList | ||
) | ||
external | ||
{ | ||
require(toAddressList.length == toTokenIDList.length, "Params not match"); | ||
for (uint256 idx = 0; idx < toAddressList.length; idx++) { | ||
token.transferFrom(msg.sender, toAddressList[idx], toTokenIDList[idx]); | ||
} | ||
} | ||
} |
Oops, something went wrong.