Skip to content

Commit

Permalink
🎉 Reveal the dungeon
Browse files Browse the repository at this point in the history
  • Loading branch information
jaczkal committed Sep 19, 2023
0 parents commit 9563d8e
Show file tree
Hide file tree
Showing 11 changed files with 420 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

.woke-build
.woke-logs
.env
pytypes
__pycache__/
*.py[cod]
.hypothesis/
woke-coverage.cov
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Hackee v3

## Goal

## Recommendations

## Setup

259 changes: 259 additions & 0 deletions contracts/Dungeon.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
// SPDX-License-Identifier: None

pragma solidity =0.8.20;

/* NPCs */
import "./NPC.sol";
import "./Villager.sol";
import "./Mathematican.sol";
import "./FinalBoss.sol";

/* Objects */
import "./Spawner.sol";

/* Helpers */
import "woke/console.sol";

contract Dungeon {
Spawner public $spawner;
NPC public $storyTeller;
NPC public $mathematican;

mapping(bytes4 => bool) public $quests;
uint256 private $traps;
address public $challenger;

event Subtitles(string subtitles);

/*
Modifiers
*/

modifier takeQuest() {
require($quests[msg.sig] == false, "Quest already taken");
$quests[msg.sig] = true;
_;
}

modifier onlyHumans() {
uint256 size;
assembly { size := extcodesize(caller()) }
require(size <= 0, "You're trying to trick me with a contract!");
_;
}

modifier cleanRoom() {
require(
$quests[this.removeWeb1.selector] == true &&
$quests[this.removeWeb2.selector] == true &&
$quests[this.removeWeb3.selector] == true,
"These functions cannot be called"
);
_;
}

modifier altarFound() {
require(
$quests[this.dodgeTraps.selector] == true,
"These functions cannot be called"
);
_;
}

modifier lootGained() {
require(
$quests[this.lockedChest.selector] == true,
"These functions cannot be called"
);
_;
}

modifier challenger() {
require(msg.sender == $challenger, "You're not the challenger");
_;
}

constructor() {
// init dungeon
$spawner = new Spawner();
$storyTeller = NPC(_spawn(keccak256("storyTeller"), type(Villager).creationCode));
$mathematican = NPC(_spawn(keccak256("mathematican"), type(Mathematican).creationCode));
$traps = 3;
// mint potential rewards
_mint(address(this), 100_000);
}

/*
Collect tokens from dungeon
*/

// get 300 tokens
function removeWeb1(uint256 slashes) external takeQuest {
unchecked {
_attempt(slashes == 666, 300, msg.sender, "Devil's cut!", "Caught in web1");
}
}

// get 500 tokens
function removeWeb2(uint256 slashes) external takeQuest {
unchecked {
_attempt((((((slashes * 3) ^ 2) % 1000) + $traps) * 2) == 666, 500, msg.sender, "Devil's cut!", "Caught in web2");
}
}

// get 3000 tokens
function removeWeb3(uint256 slashes) external takeQuest {
unchecked {
_attempt(abi.decode($mathematican.interaction(abi.encode(slashes)), (uint256)) == 666, 3000, msg.sender, "Devil's cut!", "Caught in web3");
}
}

// get 750 tokens
function findMoreTraps() external takeQuest {
uint256 traps = uint256(block.timestamp/* + block.prevrandao */) % 6;
traps == 0 || traps == 1 ? traps += 2 : traps;
$traps = traps;
_attempt(traps == 5, 750, msg.sender, "Nicely done!", "You missed some traps.");
}

// get 1000 tokens
function etherForTokens() external takeQuest {
_attempt(address(this).balance == 1000, 1000, msg.sender, "Etheeer!", "You failed.");
_muhahaha();
}

// get 1500 tokens
function lockedChest(uint256 pin) external takeQuest {
require(pin != 1234, "You're cheatin'");
uint32 allowed_range_pin = uint32(pin);
_attempt(allowed_range_pin == 1234, 1500, msg.sender, "Gotcha.", "Incorrect pin.");
}

// get something
function gamble(function(address, uint256) external rollin, uint256 buyIn) external takeQuest {
rollin(address(this), (buyIn / 1000) * buyIn);
_attempt(buyIn <= 2000, buyIn, msg.sender, "Nice game.", "You're too greedy.");
}

// one wish in front of altar
function spawnNPC(bytes32 secretSubstance, bytes memory lifeform) altarFound takeQuest public {
$spawner.spawnNPC(secretSubstance, lifeform);
}

// get 2500 tokens
function assignChallenger() external cleanRoom onlyHumans takeQuest {
$challenger = msg.sender;
_attempt(1 == 1, 2500, msg.sender, "Buy something useful.", "Wut?");
}

// get 10000 tokens
function spawnFinalBoss() external altarFound cleanRoom lootGained challenger takeQuest {
NPC finalBoss = NPC(_spawn(keccak256("finalBoss"), type(FinalBoss).creationCode));
bytes memory result = finalBoss.interaction(abi.encodePacked(msg.sender));
_attempt(abi.decode(result, (bool)) == true, 10000, msg.sender, "What a legend!", "You have been defecated");
}

// get something
function dodgeTraps() external {
if ($traps == 0) {
$quests[msg.sig] = true;
emit Subtitles("You dodged all traps.");
return;
}
_attempt($traps >= 0, 250 * $traps, msg.sender, "Doge!", "You failed.");
_muhahaha();
$traps--;
}

/*
Helper part
*/

// TODO this will be deprecated
function getTraps() external view returns (uint256) {
return $traps;
}

function _attempt(bool condition, uint256 value, address to, string memory good, string memory bad) private {
if (condition) {
this.transfer(to, value);
emit Subtitles(good);
} else {
this._burn(address(this), value);
emit Subtitles(bad);
}
}

function _spawn(bytes32 secretSubstance, bytes memory lifeform) private returns (address payable) {
return payable($spawner.spawnNPC(secretSubstance, lifeform));
}

function _muhahaha() private {
msg.sender.call{value: 0}("");
}

/*
Evaluation part
*/

function evaluate(address hackeer) external view returns (uint256) {
return $balances[hackeer];
}

/*
ERC20 part
*/

uint256 public $totalSupply;
mapping(address => uint256) public $balances;
mapping(address => mapping(address => uint256)) public $allowed;

function balanceOf(address _owner) public view returns (uint256 balance) {
return $balances[_owner];
}

function transfer(address _to, uint256 _value) public returns (bool success) {
require($balances[msg.sender] >= _value);
$balances[msg.sender] -= _value;
$balances[_to] += _value;
return true;
}

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require($balances[_from] >= _value && $allowed[_from][msg.sender] >= _value);
$balances[_to] += _value;
$balances[_from] -= _value;
$allowed[_from][msg.sender] -= _value;
return true;
}

function approve(address _spender, uint256 _value) public returns (bool success) {
$allowed[msg.sender][_spender] = _value;
return true;
}

function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return $allowed[_owner][_spender];
}

function _mint(address _to, uint256 _amount) private {
$totalSupply += _amount;
$balances[_to] += _amount;
}

function _burn(address _from, uint256 _amount) external {
require(_from == address(this), "You don't have such power.");
require($balances[_from] >= _amount);
$totalSupply -= _amount;
$balances[_from] -= _amount;
}

receive() external payable {
revert("You can't bribe the dungeon!");
}

fallback() external {
revert("Fall back stranger.");
}

}
41 changes: 41 additions & 0 deletions contracts/Mathematican.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: None

pragma solidity =0.8.20;

import "./NPC.sol";

contract Mathematican is NPC {
function interaction(bytes memory input) external override returns (bytes memory output){
uint256 x = abi.decode(input, (uint256));
uint256 y;
assembly {
let arg := x
x := sub(x,1)
x := or(x, div(x, 0x02))
x := or(x, div(x, 0x04))
x := or(x, div(x, 0x10))
x := or(x, div(x, 0x100))
x := or(x, div(x, 0x10000))
x := or(x, div(x, 0x100000000))
x := or(x, div(x, 0x10000000000000000))
x := or(x, div(x, 0x100000000000000000000000000000000))
x := add(x, 1)
let m := mload(0x40)
mstore(m, 0xf8f9cbfae6cc78fbefe7cdc3a1793dfcf4f0e8bbd8cec470b6a28a7a5a3e1efd)
mstore(add(m,0x20), 0xf5ecf1b3e9debc68e1d9cfabc5997135bfb7a7a3938b7b606b5b4b3f2f1f0ffe)
mstore(add(m,0x40), 0xf6e4ed9ff2d6b458eadcdf97bd91692de2d4da8fd2d0ac50c6ae9a8272523616)
mstore(add(m,0x60), 0xc8c0b887b0a8a4489c948c7f847c6125746c645c544c444038302820181008ff)
mstore(add(m,0x80), 0xf7cae577eec2a03cf3bad76fb589591debb2dd67e0aa9834bea6925f6a4a2e0e)
mstore(add(m,0xa0), 0xe39ed557db96902cd38ed14fad815115c786af479b7e83247363534337271707)
mstore(add(m,0xc0), 0xc976c13bb96e881cb166a933a55e490d9d56952b8d4e801485467d2362422606)
mstore(add(m,0xe0), 0x753a6d1b65325d0c552a4d1345224105391a310b29122104190a110309020100)
mstore(0x40, add(m, 0x100))
let magic := 0x818283848586878898a8b8c8d8e8f929395969799a9b9d9e9faaeb6bedeeff
let shift := 0x100000000000000000000000000000000000000000000000000000000000000
let a := div(mul(x, magic), shift)
y := div(mload(add(m,sub(255,a))), shift)
y := add(y, mul(256, gt(arg, 0x8000000000000000000000000000000000000000000000000000000000000000)))
}
output = abi.encode(y);
}
}
24 changes: 24 additions & 0 deletions contracts/NPC.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: None

pragma solidity =0.8.20;

contract NPC {
function interaction(bytes memory input) external virtual returns (bytes memory output) {
return bytes(hex"42");
}

function kill(address backpack) external {
selfdestruct(payable(backpack));
}

function givelife(bytes memory lifeform) external payable {
assembly {
if iszero(create(0, add(lifeform, 32), mload(lifeform))) {
revert(0, 0)
}
selfdestruct(caller())
}
}

receive () external payable {}
}
30 changes: 30 additions & 0 deletions contracts/Spawner.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: None

pragma solidity =0.8.20;

import "./NPC.sol";

import 'woke/console.sol';

contract Spawner {
address private $dungeon;

constructor() {
$dungeon = msg.sender;
}

modifier onlyDungeon() {
require(msg.sender == $dungeon, "Only dungeon can spawn NPCs");
_;
}

function spawnNPC(bytes32 secretSubstance, bytes memory lifeform) public onlyDungeon returns (address) {
NPC npc = new NPC{ salt: secretSubstance }();
npc.givelife(lifeform);
return address(uint160(uint256(keccak256(abi.encodePacked(hex'd6_94', uint160(uint256(keccak256(abi.encodePacked(hex'ff', address(this), secretSubstance, keccak256(type(NPC).creationCode))))), hex'01')))));
}

function whereSpawner() external view returns (address) {
return $dungeon;
}
}
17 changes: 17 additions & 0 deletions contracts/Villager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: None

pragma solidity =0.8.20;

import "./NPC.sol";

contract Villager is NPC {
function interaction(bytes memory) external override returns (bytes memory output) {
return "Just go to the dungeon, stranger, you'll die or get tokens."
" Everything what you find in the dungeon can be considered as yours."
" Beware of traps and webs and try to defeat the dungeon boss."
" If you do so, whole Etherea will be grateful to you."
" I know about some (hopefully) useful maps of the dungeon for you."
" They are in a locked chest in the dungeon."
" Farewell.";
}
}
Binary file added contracts/lockedChest.dungeon
Binary file not shown.
Empty file added scripts/__init__.py
Empty file.
Loading

0 comments on commit 9563d8e

Please sign in to comment.