-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit e5d5cfa
Showing
12 changed files
with
14,770 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
node_modules | ||
.env | ||
coverage | ||
coverage.json | ||
typechain | ||
typechain-types | ||
|
||
# Hardhat files | ||
cache | ||
artifacts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
compiler: | ||
solc: | ||
remappings: | ||
- "@openzeppelin=OpenZeppelin/[email protected]" | ||
dependencies: | ||
- OpenZeppelin/[email protected] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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 | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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); | ||
} | ||
} |
Oops, something went wrong.