Skip to content

Commit

Permalink
feat: refactor simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
clement-ux committed Aug 2, 2024
1 parent 54d8a6c commit 4f9919d
Show file tree
Hide file tree
Showing 10 changed files with 509 additions and 17 deletions.
73 changes: 73 additions & 0 deletions test/Actions.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

// Internal utils
import {TickMath} from "test/libraries/TickMath.sol";
import {INonfungiblePositionManager} from "test/interfaces/INonfungiblePositionManager.sol";

// Internal for testing
import {Base_Test_} from "test/Base.sol";

abstract contract Actions is Base_Test_ {
////////////////////////////////////////////////////////////////
/// --- SETUP
////////////////////////////////////////////////////////////////
function setUp() public virtual override {
super.setUp();
}

////////////////////////////////////////////////////////////////
/// --- MANAGE LIQUDITY
////////////////////////////////////////////////////////////////
function addLiquidity(uint256 amount0, uint256 amount1) public returns (uint256, uint128) {
deal(address(token0), address(this), amount0);
deal(address(token1), address(this), amount1);

// Add liquidity
(uint256 tokenId, uint128 liquidity,,) = nftManager.mint(
INonfungiblePositionManager.MintParams({
token0: address(token0),
token1: address(token1),
tickSpacing: TICK_SPACING,
tickLower: LOWER_TICK,
tickUpper: UPPER_TICK,
amount0Desired: amount0,
amount1Desired: amount1,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp + 100,
sqrtPriceX96: 0
})
);

return (tokenId, liquidity);
}

////////////////////////////////////////////////////////////////
/// --- MANAGE GAUGE
////////////////////////////////////////////////////////////////
function stake(uint256 tokenId) public {
gauge.deposit(tokenId);
}

////////////////////////////////////////////////////////////////
/// --- MANAGE SWAP
////////////////////////////////////////////////////////////////
function swap(address tokenIn, uint256 amountIn) public {
deal(tokenIn, address(this), amountIn);

bool zeroForOne = tokenIn == address(token0);
int256 amountSpecified = zeroForOne ? int256(amountIn) : -int256(amountIn);
uint160 sqrtPriceLimitX96 = zeroForOne ? TickMath.getSqrtRatioAtTick(-100) : TickMath.getSqrtRatioAtTick(100);

// Swap
pool.swap({
recipient: address(this),
zeroForOne: zeroForOne,
amountSpecified: amountSpecified,
sqrtPriceLimitX96: sqrtPriceLimitX96,
data: ""
});
}
}
104 changes: 104 additions & 0 deletions test/Base.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

// Foundry
import {Test} from "forge-std/Test.sol";

// Solmate
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {MockERC20} from "@solmate/test/utils/mocks/MockERC20.sol";

// Aerodrome
import {IVoter} from "test/interfaces/IVoter.sol";
import {ICLPool} from "test/interfaces/ICLPool.sol";
import {ICLGauge} from "test/interfaces/ICLGauge.sol";
import {ICLPoolFactory} from "test/interfaces/ICLPoolFactory.sol";
import {INonfungiblePositionManager} from "test/interfaces/INonfungiblePositionManager.sol";

// Internal utils
import {Base} from "test/utils/Addresses.sol";
import {TickMath} from "test/libraries/TickMath.sol";

abstract contract Base_Test_ is Test {
////////////////////////////////////////////////////////////////
/// --- CONSTANTS & IMMUTABLES
////////////////////////////////////////////////////////////////
int24 public constant LOWER_TICK = 0;
int24 public constant UPPER_TICK = 1;
int24 public constant TICK_SPACING = 1;
uint256 public constant DEFAULT_AMOUNT = 100 ether;

IVoter public immutable voter = IVoter(Base.VOTER);
uint160 public immutable initialPriceX96 = TickMath.getSqrtRatioAtTick(0);
ICLPoolFactory public immutable poolFactory = ICLPoolFactory(Base.CLPOOL_FACTORY);

////////////////////////////////////////////////////////////////
/// --- CONTRACTS & INTERFACES
////////////////////////////////////////////////////////////////
ERC20 public token0; // OETHb
ERC20 public token1; // WETH
ERC20 public rewardToken;

ICLPool public pool;
ICLGauge public gauge;
INonfungiblePositionManager public nftManager;

////////////////////////////////////////////////////////////////
/// --- STATE VARIABLES
////////////////////////////////////////////////////////////////
uint256 public liquidityRatio = 8e17; // 80% OETHb, 20% WETH

////////////////////////////////////////////////////////////////
/// --- SETUP
////////////////////////////////////////////////////////////////
function setUp() public virtual {
// 1. Create fork
vm.createSelectFork("base", 17906760);

// 2. Create Tokens
token0 = ERC20(new MockERC20("Origin ETH Base", "OETHb", 18));
token1 = ERC20(new MockERC20("Wrapped ETH", "WETH", 18));
rewardToken = ERC20(new MockERC20("Reward Token", "RT", 18));
if (token1 < token0) (token0, token1) = (token1, token0); // Needed for pool address computing

// 3. Whitelist token0 and token1 in Voter
vm.startPrank(Base.GOV_VOTER);
voter.whitelistToken(address(token0), true);
voter.whitelistToken(address(token1), true);
vm.stopPrank();

// 4. Create Pool
pool = ICLPool(
poolFactory.createPool({
tokenA: address(token0),
tokenB: address(token1),
tickSpacing: TICK_SPACING,
sqrtPriceX96: initialPriceX96
})
);

// 5. Create Gauge
gauge = ICLGauge(payable(voter.createGauge({_poolFactory: address(poolFactory), _pool: address(pool)})));
nftManager = INonfungiblePositionManager(payable(pool.nft()));

// 6. Max approve all tokens
token0.approve(address(pool), type(uint256).max);
token1.approve(address(pool), type(uint256).max);
token0.approve(address(nftManager), type(uint256).max);
token1.approve(address(nftManager), type(uint256).max);
nftManager.setApprovalForAll(address(gauge), true);

// 7. Label contracts
vm.label(address(token0), "OETHb");
vm.label(address(token1), "WETH");
vm.label(address(rewardToken), "Reward Token");
vm.label(address(pool), "CLPool OETHb/WETH");
vm.label(address(gauge), "CLGauge OETHb/WETH");
vm.label(address(nftManager), "NFTManager");
}

////////////////////////////////////////////////////////////////
/// --- CALLBACK
////////////////////////////////////////////////////////////////
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {}
}
30 changes: 30 additions & 0 deletions test/Simulator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

// Solmate
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";

// Internal for testing
import {Actions} from "test/Actions.sol";

contract Simulator is Actions {
using FixedPointMathLib for uint256;

////////////////////////////////////////////////////////////////
/// --- SETUP
////////////////////////////////////////////////////////////////
function setUp() public virtual override {
super.setUp();
}

////////////////////////////////////////////////////////////////
/// --- SIMULATION
////////////////////////////////////////////////////////////////
function test1() public {
(uint256 tokenId,) =
addLiquidity(DEFAULT_AMOUNT.mulWadDown(liquidityRatio), DEFAULT_AMOUNT.mulWadUp(1e18 - liquidityRatio));
stake(tokenId);
swap(address(token0), 10 ether);
nftManager.positions(tokenId);
}
}
84 changes: 71 additions & 13 deletions test/Simulator.t.sol → test/SimulatorOld.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,76 @@
pragma solidity 0.8.25;

// Foundry
import {Test} from "forge-std/Test.sol";
import {Test, console} from "forge-std/Test.sol";

// Solmate
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {MockERC20} from "@solmate/test/utils/mocks/MockERC20.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";

// Aerodrome
import {IVoter} from "test/interfaces/IVoter.sol";
import {ICLPool} from "test/interfaces/ICLPool.sol";
import {ICLFactory} from "test/interfaces/ICLFactory.sol";
import {ICLGauge} from "test/interfaces/ICLGauge.sol";
import {ICLPoolFactory} from "test/interfaces/ICLPoolFactory.sol";
import {ICLGaugeFactory} from "test/interfaces/ICLGaugeFactory.sol";
import {IFactoryRegistry} from "test/interfaces/IFactoryRegistry.sol";
import {INonfungiblePositionManager} from "test/interfaces/INonfungiblePositionManager.sol";

// Internal utils
import {Base} from "test/utils/Addresses.sol";
import {TickMath} from "test/libraries/TickMath.sol";

/*
contract Simulator is Test {
using FixedPointMathLib for uint256;
////////////////////////////////////////////////////////////////
/// --- CONSTANTS & IMMUTABLES
////////////////////////////////////////////////////////////////
bool public constant ENABLE_GAUGE_AT_CREATION = true;
int24 public constant TICK_SPACING = 1;
uint256 public constant DEFAULT_AMOUNT = 100 ether;
uint160 public immutable INITIAL_SQRTPRICEX96 = TickMath.getSqrtRatioAtTick(0);
////////////////////////////////////////////////////////////////
/// --- CONTRACTS & INTERFACES
////////////////////////////////////////////////////////////////
ERC20 public token0; // OETHb
ERC20 public token1; // WETH
ERC20 public rewardToken;
IVoter public voter;
ICLPool public pool;
ICLFactory public factory;
ICLGauge public gauge;
ICLPoolFactory public poolFactory;
ICLGaugeFactory public gaugeFactory;
IFactoryRegistry public factoryRegistry;
INonfungiblePositionManager public nftManager;
uint256 public ratio = 8e17; // 80% OETHb, 20% WETH
////////////////////////////////////////////////////////////////
/// --- SETUP
////////////////////////////////////////////////////////////////
function setUp() public {
token1 = ERC20(new MockERC20("Wrapped ETH", "WETH", 18));
token0 = ERC20(new MockERC20("Origin ETH Base", "OETHb", 18));
factory = ICLFactory(Base.CLFACTORY);
rewardToken = ERC20(new MockERC20("Reward Token", "RT", 18));
voter = IVoter(Base.AERODROME_VOTER);
poolFactory = ICLPoolFactory(Base.CLPOOL_FACTORY);
factoryRegistry = IFactoryRegistry(Base.FACTORY_REGISTRY);
nftManager = INonfungiblePositionManager(payable(Base.NFT_POSITION_MANAGER));
// Ensure token0 is less than token1, otherwise it will fail when compute pool address
require(token0 < token1, "Token0 must be less than Token1");
// Create fork
vm.createSelectFork(vm.envString("BASE_PROVIDER_URL"), vm.envUint("BASE_BLOCK_NUMBER"));
vm.createSelectFork("base", 17906760);
// Create pool with token0 and token1
pool = ICLPool(
factory.createPool({
poolFactory.createPool({
tokenA: address(token0),
tokenB: address(token1),
tickSpacing: TICK_SPACING,
Expand All @@ -64,20 +85,36 @@ contract Simulator is Test {
token0.approve(address(nftManager), type(uint256).max);
token1.approve(address(nftManager), type(uint256).max);
/*
// Label all addresses
vm.label(address(token0), "Token0");
vm.label(address(token1), "Token1");
vm.label(address(pool), "Pool T0/T1");
vm.label(address(factory), "CLFactory");
vm.label(address(rewardToken), "Reward Token");
vm.label(address(pool), "CLPool T0/T1");
vm.label(address(poolFactory), "CLFactory");
vm.label(Base.CLPOOL_IMPL, "CLPool Implementation");
vm.label(address(nftManager), "NFT Position Manager");
//vm.label(address(gauge), "Gauge T0/T1");
vm.label(Base.CLGAUGE_IMPL, "CLGauge Implementation");
vm.label(address(gaugeFactory), "CLGaugeFactory");
vm.label(Base.FACTORY_REGISTRY, "Factory Registry");
vm.label(Base.AERODROME_VOTER, "Aerodrome Voter");
vm.label(address(factoryRegistry), "Factory Registry");
vm.label(address(voter), "Voter");
(, address gaugeFactory_) = factoryRegistry.factoriesToPoolFactory(address(poolFactory));
gaugeFactory = ICLGaugeFactory(gaugeFactory_);
// Add a gauge to the pool
if (ENABLE_GAUGE_AT_CREATION) enableGauge();
}
function test() public {
addLiquidity(100 ether, 100 ether);
swap(address(token0), 10 ether);
(uint256 tokenId,) = addLiquidity(DEFAULT_AMOUNT.mulWadDown(ratio), DEFAULT_AMOUNT.mulWadDown(1e18 - ratio));
stake(tokenId);
//swap(address(token0), 10 ether);
}
/*
////////////////////////////////////////////////////////////////
/// --- ACTIONS
////////////////////////////////////////////////////////////////
Expand All @@ -93,8 +130,8 @@ contract Simulator is Test {
tickSpacing: 1,
tickLower: 0,
tickUpper: 1,
amount0Desired: 100 ether,
amount1Desired: 100 ether,
amount0Desired: amount0,
amount1Desired: amount1,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
Expand All @@ -106,6 +143,10 @@ contract Simulator is Test {
return (tokenId, liquidity);
}
function stake(uint256 tokenId) public {
gauge.deposit(tokenId);
}
function swap(address tokenIn, uint256 amountIn) public {
deal(tokenIn, address(this), amountIn);
Expand All @@ -123,8 +164,25 @@ contract Simulator is Test {
});
}
function enableGauge() public {
vm.prank(Base.AERODROME_VOTER);
voter.createGauge(address(poolFactory), address(pool));
gauge = ICLGauge(
payable(
gaugeFactory.createGauge({
_forwarder: address(0), // not used
_pool: _pool,
_feesVotingReward: address(0),
_rewardToken: _rewardToken,
_isPool: false // seems to be only for old config
})
)
);
}
////////////////////////////////////////////////////////////////
/// --- CALLBACK
////////////////////////////////////////////////////////////////
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {}
}
}*/
Loading

0 comments on commit 4f9919d

Please sign in to comment.