Skip to content

Commit 7772f18

Browse files
author
Thai Xuan Dang
committed
test: core specification Hardhat
1 parent e3a5b85 commit 7772f18

32 files changed

+9490
-58
lines changed

src/core/test/CompileTrigger.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity =0.7.6;
3+
4+
import "@openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity =0.7.6;
3+
4+
import "../interfaces/IERC20Minimal.sol";
5+
6+
import "../interfaces/callback/IKatanaV3SwapCallback.sol";
7+
import "../interfaces/IKatanaV3Pool.sol";
8+
9+
contract KatanaV3PoolSwapTest is IKatanaV3SwapCallback {
10+
int256 private _amount0Delta;
11+
int256 private _amount1Delta;
12+
13+
function getSwapResult(address pool, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96)
14+
external
15+
returns (int256 amount0Delta, int256 amount1Delta, uint160 nextSqrtRatio)
16+
{
17+
(amount0Delta, amount1Delta) =
18+
IKatanaV3Pool(pool).swap(address(0), zeroForOne, amountSpecified, sqrtPriceLimitX96, abi.encode(msg.sender));
19+
20+
(nextSqrtRatio,,,,,,) = IKatanaV3Pool(pool).slot0();
21+
}
22+
23+
function katanaV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external override {
24+
address sender = abi.decode(data, (address));
25+
26+
if (amount0Delta > 0) {
27+
IERC20Minimal(IKatanaV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta));
28+
} else if (amount1Delta > 0) {
29+
IERC20Minimal(IKatanaV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta));
30+
}
31+
}
32+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity =0.7.6;
3+
4+
import "../KatanaV3Pool.sol";
5+
6+
// used for testing time dependent behavior
7+
contract MockTimeKatanaV3Pool is KatanaV3Pool {
8+
constructor(address factory, address governance) KatanaV3Pool(factory, governance) { }
9+
10+
// Monday, October 5, 2020 9:00:00 AM GMT-05:00
11+
uint256 public time = 1601906400;
12+
13+
/// @inheritdoc IKatanaV3PoolImmutablesInitializable
14+
function initializeImmutables(address factory, address token0, address token1, uint24 fee, int24 tickSpacing)
15+
public
16+
override
17+
{
18+
super.initializeImmutables(factory, token0, token1, fee, tickSpacing);
19+
time = 1601906400;
20+
}
21+
22+
function setFeeGrowthGlobal0X128(uint256 _feeGrowthGlobal0X128) external {
23+
feeGrowthGlobal0X128 = _feeGrowthGlobal0X128;
24+
}
25+
26+
function setFeeGrowthGlobal1X128(uint256 _feeGrowthGlobal1X128) external {
27+
feeGrowthGlobal1X128 = _feeGrowthGlobal1X128;
28+
}
29+
30+
function advanceTime(uint256 by) external {
31+
time += by;
32+
}
33+
34+
function _blockTimestamp() internal view override returns (uint32) {
35+
return uint32(time);
36+
}
37+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity =0.7.6;
3+
4+
import "@openzeppelin/contracts/utils/Create2.sol";
5+
6+
import "../interfaces/IKatanaV3PoolDeployer.sol";
7+
import "../interfaces/IKatanaV3PoolBeaconImmutables.sol";
8+
9+
import "../KatanaV3PoolBeacon.sol";
10+
import "./MockTimeKatanaV3Pool.sol";
11+
12+
contract MockTimeKatanaV3PoolDeployer is IKatanaV3PoolDeployer {
13+
struct Parameters {
14+
address factory;
15+
address token0;
16+
address token1;
17+
uint24 fee;
18+
int24 tickSpacing;
19+
}
20+
21+
address public immutable override BEACON;
22+
23+
Parameters public override parameters;
24+
25+
event PoolDeployed(address pool);
26+
27+
constructor(address factory, address governance) {
28+
address poolImplemenation = address(new MockTimeKatanaV3Pool(factory, governance));
29+
BEACON = address(new KatanaV3PoolBeacon(poolImplemenation));
30+
}
31+
32+
function deploy(address factory, address token0, address token1, uint24 fee, int24 tickSpacing)
33+
external
34+
returns (address pool)
35+
{
36+
parameters = Parameters({ factory: factory, token0: token0, token1: token1, fee: fee, tickSpacing: tickSpacing });
37+
bytes memory creationCode = IKatanaV3PoolBeaconImmutables(BEACON).POOL_PROXY_INIT_CODE();
38+
bytes32 salt = keccak256(abi.encode(token0, token1, fee));
39+
pool = Create2.deploy(0, salt, creationCode);
40+
emit PoolDeployed(pool);
41+
delete parameters;
42+
}
43+
}

src/core/test/TestERC20.sol

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity =0.7.6;
3+
4+
import "../interfaces/IERC20Minimal.sol";
5+
6+
contract TestERC20 is IERC20Minimal {
7+
mapping(address => uint256) public override balanceOf;
8+
mapping(address => mapping(address => uint256)) public override allowance;
9+
10+
constructor(uint256 amountToMint) {
11+
mint(msg.sender, amountToMint);
12+
}
13+
14+
function mint(address to, uint256 amount) public {
15+
uint256 balanceNext = balanceOf[to] + amount;
16+
require(balanceNext >= amount, "overflow balance");
17+
balanceOf[to] = balanceNext;
18+
}
19+
20+
function transfer(address recipient, uint256 amount) external override returns (bool) {
21+
uint256 balanceBefore = balanceOf[msg.sender];
22+
require(balanceBefore >= amount, "insufficient balance");
23+
balanceOf[msg.sender] = balanceBefore - amount;
24+
25+
uint256 balanceRecipient = balanceOf[recipient];
26+
require(balanceRecipient + amount >= balanceRecipient, "recipient balance overflow");
27+
balanceOf[recipient] = balanceRecipient + amount;
28+
29+
emit Transfer(msg.sender, recipient, amount);
30+
return true;
31+
}
32+
33+
function approve(address spender, uint256 amount) external override returns (bool) {
34+
allowance[msg.sender][spender] = amount;
35+
emit Approval(msg.sender, spender, amount);
36+
return true;
37+
}
38+
39+
function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
40+
uint256 allowanceBefore = allowance[sender][msg.sender];
41+
require(allowanceBefore >= amount, "allowance insufficient");
42+
43+
allowance[sender][msg.sender] = allowanceBefore - amount;
44+
45+
uint256 balanceRecipient = balanceOf[recipient];
46+
require(balanceRecipient + amount >= balanceRecipient, "overflow balance recipient");
47+
balanceOf[recipient] = balanceRecipient + amount;
48+
uint256 balanceSender = balanceOf[sender];
49+
require(balanceSender >= amount, "underflow balance sender");
50+
balanceOf[sender] = balanceSender - amount;
51+
52+
emit Transfer(sender, recipient, amount);
53+
return true;
54+
}
55+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity =0.7.6;
3+
4+
import "../interfaces/IERC20Minimal.sol";
5+
6+
import "../libraries/SafeCast.sol";
7+
import "../libraries/TickMath.sol";
8+
9+
import "../interfaces/callback/IKatanaV3MintCallback.sol";
10+
import "../interfaces/callback/IKatanaV3SwapCallback.sol";
11+
import "../interfaces/callback/IKatanaV3FlashCallback.sol";
12+
13+
import "../interfaces/IKatanaV3Pool.sol";
14+
15+
contract TestKatanaV3Callee is IKatanaV3MintCallback, IKatanaV3SwapCallback, IKatanaV3FlashCallback {
16+
using SafeCast for uint256;
17+
18+
function swapExact0For1(address pool, uint256 amount0In, address recipient, uint160 sqrtPriceLimitX96) external {
19+
IKatanaV3Pool(pool).swap(recipient, true, amount0In.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender));
20+
}
21+
22+
function swap0ForExact1(address pool, uint256 amount1Out, address recipient, uint160 sqrtPriceLimitX96) external {
23+
IKatanaV3Pool(pool).swap(recipient, true, -amount1Out.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender));
24+
}
25+
26+
function swapExact1For0(address pool, uint256 amount1In, address recipient, uint160 sqrtPriceLimitX96) external {
27+
IKatanaV3Pool(pool).swap(recipient, false, amount1In.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender));
28+
}
29+
30+
function swap1ForExact0(address pool, uint256 amount0Out, address recipient, uint160 sqrtPriceLimitX96) external {
31+
IKatanaV3Pool(pool).swap(recipient, false, -amount0Out.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender));
32+
}
33+
34+
function swapToLowerSqrtPrice(address pool, uint160 sqrtPriceX96, address recipient) external {
35+
IKatanaV3Pool(pool).swap(recipient, true, type(int256).max, sqrtPriceX96, abi.encode(msg.sender));
36+
}
37+
38+
function swapToHigherSqrtPrice(address pool, uint160 sqrtPriceX96, address recipient) external {
39+
IKatanaV3Pool(pool).swap(recipient, false, type(int256).max, sqrtPriceX96, abi.encode(msg.sender));
40+
}
41+
42+
event SwapCallback(int256 amount0Delta, int256 amount1Delta);
43+
44+
function katanaV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external override {
45+
address sender = abi.decode(data, (address));
46+
47+
emit SwapCallback(amount0Delta, amount1Delta);
48+
49+
if (amount0Delta > 0) {
50+
IERC20Minimal(IKatanaV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta));
51+
} else if (amount1Delta > 0) {
52+
IERC20Minimal(IKatanaV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta));
53+
} else {
54+
// if both are not gt 0, both must be 0.
55+
assert(amount0Delta == 0 && amount1Delta == 0);
56+
}
57+
}
58+
59+
function mint(address pool, address recipient, int24 tickLower, int24 tickUpper, uint128 amount) external {
60+
IKatanaV3Pool(pool).mint(recipient, tickLower, tickUpper, amount, abi.encode(msg.sender));
61+
}
62+
63+
event MintCallback(uint256 amount0Owed, uint256 amount1Owed);
64+
65+
function katanaV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata data) external override {
66+
address sender = abi.decode(data, (address));
67+
68+
emit MintCallback(amount0Owed, amount1Owed);
69+
if (amount0Owed > 0) {
70+
IERC20Minimal(IKatanaV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, amount0Owed);
71+
}
72+
if (amount1Owed > 0) {
73+
IERC20Minimal(IKatanaV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, amount1Owed);
74+
}
75+
}
76+
77+
event FlashCallback(uint256 fee0, uint256 fee1);
78+
79+
function flash(address pool, address recipient, uint256 amount0, uint256 amount1, uint256 pay0, uint256 pay1)
80+
external
81+
{
82+
IKatanaV3Pool(pool).flash(recipient, amount0, amount1, abi.encode(msg.sender, pay0, pay1));
83+
}
84+
85+
function katanaV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata data) external override {
86+
emit FlashCallback(fee0, fee1);
87+
88+
(address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256));
89+
90+
if (pay0 > 0) IERC20Minimal(IKatanaV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, pay0);
91+
if (pay1 > 0) IERC20Minimal(IKatanaV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, pay1);
92+
}
93+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity =0.7.6;
3+
4+
import "../libraries/TickMath.sol";
5+
6+
import "../interfaces/callback/IKatanaV3SwapCallback.sol";
7+
8+
import "../interfaces/IKatanaV3Pool.sol";
9+
10+
contract TestKatanaV3ReentrantCallee is IKatanaV3SwapCallback {
11+
string private constant expectedReason = "LOK";
12+
13+
function swapToReenter(address pool) external {
14+
IKatanaV3Pool(pool).swap(address(0), false, 1, TickMath.MAX_SQRT_RATIO - 1, new bytes(0));
15+
}
16+
17+
function katanaV3SwapCallback(int256, int256, bytes calldata) external override {
18+
// try to reenter swap
19+
try IKatanaV3Pool(msg.sender).swap(address(0), false, 1, 0, new bytes(0)) { }
20+
catch Error(string memory reason) {
21+
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
22+
}
23+
24+
// try to reenter mint
25+
try IKatanaV3Pool(msg.sender).mint(address(0), 0, 0, 0, new bytes(0)) { }
26+
catch Error(string memory reason) {
27+
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
28+
}
29+
30+
// try to reenter collect
31+
try IKatanaV3Pool(msg.sender).collect(address(0), 0, 0, 0, 0) { }
32+
catch Error(string memory reason) {
33+
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
34+
}
35+
36+
// try to reenter burn
37+
try IKatanaV3Pool(msg.sender).burn(0, 0, 0) { }
38+
catch Error(string memory reason) {
39+
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
40+
}
41+
42+
// try to reenter flash
43+
try IKatanaV3Pool(msg.sender).flash(address(0), 0, 0, new bytes(0)) { }
44+
catch Error(string memory reason) {
45+
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
46+
}
47+
48+
// try to reenter collectProtocol
49+
try IKatanaV3Pool(msg.sender).collectProtocol(address(0), 0, 0) { }
50+
catch Error(string memory reason) {
51+
require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason)));
52+
}
53+
54+
require(false, "Unable to reenter");
55+
}
56+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity =0.7.6;
3+
4+
import "../libraries/SafeCast.sol";
5+
import "../libraries/TickMath.sol";
6+
7+
import "../interfaces/IERC20Minimal.sol";
8+
import "../interfaces/callback/IKatanaV3SwapCallback.sol";
9+
import "../interfaces/IKatanaV3Pool.sol";
10+
11+
contract TestKatanaV3Router is IKatanaV3SwapCallback {
12+
using SafeCast for uint256;
13+
14+
// flash swaps for an exact amount of token0 in the output pool
15+
function swapForExact0Multi(address recipient, address poolInput, address poolOutput, uint256 amount0Out) external {
16+
address[] memory pools = new address[](1);
17+
pools[0] = poolInput;
18+
IKatanaV3Pool(poolOutput).swap(
19+
recipient, false, -amount0Out.toInt256(), TickMath.MAX_SQRT_RATIO - 1, abi.encode(pools, msg.sender)
20+
);
21+
}
22+
23+
// flash swaps for an exact amount of token1 in the output pool
24+
function swapForExact1Multi(address recipient, address poolInput, address poolOutput, uint256 amount1Out) external {
25+
address[] memory pools = new address[](1);
26+
pools[0] = poolInput;
27+
IKatanaV3Pool(poolOutput).swap(
28+
recipient, true, -amount1Out.toInt256(), TickMath.MIN_SQRT_RATIO + 1, abi.encode(pools, msg.sender)
29+
);
30+
}
31+
32+
event SwapCallback(int256 amount0Delta, int256 amount1Delta);
33+
34+
function katanaV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) public override {
35+
emit SwapCallback(amount0Delta, amount1Delta);
36+
37+
(address[] memory pools, address payer) = abi.decode(data, (address[], address));
38+
39+
if (pools.length == 1) {
40+
// get the address and amount of the token that we need to pay
41+
address tokenToBePaid = amount0Delta > 0 ? IKatanaV3Pool(msg.sender).token0() : IKatanaV3Pool(msg.sender).token1();
42+
int256 amountToBePaid = amount0Delta > 0 ? amount0Delta : amount1Delta;
43+
44+
bool zeroForOne = tokenToBePaid == IKatanaV3Pool(pools[0]).token1();
45+
IKatanaV3Pool(pools[0]).swap(
46+
msg.sender,
47+
zeroForOne,
48+
-amountToBePaid,
49+
zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
50+
abi.encode(new address[](0), payer)
51+
);
52+
} else {
53+
if (amount0Delta > 0) {
54+
IERC20Minimal(IKatanaV3Pool(msg.sender).token0()).transferFrom(payer, msg.sender, uint256(amount0Delta));
55+
} else {
56+
IERC20Minimal(IKatanaV3Pool(msg.sender).token1()).transferFrom(payer, msg.sender, uint256(amount1Delta));
57+
}
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)