Skip to content

Commit

Permalink
add files form attachment
Browse files Browse the repository at this point in the history
  • Loading branch information
iczc committed Jan 8, 2023
0 parents commit e5d5cfa
Show file tree
Hide file tree
Showing 12 changed files with 14,770 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules
.env
coverage
coverage.json
typechain
typechain-types

# Hardhat files
cache
artifacts
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# RealWorld CTF 5th - realwrap

## Description

WETH on Ethereum is too cumbersome! I'll show you what is real Wrapped ETH by utilizing precompiled contract, it works like a charm especially when exchanging ETH in a swap pair. And most important, IT IS VERY SECURE!

nc ip 20000

faucet: http://ip:8080

RPC(geth v1.10.26 with realwrap patch): http://ip:8545

[attachment](https://github.com/iczc/rwctf-5th-realwrap/releases)
6 changes: 6 additions & 0 deletions brownie-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
compiler:
solc:
remappings:
- "@openzeppelin=OpenZeppelin/[email protected]"
dependencies:
- OpenZeppelin/[email protected]
61 changes: 61 additions & 0 deletions contracts/Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./UniswapV2Pair.sol";

contract SimpleToken is ERC20 {
constructor(uint256 _initialSupply) ERC20("SimpleToken", "SPT") {
_mint(msg.sender, _initialSupply);
}
}

interface IUniswapV2Pair {
function getReserves()
external
view
returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);

function mint(address to) external returns (uint liquidity);

function initialize(address, address) external;
}

contract Factory {
address public constant WETH = 0x0000000000000000000000000000000000004eA1;
address public uniswapV2Pair;

event PairCreated(
address indexed token0,
address indexed token1,
address pair
);

constructor() payable {
require(msg.value == 1 ether);
address token = address(new SimpleToken(10 ** 8 * 1 ether));
uniswapV2Pair = createPair(WETH, token);
IERC20(WETH).transfer(uniswapV2Pair, 1 ether);
IERC20(token).transfer(uniswapV2Pair, 100 ether);
IUniswapV2Pair(uniswapV2Pair).mint(msg.sender);
}

function createPair(
address tokenA,
address tokenB
) public returns (address pair) {
(address token0, address token1) = tokenA < tokenB
? (tokenA, tokenB)
: (tokenB, tokenA);
bytes32 salt = keccak256(abi.encodePacked(token0, token1));
pair = address(new UniswapV2Pair{salt: salt}());
IUniswapV2Pair(pair).initialize(token0, token1);
emit PairCreated(token0, token1, pair);
}

function isSolved() public view returns (bool) {
(uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(uniswapV2Pair)
.getReserves();
return reserve0 == 0 && reserve1 == 0;
}
}
257 changes: 257 additions & 0 deletions contracts/UniswapV2Pair.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "./libraries/UQ112x112.sol";

interface IUniswapV2Callee {
function uniswapV2Call(
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
}

contract UniswapV2ERC20 is ERC20 {
constructor() ERC20("Uniswap V2", "UNI-V2") {}
}

contract UniswapV2Pair is UniswapV2ERC20 {
using SafeMath for uint256;
using UQ112x112 for uint224;
using SafeERC20 for IERC20;
uint256 public constant MINIMUM_LIQUIDITY = 10 ** 3;

address public factory;
address public token0;
address public token1;

uint112 private reserve0; // uses single storage slot, accessible via getReserves
uint112 private reserve1; // uses single storage slot, accessible via getReserves
uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves

uint256 public price0CumulativeLast;
uint256 public price1CumulativeLast;

uint256 private unlocked = 1;
modifier lock() {
require(unlocked == 1, "UniswapV2: LOCKED");
unlocked = 0;
_;
unlocked = 1;
}

function getReserves()
public
view
returns (
uint112 _reserve0,
uint112 _reserve1,
uint32 _blockTimestampLast
)
{
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}

event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(
address indexed sender,
uint256 amount0,
uint256 amount1,
address indexed to
);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);

constructor() {
factory = msg.sender;
}

// called once by the factory at time of deployment
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, "UniswapV2: FORBIDDEN"); // sufficient check
token0 = _token0;
token1 = _token1;
}

// update reserves and, on the first call per block, price accumulators
function _update(
uint256 balance0,
uint256 balance1,
uint112 _reserve0,
uint112 _reserve1
) private {
require(
balance0 <= type(uint112).max && balance1 <= type(uint112).max,
"UniswapV2: OVERFLOW"
);
uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);
unchecked {
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// * never overflows, and + overflow is desired
price0CumulativeLast +=
uint256(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) *
timeElapsed;
price1CumulativeLast +=
uint256(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) *
timeElapsed;
}
}
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}

// this low-level function should be called from a contract which performs important safety checks
function mint(address to) external lock returns (uint256 liquidity) {
(uint112 _reserve0, uint112 _reserve1, ) = getReserves();
uint256 balance0 = IERC20(token0).balanceOf(address(this));
uint256 balance1 = IERC20(token1).balanceOf(address(this));
uint256 amount0 = balance0.sub(_reserve0);
uint256 amount1 = balance1.sub(_reserve1);

uint256 _totalSupply = totalSupply();
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
_mint(address(0xdEaD), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = Math.min(
amount0.mul(_totalSupply) / _reserve0,
amount1.mul(_totalSupply) / _reserve1
);
}
require(liquidity > 0, "UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED");
_mint(to, liquidity);

_update(balance0, balance1, _reserve0, _reserve1);
emit Mint(msg.sender, amount0, amount1);
}

// this low-level function should be called from a contract which performs important safety checks
function burn(
address to
) external lock returns (uint256 amount0, uint256 amount1) {
(uint112 _reserve0, uint112 _reserve1, ) = getReserves();
address _token0 = token0;
address _token1 = token1;
uint256 balance0 = IERC20(_token0).balanceOf(address(this));
uint256 balance1 = IERC20(_token1).balanceOf(address(this));
uint256 liquidity = balanceOf(address(this));

uint256 _totalSupply = totalSupply();
amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
require(
amount0 > 0 && amount1 > 0,
"UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED"
);
_burn(address(this), liquidity);
IERC20(token0).safeTransfer(to, amount0);
IERC20(token1).safeTransfer(to, amount1);

balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));

_update(balance0, balance1, _reserve0, _reserve1);
emit Burn(msg.sender, amount0, amount1, to);
}

// this low-level function should be called from a contract which performs important safety checks
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external lock {
require(
amount0Out > 0 || amount1Out > 0,
"UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT"
);
(uint112 _reserve0, uint112 _reserve1, ) = getReserves();
require(
amount0Out < _reserve0 && amount1Out < _reserve1,
"UniswapV2: INSUFFICIENT_LIQUIDITY"
);

uint256 balance0;
uint256 balance1;
{
// scope for _token{0,1}, avoids stack too deep errors
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, "UniswapV2: INVALID_TO");
if (amount0Out > 0) IERC20(_token0).safeTransfer(to, amount0Out);
if (amount1Out > 0) IERC20(_token1).safeTransfer(to, amount1Out);
if (data.length > 0)
IUniswapV2Callee(to).uniswapV2Call(
msg.sender,
amount0Out,
amount1Out,
data
);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint256 amount0In = balance0 > _reserve0 - amount0Out
? balance0 - (_reserve0 - amount0Out)
: 0;
uint256 amount1In = balance1 > _reserve1 - amount1Out
? balance1 - (_reserve1 - amount1Out)
: 0;
require(
amount0In > 0 || amount1In > 0,
"UniswapV2: INSUFFICIENT_INPUT_AMOUNT"
);
{
// scope for reserve{0,1}Adjusted, avoids stack too deep errors
uint256 balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint256 balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(
balance0Adjusted.mul(balance1Adjusted) >=
uint256(_reserve0).mul(_reserve1).mul(1000 ** 2),
"UniswapV2: K"
);
}

_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}

// force balances to match reserves
function skim(address to) external lock {
address _token0 = token0;
address _token1 = token1;
IERC20(_token0).safeTransfer(
to,
IERC20(_token0).balanceOf(address(this)) - reserve0
);
IERC20(_token1).safeTransfer(
to,
IERC20(_token1).balanceOf(address(this)) - reserve1
);
}

// force reserves to match balances
function sync() external lock {
_update(
IERC20(token0).balanceOf(address(this)),
IERC20(token1).balanceOf(address(this)),
reserve0,
reserve1
);
}
}
20 changes: 20 additions & 0 deletions contracts/libraries/UQ112x112.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma solidity ^0.8.17;

// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))

// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
// https://github.com/Uniswap/v2-core/blob/master/contracts/libraries/UQ112x112.sol
library UQ112x112 {
uint224 constant Q112 = 2 ** 112;

// encode a uint112 as a UQ112x112
function encode(uint112 y) internal pure returns (uint224 z) {
z = uint224(y) * Q112;
}

// divide a UQ112x112 by a uint112, returning a UQ112x112
function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
z = x / uint224(y);
}
}
Loading

0 comments on commit e5d5cfa

Please sign in to comment.