basic test environment works
This commit is contained in:
17
foundry.toml
17
foundry.toml
@@ -1,9 +1,20 @@
|
||||
[profile.default]
|
||||
solc_version = '0.7.6'
|
||||
libs = ['lib']
|
||||
remappings = [
|
||||
'@openzeppelin/contracts/=lib/openzeppelin/contracts/',
|
||||
'@uniswap/v3-core/=lib/uniswap/v3-core/',
|
||||
'@uniswap/v3-periphery/=lib/uniswap/v3-periphery/',
|
||||
'@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/',
|
||||
'@uniswap/v3-core/=lib/v3-core/',
|
||||
'@uniswap/v3-periphery/=lib/v3-periphery/',
|
||||
]
|
||||
optimizer=true
|
||||
optimizer_runs=999999999
|
||||
sizes = true
|
||||
via_ir = false
|
||||
gas_reports = ['*']
|
||||
gas_reports_ignore = []
|
||||
|
||||
[profile.default.rpc_endpoints]
|
||||
# todo put these into a secrets file
|
||||
arbitrum_alchemy='https://arb-mainnet.g.alchemy.com/v2/L0eaUwWEoWzszCK9EhqHdl_p7VHStkaC'
|
||||
arbitrum_ankr='https://rpc.ankr.com/arbitrum/056ed471570655a3bb77cf31b9e3576658d63d2acb88911f84e7acaf211b55ac'
|
||||
arbitrum_publicnode='https://arbitrum-one.publicnode.com'
|
||||
|
||||
3
lib/libs.txt
Normal file
3
lib/libs.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Uniswap/v3-core
|
||||
Uniswap/v3-periphery
|
||||
OpenZeppelin/openzeppelin-contracts@v3.4.2-solc-0.7
|
||||
@@ -1,22 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity =0.7.6;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
// the version of OpenZeppelin required by Uniswap v3 doesn't have IERC20Metadata yet, so we copy it here.
|
||||
interface IERC20Metadata is IERC20 {
|
||||
/**
|
||||
* @dev Returns the name of the token.
|
||||
*/
|
||||
function name() external view returns (string memory);
|
||||
|
||||
/**
|
||||
* @dev Returns the symbol of the token.
|
||||
*/
|
||||
function symbol() external view returns (string memory);
|
||||
|
||||
/**
|
||||
* @dev Returns the decimals places of the token.
|
||||
*/
|
||||
function decimals() external view returns (uint8);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import "openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "./IERC20Metadata.sol";
|
||||
pragma solidity =0.7.6;
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
|
||||
contract MockERC20 is ERC20, IERC20Metadata {
|
||||
contract MockERC20 is ERC20 {
|
||||
|
||||
constructor(string name, string symbol, uint8 decimals)
|
||||
constructor(string memory name, string memory symbol, uint8 decimals)
|
||||
ERC20(name, symbol)
|
||||
{
|
||||
_setupDecimals(decimals);
|
||||
|
||||
14
src/Util.sol
Normal file
14
src/Util.sol
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity =0.7.6;
|
||||
pragma abicoder v2;
|
||||
|
||||
library Util {
|
||||
function roundTick(int24 tick, int24 window) public pure returns (int24) {
|
||||
// NOTE: we round half toward zero
|
||||
int24 mod = tick % window;
|
||||
if (tick < 0)
|
||||
return - mod <= window / 2 ? tick - mod : tick - (window + mod);
|
||||
else
|
||||
return mod > window / 2 ? tick + (window - mod) : tick - mod;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,95 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity =0.7.6;
|
||||
pragma abicoder v2;
|
||||
|
||||
import "forge-std/console2.sol";
|
||||
import "forge-std/Test.sol";
|
||||
import "../src/MockERC20.sol";
|
||||
import "uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
|
||||
import "uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
|
||||
import "uniswap/v3-core/contracts/libraries/TickMath.sol";
|
||||
import "uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol";
|
||||
import "uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
|
||||
import "uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";
|
||||
import "../src/Util.sol";
|
||||
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
|
||||
import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
|
||||
import "@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol";
|
||||
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
|
||||
import "@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";
|
||||
|
||||
|
||||
contract TestSinglePool {
|
||||
contract TestSinglePool is Test {
|
||||
|
||||
IUniswapV3Factory public factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
|
||||
INonfungiblePositionManager public nfpm = INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
|
||||
ISwapRouter public swapper = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
|
||||
IUniswapV3Pool public immutable pool;
|
||||
IUniswapV3Pool public pool;
|
||||
uint24 public fee;
|
||||
MockERC20 public WETH;
|
||||
MockERC20 public USDC;
|
||||
MockERC20 public token0; // either WETH or USDC depending on the order in the pool
|
||||
MockERC20 public token1;
|
||||
MockERC20 public COIN;
|
||||
MockERC20 public USD;
|
||||
address public token0; // either WETH or USDC depending on the order in the pool
|
||||
address public token1;
|
||||
bool public inverted;
|
||||
|
||||
function setUp() public {
|
||||
MockERC20 weth = MockERC20('Mock Wrapped Ethereum', 'WETH', 18);
|
||||
MockERC20 usdc = MockERC20('Mock USD Coin', 'USDC', 6);
|
||||
uint24 fee_ = 500;
|
||||
fee = fee_;
|
||||
WETH = weth;
|
||||
USDC = usdc;
|
||||
IUniswapV3Pool pool_ = UniswapV3Pool(factory.createPool(address(weth), address(usdc), fee_));
|
||||
pool = pool_;
|
||||
token0 = pool_.token0();
|
||||
token1 = pool_.token1();
|
||||
COIN = new MockERC20('Mock Coin', 'MOCK', 18);
|
||||
USD = new MockERC20('Universally Supported Dollars', 'USD', 6);
|
||||
fee = 500;
|
||||
inverted = address(COIN) > address(USD);
|
||||
token0 = inverted ? address(USD) : address(COIN);
|
||||
token1 = inverted ? address(COIN) : address(USD);
|
||||
uint160 initialPrice = 1 * 2**96;
|
||||
// if this reverts here make sure Anvil is started and you are running forge with --rpc-url
|
||||
pool = IUniswapV3Pool(nfpm.createAndInitializePoolIfNecessary(token0, token1, fee, initialPrice));
|
||||
int24 ts = pool.tickSpacing();
|
||||
(, int24 lower, , , , ,) = pool.slot0();
|
||||
int24 upper = lower;
|
||||
for (int8 i = 0; i < 10; i++) {
|
||||
lower -= ts;
|
||||
upper += ts;
|
||||
stake(1 * 10 ** COIN.decimals(), 1000 * 10 ** USD.decimals(), lower, upper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function stake(uint128 liquidity_, int24 lower, int24 upper) public
|
||||
returns (
|
||||
uint256 tokenId,
|
||||
uint128 liquidity,
|
||||
uint256 amount0,
|
||||
uint256 amount1
|
||||
)
|
||||
{
|
||||
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(lower);
|
||||
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(upper);
|
||||
(uint160 sqrtPriceX96, , , , , ,) = pool.slot0();
|
||||
(amount0, amount1) = LiquidityAmounts.getAmountsForLiquidity(sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, liquidity_);
|
||||
return _stake(amount0, amount1, lower, upper);
|
||||
}
|
||||
|
||||
function testSwap() public {
|
||||
COIN.mint(address(this), 1 * 10**18);
|
||||
uint256 usd = swap(COIN, USD, 1 * 10**18);
|
||||
console2.log(usd);
|
||||
}
|
||||
|
||||
function stake(uint256 coinAmount, uint256 usdAmount, int24 lower, int24 upper) public
|
||||
returns (
|
||||
uint256 tokenId,
|
||||
uint128 liquidity,
|
||||
uint256 amount0,
|
||||
uint256 amount1
|
||||
)
|
||||
{
|
||||
return _stake(coinAmount, usdAmount, lower, upper);
|
||||
}
|
||||
|
||||
function _stake(uint256 coinAmount, uint256 usdAmount, int24 lower, int24 upper) private
|
||||
returns (
|
||||
uint256 tokenId,
|
||||
uint128 liquidity,
|
||||
uint256 amount0,
|
||||
uint256 amount1
|
||||
)
|
||||
{
|
||||
COIN.mint(address(this), coinAmount);
|
||||
COIN.approve(address(nfpm), coinAmount);
|
||||
USD.mint(address(this), usdAmount);
|
||||
USD.approve(address(nfpm), usdAmount);
|
||||
// struct MintParams {
|
||||
// address token0;
|
||||
// address token1;
|
||||
@@ -49,23 +103,38 @@ contract TestSinglePool {
|
||||
// address recipient;
|
||||
// uint256 deadline;
|
||||
// }
|
||||
|
||||
function stake(uint160 liquidity, uint24 lower, uint24 upper) public {
|
||||
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(lower);
|
||||
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(upper);
|
||||
(uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
|
||||
(uint256 amount0, uint256 amount1) = LiquidityAmounts.getAmountsForLiquidity(liquidity, sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96);
|
||||
token0.mint(amount0);
|
||||
token1.mint(amount1);
|
||||
int24 ts = pool.tickSpacing();
|
||||
lower = Util.roundTick(lower, ts);
|
||||
upper = Util.roundTick(upper, ts);
|
||||
(uint256 a0, uint256 a1) = inverted ? (usdAmount, coinAmount) : (coinAmount, usdAmount);
|
||||
INonfungiblePositionManager.MintParams memory params = INonfungiblePositionManager.MintParams(
|
||||
address(token0), address(token1), fee, lower, upper, amount0, amount1, 0, 0, msg.sender, block.timestamp
|
||||
token0, token1, fee, lower, upper, a0, a1, 0, 0, msg.sender, block.timestamp
|
||||
);
|
||||
nfpm.mint(params);
|
||||
return nfpm.mint(params);
|
||||
}
|
||||
|
||||
|
||||
function swap() public {
|
||||
function swap(MockERC20 inToken, MockERC20 outToken, uint256 amountIn) public returns (uint256 amountOut) {
|
||||
uint160 limit = address(inToken) == pool.token0() ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1;
|
||||
return swap(inToken, outToken, amountIn, limit);
|
||||
}
|
||||
|
||||
function swap(MockERC20 inToken, MockERC20 outToken, uint256 amountIn, uint160 sqrtPriceLimitX96) public returns (uint256 amountOut) {
|
||||
inToken.approve(address(swapper), amountIn);
|
||||
// struct ExactInputSingleParams {
|
||||
// address tokenIn;
|
||||
// address tokenOut;
|
||||
// uint24 fee;
|
||||
// address recipient;
|
||||
// uint256 deadline;
|
||||
// uint256 amountIn;
|
||||
// uint256 amountOutMinimum;
|
||||
// uint160 sqrtPriceLimitX96;
|
||||
// }
|
||||
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams(
|
||||
address(inToken), address(outToken), fee, msg.sender, block.timestamp, amountIn, 0, sqrtPriceLimitX96
|
||||
);
|
||||
return swapper.exactInputSingle(params);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user