harb/onchain/test/BaseLineLP.t.sol

218 lines
56 KiB
Solidity
Raw Normal View History

2024-04-11 07:28:54 +02:00
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
import {PoolAddress, PoolKey} from "@aperture/uni-v3-lib/PoolAddress.sol";
import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol";
2024-04-27 07:04:33 +02:00
import "@aperture/uni-v3-lib/TickMath.sol";
2024-04-11 07:28:54 +02:00
import "../src/interfaces/IWETH9.sol";
2024-04-16 06:58:41 +02:00
import {WETH} from "solmate/tokens/WETH.sol";
2024-04-11 07:28:54 +02:00
import "../src/Harb.sol";
import {Stake, ExceededAvailableStake} from "../src/Stake.sol";
2024-05-29 17:26:19 +02:00
import "./helpers/PoolSerializer.sol";
2024-04-11 07:28:54 +02:00
address constant TAX_POOL = address(2);
// default fee of 1%
uint24 constant FEE = uint24(10_000);
2024-05-29 17:26:19 +02:00
contract BaseLineLPTest is PoolSerializer {
2024-04-11 07:28:54 +02:00
uint256 mainnetFork;
IWETH9 weth;
Harb harb;
IUniswapV3Factory factory;
Stake stake;
2024-05-29 17:26:19 +02:00
BaseLineLP lm;
2024-04-27 07:04:33 +02:00
IUniswapV3Pool pool;
bool token0isWeth;
2024-04-11 07:28:54 +02:00
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
// z is now the integer square root of y, or the closest integer to the square root of y.
}
2024-05-14 09:18:21 +02:00
function initializePoolFor1Cent(address _pool) public {
2024-04-11 07:28:54 +02:00
uint256 price;
2024-04-27 07:04:33 +02:00
if (token0isWeth) {
2024-04-11 07:28:54 +02:00
// ETH as token0, so we are setting the price of 1 ETH in terms of token1 (USD cent)
2024-04-23 06:58:34 +02:00
price = 3000 * 10**20; // 1 ETH = 3700 USD, scaled by 10^18 for precision
2024-04-11 07:28:54 +02:00
} else {
// Token (valued at 1 USD cent) as token0, ETH as token1
// We invert the logic to represent the price of 1 token in terms of ETH
price = uint256(10**16) / 3700; // Adjust for 18 decimal places
}
2024-04-23 06:58:34 +02:00
uint160 sqrtPriceX96 = uint160(sqrt(price) * 2**96 / 10**9); // Adjust sqrt value to 96-bit precision
2024-04-11 07:28:54 +02:00
// Initialize pool with the calculated sqrtPriceX96
2024-05-14 09:18:21 +02:00
IUniswapV3Pool(_pool).initialize(sqrtPriceX96);
2024-04-11 07:28:54 +02:00
}
2024-04-16 06:58:41 +02:00
function deployContract(bytes memory bytecode, bytes memory constructorArgs) internal returns (address addr) {
bytes memory deploymentData = abi.encodePacked(bytecode, constructorArgs);
assembly {
addr := create(0, add(deploymentData, 0x20), mload(deploymentData))
}
require(addr != address(0), "Contract deployment failed");
}
2024-04-11 07:28:54 +02:00
function setUp() public {
2024-04-16 06:58:41 +02:00
bytes memory factoryBytecode = hex"60a060405234801561001057600080fd5b503060601b608052600380546001600160a01b031916339081179091556040516000907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c908290a36101f4600081815260046020527ffb8cf1d12598d1a039dd1d106665851a96aadf67d0d9ed76fceea282119208b7805462ffffff1916600a90811790915560405190929160008051602061614b83398151915291a3610bb8600081815260046020527f72dffa9b822156d9cf4b0090fa0b656bcb9cc2b2c60eb6acfc20a34f54b31743805462ffffff1916603c90811790915560405190929160008051602061614b83398151915291a3612710600081815260046020527f8cc740d51daa94ff54f33bd779c2d20149f524c340519b49181be5a08615f829805462ffffff191660c890811790915560405190929160008051602061614b83398151915291a360805160601c615fd7610174600039806105515250615fd76000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c8063890357301161005b578063890357301461013b5780638a7c195f146101855780638da5cb5b146101b0578063a1671295146101b85761007d565b806313af4035146100825780631698ee82146100aa57806322afcccb14610102575b600080fd5b6100a86004803603602081101561009857600080fd5b50356001600160a01b03166101f4565b005b6100e6600480360360608110156100c057600080fd5b5080356001600160a01b03908116916020810135909116906040013562ffffff16610267565b604080516001600160a01b039092168252519081900360200190f35b6101246004803603602081101561011857600080fd5b503562ffffff16610293565b6040805160029290920b8252519081900360200190f35b6101436102a8565b604080516001600160a01b0396871681529486166020860152929094168383015262ffffff16606083015260029290920b608082015290519081900360a00190f35b6100a86004803603604081101561019b57600080fd5b5062ffffff813516906020013560020b6102de565b6100e66103a1565b6100e6600480360360608110156101ce57600080fd5b5080356001600160a01b03908116916020810135909116906040013562ffffff166103b0565b6003546001600160a01b0316331461020b57600080fd5b6003546040516001600160a01b038084169216907fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c90600090a3600380546001600160a01b0319166001600160a01b0392909216919091179055565b60056020908152600093845260408085208252928452828420905282529020546001600160a01b031681565b60046020526000908152604090205460020b81565b600054600154600280546001600160a01b03938416939283169281169162ffffff600160a01b83041691600160b81b9004900b85565b6003546001600160a01b031633146102f557600080fd5b620f42408262ffffff161061030957600080fd5b60008160020b13801561032057506140008160020b125b61032957600080fd5b62ffffff8216600090815260046020526040902054600290810b900b1561034f57600080fd5b62ffffff828116600081815260046020526040808220805462ffffff1916600287900b958616179055517fc66a3fdf07232cdd185febcc6579d408c241b47ae2f9907d84be655141eeaecc9190a35050565b6003546001600160a01b031681565b60006103ba610546565b826001600160a01b0316846001600160a01b031614156103d957600080fd5b600080846001600160a01b0316866001600160a01b0316106103fc5784866103ff565b85855b90925090506001600160a01b03821661041757600080fd5b62ffffff8416600090815260046020526040902054600290810b9081900b61043e57600080fd5b6001600160a01b0383811660009081526005602090815260408083208685168452825280832062ffffff8a168452909152902054161561047d57600080fd5b61048a308484888561057d565b6001600160a01b03808516600081815260056020818152604080842089871680865290835281852062ffffff8e168087529084528286208054988a166001600160a01b0319998a1681179091558287529484528286208787528452828620818752845294829020805490971684179096558051600289900b815291820192909252815195995091947f783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b71189281900390910190a45050509392505050565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461057b57600080fd5b565b6040805160a0810182526001600160a01b03878116808352878216602080850182905292881684860181905262ffffff888116606080880182905260028a810b6080998a01819052600080546001600160a01b03199081169099178155600180548a1689179055825490981686177fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff16600160a01b8502177fffffffffffff000000ffffffffffffffffffffffffffffffffffffffffffffff16600160b81b91830b909516029390931790925587518087019490945283880192909252828101919091528551808303909101815293019384905282519290
address factoryAddress = deployContract(factoryBytecode, "");
factory = IUniswapV3Factory(factoryAddress);
weth = IWETH9(address(new WETH()));
2024-04-11 07:28:54 +02:00
TwabController tc = new TwabController(60 * 60 * 24, uint32(block.timestamp));
2024-04-16 06:58:41 +02:00
harb = new Harb("HARB", "HARB", factoryAddress, address(weth), tc);
2024-04-27 07:04:33 +02:00
pool = IUniswapV3Pool(factory.createPool(address(weth), address(harb), FEE));
2024-04-11 07:28:54 +02:00
2024-04-27 07:04:33 +02:00
token0isWeth = address(weth) < address(harb);
initializePoolFor1Cent(address(pool));
2024-04-11 07:28:54 +02:00
stake = new Stake(address(harb));
harb.setStakingPool(address(stake));
2024-05-29 17:26:19 +02:00
lm = new BaseLineLP(factoryAddress, address(weth), address(harb));
harb.setLiquidityManager(address(lm));
createCSVHeader();
2024-04-11 07:28:54 +02:00
}
2024-05-29 17:26:19 +02:00
function test_LP() public {
address account = makeAddr("alice");
vm.deal(account, 20 ether);
2024-04-11 07:28:54 +02:00
vm.prank(account);
2024-05-29 17:26:19 +02:00
(bool sent, ) = address(lm).call{value: 10 ether}("");
2024-04-11 07:28:54 +02:00
require(sent, "Failed to send Ether");
2024-04-27 07:04:33 +02:00
// Try to shift liquidity manager's state, expect failure due to initial state
2024-04-11 07:28:54 +02:00
vm.expectRevert();
2024-05-29 17:26:19 +02:00
lm.shift();
2024-04-11 07:28:54 +02:00
2024-06-07 11:22:22 +02:00
// have some time pass to record prices in uni oracle
uint256 timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
2024-04-29 06:27:28 +02:00
// Setup of liquidity
2024-05-29 17:26:19 +02:00
lm.slide();
appendPossitions(lm, pool, token0isWeth);
2024-04-27 07:04:33 +02:00
2024-04-29 06:27:28 +02:00
// Small buy into Anchor
2024-04-27 07:04:33 +02:00
vm.prank(account);
2024-05-29 17:26:19 +02:00
weth.deposit{value: 10 ether}();
2024-04-27 07:04:33 +02:00
vm.prank(account);
2024-05-29 17:26:19 +02:00
weth.approve(address(this), 10 ether);
2024-04-27 07:04:33 +02:00
pool.swap(
account, // Recipient of the output tokens
token0isWeth,
int256(0.5 ether),
token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(account, int256(0.5 ether), true)
);
2024-06-07 11:22:22 +02:00
// have some time pass to record prices in uni oracle
timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
2024-04-27 07:04:33 +02:00
2024-05-29 17:26:19 +02:00
lm.shift();
appendPossitions(lm, pool, token0isWeth);
2024-04-29 06:27:28 +02:00
// large buy into discovery
2024-05-14 09:18:21 +02:00
pool.swap(
2024-04-29 06:27:28 +02:00
account, // Recipient of the output tokens
token0isWeth,
int256(3 ether),
token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(account, int256(3 ether), true)
);
2024-06-07 11:22:22 +02:00
// have some time pass to record prices in uni oracle
timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
2024-04-29 06:27:28 +02:00
2024-05-29 17:26:19 +02:00
lm.shift();
appendPossitions(lm, pool, token0isWeth);
2024-04-29 06:27:28 +02:00
2024-05-14 09:18:21 +02:00
// sell into anchor
2024-04-29 06:27:28 +02:00
vm.prank(account);
2024-05-29 17:26:19 +02:00
harb.approve(address(this), 12000000 ether);
2024-04-29 06:27:28 +02:00
pool.swap(
account, // Recipient of the output tokens
!token0isWeth,
int256(300000 ether),
!token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(account, int256(300000 ether), false)
);
2024-06-07 11:22:22 +02:00
// have some time pass to record prices in uni oracle
timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
2024-04-29 06:27:28 +02:00
2024-05-29 17:26:19 +02:00
lm.slide();
appendPossitions(lm, pool, token0isWeth);
2024-04-29 06:27:28 +02:00
2024-05-14 09:18:21 +02:00
// large sell into floor
2024-04-29 06:27:28 +02:00
harb.setLiquidityManager(address(account));
vm.prank(account);
2024-05-29 17:26:19 +02:00
harb.mint(3600000 ether);
harb.setLiquidityManager(address(lm));
2024-05-14 09:18:21 +02:00
pool.swap(
2024-04-29 06:27:28 +02:00
account, // Recipient of the output tokens
!token0isWeth,
2024-05-29 17:26:19 +02:00
int256(3600000 ether),
2024-04-29 06:27:28 +02:00
!token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
2024-05-29 17:26:19 +02:00
abi.encode(account, int256(3600000 ether), false)
2024-04-29 06:27:28 +02:00
);
2024-06-07 11:22:22 +02:00
// have some time pass to record prices in uni oracle
timeBefore = block.timestamp;
vm.warp(timeBefore + (60 * 60 * 5));
2024-05-29 17:26:19 +02:00
// add to CSV
2024-04-29 06:27:28 +02:00
2024-05-29 17:26:19 +02:00
lm.slide();
appendPossitions(lm, pool, token0isWeth);
writeCsv();
2024-04-27 07:04:33 +02:00
}
2024-05-29 17:26:19 +02:00
2024-04-27 07:04:33 +02:00
/*//////////////////////////////////////////////////////////////
CALLBACKS
//////////////////////////////////////////////////////////////*/
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata _data
)
external
{
require(amount0Delta > 0 || amount1Delta > 0);
(address seller, uint256 harbIn, bool buy) = abi.decode(_data, (address, uint256, bool));
(bool isExactInput, uint256 amountToPay) = amount0Delta > 0
? (!token0isWeth, uint256(amount0Delta))
: (token0isWeth, uint256(amount1Delta));
if (buy) {
weth.transferFrom(seller, msg.sender, amountToPay);
return;
}
require(isExactInput, "reason 1");
// check input amount
require(amountToPay == harbIn, "reason 2");
// transfer eth to univ3 pool
require(harb.transferFrom(seller, msg.sender, amountToPay), "reason 3");
2024-04-11 07:28:54 +02:00
}
2024-04-27 07:04:33 +02:00
receive() external payable {}
2024-04-11 07:28:54 +02:00
}