diff --git a/onchain/README.md b/onchain/README.md index 30633ab..f57b721 100644 --- a/onchain/README.md +++ b/onchain/README.md @@ -81,16 +81,25 @@ $ cast --help ### Harb -address: 0x275403401f9c6f4659b6ffb6ab01798e1de9a912 +address: 0xcd02666582a2057085edabc55c5120155ba4e93c abi: ``` -[{"type":"constructor","inputs":[{"name":"name_","type":"string","internalType":"string"},{"name":"symbol_","type":"string","internalType":"string"},{"name":"twabController_","type":"address","internalType":"contract TwabController"}],"stateMutability":"nonpayable"},{"type":"function","name":"DOMAIN_SEPARATOR","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"TAX_POOL","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"allowance","inputs":[{"name":"owner","type":"address","internalType":"address"},{"name":"spender","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"approve","inputs":[{"name":"spender","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"balanceOf","inputs":[{"name":"_account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"burn","inputs":[{"name":"_amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"type":"function","name":"decreaseAllowance","inputs":[{"name":"spender","type":"address","internalType":"address"},{"name":"subtractedValue","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"eip712Domain","inputs":[],"outputs":[{"name":"fields","type":"bytes1","internalType":"bytes1"},{"name":"name","type":"string","internalType":"string"},{"name":"version","type":"string","internalType":"string"},{"name":"chainId","type":"uint256","internalType":"uint256"},{"name":"verifyingContract","type":"address","internalType":"address"},{"name":"salt","type":"bytes32","internalType":"bytes32"},{"name":"extensions","type":"uint256[]","internalType":"uint256[]"}],"stateMutability":"view"},{"type":"function","name":"increaseAllowance","inputs":[{"name":"spender","type":"address","internalType":"address"},{"name":"addedValue","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"liquidityManager","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"mint","inputs":[{"name":"_amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"name","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"nonces","inputs":[{"name":"owner","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"permit","inputs":[{"name":"owner","type":"address","internalType":"address"},{"name":"spender","type":"address","internalType":"address"},{"name":"value","type":"uint256","internalType":"uint256"},{"name":"deadline","type":"uint256","internalType":"uint256"},{"name":"v","type":"uint8","internalType":"uint8"},{"name":"r","type":"bytes32","internalType":"bytes32"},{"name":"s","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setLiquidityManager","inputs":[{"name":"liquidityManager_","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setStakingPool","inputs":[{"name":"stakingPool_","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"stakingPool","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"symbol","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"transfer","inputs":[{"name":"to","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"transferFrom","inputs":[{"name":"from","type":"address","internalType":"address"},{"name":"to","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"type":"function","name":"twabController","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract TwabController"}],"stateMutability":"view"},{"type":"event","name":"Approval","inputs":[{"name":"owner","type":"address","indexed":true,"internalType":"address"},{"name":"spender","type":"address","indexed":true,"internalType":"address"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"EIP712DomainChanged","inputs":[],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"name":"from","type":"address","indexed":true,"internalType":"address"},{"name":"to","type":"address","indexed":true,"internalType":"address"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"error","name":"InvalidShortString","inputs":[]},{"type":"error","name":"StringTooLong","inputs":[{"name":"str","type":"string","internalType":"string"}]},{"type":"error","name":"ZeroAddressInConstructor","inputs":[]}] +[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WETH9","type":"address"},{"internalType":"contract TwabController","name":"twabController_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[],"name":"ZeroAddressInConstructor","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TAX_POOL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"claimUbi","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getUbiDue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidityManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"liquidityManager_","type":"address"}],"name":"setLiquidityManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stakingPool_","type":"address"}],"name":"setStakingPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sumTaxCollected","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"twabController","outputs":[{"internalType":"contract TwabController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ubiTitles","outputs":[{"internalType":"uint256","name":"sumTaxCollected","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"stateMutability":"view","type":"function"}] ``` ### Stake -address: 0xe7cba9fc61c53176527d4bd1d5868f9c0ae3d8e1 +address: 0x4256777543814d66f9F66390f6BB33c55A24C331 abi: ``` -[{"type":"constructor","inputs":[{"name":"_tokenContract","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"dormantSupply","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"exitPosition","inputs":[{"name":"positionID","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"minStake","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"outstandingStake","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"payTax","inputs":[{"name":"positionID","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"positions","inputs":[{"name":"positionID","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"share","type":"uint256","internalType":"uint256"},{"name":"owner","type":"address","internalType":"address"},{"name":"creationTime","type":"uint32","internalType":"uint32"},{"name":"lastTaxTime","type":"uint32","internalType":"uint32"},{"name":"taxRate","type":"uint32","internalType":"uint32"}],"stateMutability":"view"},{"type":"function","name":"snatch","inputs":[{"name":"assets","type":"uint256","internalType":"uint256"},{"name":"receiver","type":"address","internalType":"address"},{"name":"taxRate","type":"uint32","internalType":"uint32"},{"name":"positionsToSnatch","type":"uint256[]","internalType":"uint256[]"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"taxDue","inputs":[{"name":"positionID","type":"uint256","internalType":"uint256"},{"name":"taxFloorDuration","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"amountDue","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"error","name":"ExceededAvailableStake","inputs":[{"name":"receiver","type":"address","internalType":"address"},{"name":"stakeWanted","type":"uint256","internalType":"uint256"},{"name":"availableStake","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"NoPermission","inputs":[{"name":"requester","type":"address","internalType":"address"},{"name":"owner","type":"address","internalType":"address"}]},{"type":"error","name":"PositionNotFound","inputs":[]},{"type":"error","name":"SharesTooLow","inputs":[{"name":"receiver","type":"address","internalType":"address"},{"name":"assets","type":"uint256","internalType":"uint256"},{"name":"sharesWanted","type":"uint256","internalType":"uint256"},{"name":"minStake","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TaxTooLow","inputs":[{"name":"receiver","type":"address","internalType":"address"},{"name":"taxRateWanted","type":"uint64","internalType":"uint64"},{"name":"taxRateMet","type":"uint64","internalType":"uint64"},{"name":"positionId","type":"uint256","internalType":"uint256"}]}] -``` \ No newline at end of file +[{"inputs":[{"internalType":"address","name":"_tokenContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"stakeWanted","type":"uint256"},{"internalType":"uint256","name":"availableStake","type":"uint256"}],"name":"ExceededAvailableStake","type":"error"},{"inputs":[{"internalType":"address","name":"requester","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"NoPermission","type":"error"},{"inputs":[],"name":"PositionNotFound","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"sharesWanted","type":"uint256"},{"internalType":"uint256","name":"minStake","type":"uint256"}],"name":"SharesTooLow","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint64","name":"taxRateWanted","type":"uint64"},{"internalType":"uint64","name":"taxRateMet","type":"uint64"},{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"TaxTooLow","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"creationTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"taxRate","type":"uint32"}],"name":"PositionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"positionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"lastTaxTime","type":"uint32"}],"name":"PositionRemoved","type":"event"},{"inputs":[],"name":"dormantSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"exitPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextPositionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"outstandingStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionID","type":"uint256"}],"name":"payTax","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"positions","outputs":[{"internalType":"uint256","name":"share","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint32","name":"creationTime","type":"uint32"},{"internalType":"uint32","name":"lastTaxTime","type":"uint32"},{"internalType":"uint32","name":"taxRate","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint32","name":"taxRate","type":"uint32"},{"internalType":"uint256[]","name":"positionsToSnatch","type":"uint256[]"}],"name":"snatch","outputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionID","type":"uint256"},{"internalType":"uint256","name":"taxFloorDuration","type":"uint256"}],"name":"taxDue","outputs":[{"internalType":"uint256","name":"amountDue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] +``` + + +## References + +- take percentage math from here: https://github.com/attestate/libharberger/tree/master +- implement this ERC: https://eips.ethereum.org/EIPS/eip-4907 +- add this function: https://github.com/721labs/partial-common-ownership/blob/3e7713bc60b6bb2e103320036ec5aeaaaceb7d2b/contracts/token/modules/Taxation.sol#L260 +- address this issue: "Seems like an owner could always frontrun buy attempts by increasing the valuation by one wei." +- rename TAX_FLOOR_DURATION to cooldown? diff --git a/onchain/script/Deploy.sol b/onchain/script/Deploy.sol index 907dcc5..d2cfc12 100644 --- a/onchain/script/Deploy.sol +++ b/onchain/script/Deploy.sol @@ -1,13 +1,15 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.19; import "forge-std/Script.sol"; import {TwabController} from "pt-v5-twab-controller/TwabController.sol"; import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol"; import "../src/Harb.sol"; import "../src/Stake.sol"; +import {BaseLineLP} from "../src/BaseLineLP.sol"; address constant WETH = 0xb16F35c0Ae2912430DAc15764477E179D9B9EbEa; //Sepolia address constant V3_FACTORY = 0x0227628f3F023bb0B980b67D528571c95c6DaC1c; //Sepolia +address constant TWABC = 0x79Deda2d38A3232fE9b68DcB7d6b461d793A7236; //new TwabController(60 * 60 * 24, uint32(block.timestamp)); uint24 constant FEE = uint24(10_000); contract SepoliaScript is Script { @@ -18,14 +20,14 @@ contract SepoliaScript is Script { uint256 privateKey = vm.deriveKey(seedPhrase, 0); vm.startBroadcast(privateKey); - TwabController tc = new TwabController(60 * 60 * 24, uint32(block.timestamp)); + TwabController tc = TwabController(TWABC); Harb harb = new Harb("Harberger Tax", "HARB", V3_FACTORY, WETH, tc); Stake stake = new Stake(address(harb)); harb.setStakingPool(address(stake)); IUniswapV3Factory factory = IUniswapV3Factory(V3_FACTORY); factory.createPool(WETH, address(harb), FEE); - LiquidityManager liquidityManager = new LiquidityManager(V3_FACTORY, WETH, address(harb)); - harb.setLiquidityManager(address(liquidityManager)); + // BaseLineLP liquidityManager = new BaseLineLP(V3_FACTORY, WETH, address(harb)); + // harb.setLiquidityManager(address(liquidityManager)); vm.stopBroadcast(); } } diff --git a/onchain/src/BaseLineLP.sol b/onchain/src/BaseLineLP.sol new file mode 100644 index 0000000..fa89c7d --- /dev/null +++ b/onchain/src/BaseLineLP.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.20; + +import "@uniswap-v3-periphery/libraries/PositionKey.sol"; +import "@uniswap-v3-core/libraries/FixedPoint128.sol"; +import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol"; +import "@aperture/uni-v3-lib/TickMath.sol"; +import "@aperture/uni-v3-lib/LiquidityAmounts.sol"; +import "@aperture/uni-v3-lib/PoolAddress.sol"; +import "@aperture/uni-v3-lib/CallbackValidation.sol"; +import "@openzeppelin/token/ERC20/IERC20.sol"; +import "./interfaces/IWETH9.sol"; +import {Harb} from "./Harb.sol"; + +/** + * @title LiquidityManager - A contract that supports the harb ecosystem. It + * protects the communities liquidity while allowing a manager role to + * take strategic liqudity positions. + */ +contract BaseLineLP { + // default fee of 1% + uint24 constant FEE = uint24(10_000); + + // the address of the Uniswap V3 factory + address immutable factory; + IWETH9 immutable weth; + Harb immutable harb; + IUniswapV3Pool immutable pool; + PoolKey private poolKey; + bool immutable token0isWeth; + + + struct TokenPosition { + // the liquidity of the position + uint128 liquidity; + int24 tickLower; + int24 tickUpper; + // the fee growth of the aggregate position as of the last action on the individual position + uint256 feeGrowthInside0LastX128; + uint256 feeGrowthInside1LastX128; + } + + TokenPosition public floor; + TokenPosition public anchor; + TokenPosition public discovery; + + modifier checkDeadline(uint256 deadline) { + require(block.timestamp <= deadline, "Transaction too old"); + _; + } + + /// @notice Emitted when liquidity is increased for a position + /// @param liquidity The amount by which liquidity for the NFT position was increased + /// @param amount0 The amount of token0 that was paid for the increase in liquidity + /// @param amount1 The amount of token1 that was paid for the increase in liquidity + event IncreaseLiquidity(int24 indexed tickLower, int24 indexed tickUpper, uint128 liquidity, uint256 amount0, uint256 amount1); + /// @notice Emitted when liquidity is decreased for a position + /// @param liquidity The amount by which liquidity for the NFT position was decreased + /// @param ethReceived The amount of WETH that was accounted for the decrease in liquidity + event PositionLiquidated(int24 indexed tickLower, int24 indexed tickUpper, uint128 liquidity, uint256 ethReceived); + + constructor(address _factory, address _WETH9, address _harb) { + factory = _factory; + weth = IWETH9(_WETH9); + poolKey = PoolAddress.getPoolKey(_WETH9, _harb, FEE); + pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + harb = Harb(_harb); + token0isWeth = _WETH9 < _harb; + } + + function posKey(int24 tickLower, int24 tickUpper) internal pure returns (bytes6 _posKey) { + bytes memory _posKeyBytes = abi.encodePacked(tickLower, tickUpper); + assembly { + _posKey := mload(add(_posKeyBytes, 6)) + } + } + + function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata) external { + CallbackValidation.verifyCallback(factory, poolKey); + if (amount0Owed > 0) IERC20(poolKey.token0).transfer(msg.sender, amount0Owed); + if (amount1Owed > 0) IERC20(poolKey.token1).transfer(msg.sender, amount1Owed); + } + + function createPosition(int24 tickLower, int24 tickUpper, uint256 amount) internal { + uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper); + uint128 liquidity = LiquidityAmounts.getLiquidityForAmount1( + sqrtRatioAX96, sqrtRatioBX96, amount / 20 + ); + pool.mint(address(this), tickLower, tickUpper, liquidity, abi.encode(poolKey)); + // read position and start tracking in storage + bytes32 positionKey = PositionKey.compute(address(this), tickLower, tickUpper); + (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128,,) = pool.positions(positionKey); + discovery = TokenPosition({ + liquidity: liquidity, + tickLower: tickLower, + tickUpper: tickUpper, + feeGrowthInside0LastX128: feeGrowthInside0LastX128, + feeGrowthInside1LastX128: feeGrowthInside1LastX128 + }); + } + + + // called once at the beginning + function deployLiquidity(int24 startTick, uint256 amount) external { + require(floor.liquidity == 0, "already set up"); + require(anchor.liquidity == 0, "already set up"); + require(discovery.liquidity == 0, "already set up"); + harb.mint(amount); + int24 tickLower; + int24 tickUpper; + + // create floor + if (token0isWeth) { + tickLower = startTick; + tickUpper = startTick + 200; + createPosition(tickLower, tickUpper, amount / 10); + } else { + tickLower = startTick - 200; + tickUpper = startTick; + createPosition(tickLower, tickUpper, amount / 10); + } + + // create anchor + if (token0isWeth) { + tickLower += 201; + tickUpper += 601; + createPosition(tickLower, tickUpper, amount / 20); + } else { + tickLower -= 601; + tickUpper -= 201; + createPosition(tickLower, tickUpper, amount / 10); + } + + // create discovery + if (token0isWeth) { + tickLower += 601; + tickUpper += 11001; + createPosition(tickLower, tickUpper, harb.balanceOf(address(this))); + } else { + tickLower -= 11001; + tickUpper -= 601; + createPosition(tickLower, tickUpper, harb.balanceOf(address(this))); + } + } + + + function getInverseAmountsForLiquidity( + uint160 sqrtRatioX96, + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) internal pure returns (uint256 amount0, uint256 amount1) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + if (sqrtRatioX96 <= sqrtRatioAX96) { + amount1 = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); + } else if (sqrtRatioX96 < sqrtRatioBX96) { + amount1 = LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity); + amount0 = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity); + } else { + amount0 = LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); + } + } + + function calculateCapacity() public view returns (uint256 capacity) { + capacity = 0; + (, int24 currentTick, , , , , ) = pool.slot0(); + uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(currentTick); + // handle floor + { + uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(floor.tickLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(floor.tickUpper); + (uint256 amount0, uint256 amount1) = getInverseAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, floor.liquidity); + capacity += token0isWeth ? amount1 : amount0; + } + + // handle anchor + { + uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(anchor.tickLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(anchor.tickUpper); + (uint256 amount0, uint256 amount1) = getInverseAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, anchor.liquidity); + capacity += token0isWeth ? amount1 : amount0; + } + + // handle discovery + { + uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(discovery.tickLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(discovery.tickUpper); + (uint256 amount0, uint256 amount1) = getInverseAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, discovery.liquidity); + capacity += token0isWeth ? amount1 : amount0; + } + } + +} diff --git a/onchain/src/Harb.sol b/onchain/src/Harb.sol index 757e3dc..c3c4a05 100644 --- a/onchain/src/Harb.sol +++ b/onchain/src/Harb.sol @@ -6,7 +6,6 @@ import {ERC20, IERC20, IERC20Metadata} from "@openzeppelin/token/ERC20/ERC20.sol import {ERC20Permit, IERC20Permit} from "@openzeppelin/token/ERC20/extensions/ERC20Permit.sol"; import {SafeCast} from "@openzeppelin/utils/math/SafeCast.sol"; import {IStake} from "./interfaces/IStake.sol"; -import {LiquidityManager} from "./LiquidityManager.sol"; import {TwabController} from "pt-v5-twab-controller/TwabController.sol"; import {Math} from "@openzeppelin/utils/math/Math.sol"; import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol"; diff --git a/onchain/src/LiquidityManager.sol b/onchain/src/LiquidityManager.sol deleted file mode 100644 index f63366f..0000000 --- a/onchain/src/LiquidityManager.sol +++ /dev/null @@ -1,303 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.20; - -import "@uniswap-v3-periphery/libraries/PositionKey.sol"; -import "@uniswap-v3-core/libraries/FixedPoint128.sol"; -import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol"; -import "@aperture/uni-v3-lib/TickMath.sol"; -import "@aperture/uni-v3-lib/LiquidityAmounts.sol"; -import "@aperture/uni-v3-lib/PoolAddress.sol"; -import "@aperture/uni-v3-lib/CallbackValidation.sol"; -import "@openzeppelin/token/ERC20/IERC20.sol"; - -/** - * @title LiquidityManager - A contract that supports the harb ecosystem. It - * protects the communities liquidity while allowing a manager role to - * take strategic liqudity positions. - */ -contract LiquidityManager { - // default fee of 1% - uint24 constant FEE = uint24(10_000); - - // the address of the Uniswap V3 factory - address public immutable factory; - // the address of WETH9 - IWETH9 immutable weth; - - Harb immutable harb; - - IUniswapV3Pool public immutable pool; - - bool immutable token0isWeth; - - struct AddLiquidityParams { - address token0; - address token1; - int24 tickLower; - int24 tickUpper; - uint256 amount0Desired; - uint256 amount1Desired; - uint256 amount0Min; - uint256 amount1Min; - uint256 deadline; - } - - struct DecreaseLiquidityParams { - address token0; - address token1; - int24 tickLower; - int24 tickUpper; - uint128 liquidity; - uint256 amount0Min; - uint256 amount1Min; - uint256 deadline; - } - - struct TokenPosition { - // the liquidity of the position - uint128 liquidity; - uint128 ethOwed; - // the fee growth of the aggregate position as of the last action on the individual position - uint256 feeGrowthInside0LastX128; - uint256 feeGrowthInside1LastX128; - } - - /// @dev The token ID position data - mapping(bytes26 => TokenPosition) private _positions; - - modifier checkDeadline(uint256 deadline) { - require(block.timestamp <= deadline, "Transaction too old"); - _; - } - - /// @notice Emitted when liquidity is increased for a position - /// @param liquidity The amount by which liquidity for the NFT position was increased - /// @param amount0 The amount of token0 that was paid for the increase in liquidity - /// @param amount1 The amount of token1 that was paid for the increase in liquidity - event IncreaseLiquidity(address indexed token, uint128 liquidity, uint256 amount0, uint256 amount1); - /// @notice Emitted when liquidity is decreased for a position - /// @param liquidity The amount by which liquidity for the NFT position was decreased - /// @param amount0 The amount of token0 that was accounted for the decrease in liquidity - /// @param amount1 The amount of token1 that was accounted for the decrease in liquidity - event DecreaseLiquidity(address indexed token, uint128 liquidity, uint256 amount0, uint256 amount1); - - constructor(address _factory, address _WETH9, address _harb) { - factory = _factory; - weth = IWETH9(_WETH9); - PoolKey memory poolKey = PoolAddress.getPoolKey(_WETH9, _harb, FEE); - pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); - harb = Harb(_harb); - token0isWeth = _WETH9 < _harb; - } - - function updateFeesOwed(bool token0isEth, address token, uint128 tokensOwed0, uint128 tokensOwed1) internal { - (uint128 tokensOwed, uint128 ethOwed) = getFeesOwed(token); - tokensOwed += token0isEth ? tokensOwed1 : tokensOwed0; - ethOwed += token0isEth ? tokensOwed0 : tokensOwed1; - _feesOwed[token] = uint256(tokensOwed) << 128 + ethOwed; - } - - function posKey(int24 tickLower, int24 tickUpper) internal pure returns (bytes6 _posKey) { - bytes memory _posKeyBytes = abi.encodePacked(tickLower, tickUpper); - assembly { - _posKey := mload(add(_posKeyBytes, 6)) - } - } - - function positions(int24 tickLower, int24 tickUpper) - external - view - returns (uint128 liquidity, uint128 ethOwed, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128) - { - TokenPosition memory position = _positions[posKey(tickLower, tickUpper)]; - return (position.liquidity, position.ethOwed, position.feeGrowthInside0LastX128, position.feeGrowthInside1LastX128); - } - - function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata data) external { - PoolKey memory poolKey = abi.decode(data, (PoolKey)); - CallbackValidation.verifyCallback(factory, poolKey); - - if (amount0Owed > 0) IERC20(poolKey.token0).transfer(msg.sender, amount0Owed); - if (amount1Owed > 0) IERC20(poolKey.token1).transfer(msg.sender, amount1Owed); - } - - /// @notice Add liquidity to an initialized pool - function addLiquidity(AddLiquidityParams memory params) internal checkDeadline(params.deadline) { - - // compute the liquidity amount - uint128 liquidity; - { - (uint160 sqrtPriceX96,,,,,,) = pool.slot0(); - uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(params.tickLower); - uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(params.tickUpper); - - liquidity = LiquidityAmounts.getLiquidityForAmounts( - sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, params.amount0Desired, params.amount1Desired - ); - } - - - { - PoolKey memory poolKey = PoolAddress.getPoolKey(params.token0, params.token1, FEE); - (uint256 amount0, uint256 amount1) = - pool.mint(address(this), params.tickLower, params.tickUpper, liquidity, abi.encode(poolKey)); - - require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, "Price slippage check"); - emit IncreaseLiquidity(token, liquidity, amount0, amount1); - } - - bytes32 positionKey = PositionKey.compute(address(this), params.tickLower, params.tickUpper); - - // this is now updated to the current transaction - (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128,,) = pool.positions(positionKey); - - TokenPosition memory position = _positions[posKey(token, params.tickLower, params.tickUpper)]; - if (liquidity == 0) { - // create entry - position = TokenPosition({ - liquidity: liquidity, - feeGrowthInside0LastX128: feeGrowthInside0LastX128, - feeGrowthInside1LastX128: feeGrowthInside1LastX128 - }); - } else { - // update entry - updateFeesOwed( - token0isWeth, - token, - uint128( - FullMath.mulDiv( - feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128, - position.liquidity, - FixedPoint128.Q128 - ) - ), - uint128( - FullMath.mulDiv( - feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128, - position.liquidity, - FixedPoint128.Q128 - ) - ) - ); - - position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128; - position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128; - position.liquidity += liquidity; - } - } - - function liquidatePosition(int24 tickLower, int24 tickUpper, uint256 amount0Min, uint256 amount1Min) - internal - returns (uint256 ethReceived, uint256 liquidity) - { - // load position - TokenPosition storage position = _positions[posKey(tickLower, tickUpper)]; - - // burn and check slippage - uint256 liquidity = position.liquidity; - (uint256 amount0, uint256 amount1) = pool.burn(tickLower, tickUpper, liquidity); - require(amount0 >= amount0Min && amount1 >= amount1Min, "Price slippage check"); - harb.burn(token0isWeth ? amount1 : amount0); - - // calculate and transfer fees - bytes32 positionKey = PositionKey.compute(address(this), tickLower, tickUpper); - (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128,,) = pool.positions(positionKey); - uint256 ethOwed = position.ethOwed; - ethOwed += - FullMath.mulDiv( - (token0isWeth) ? feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128 : feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128, - liquidity, - FixedPoint128.Q128 - ); - weth.withdraw(ethOwed); - (bool sent, ) = feeRecipient.call{value: ethOwed}(""); - require(sent, "Failed to send Ether"); - - // event, cleanup and return - ethReceived = token0isWeth ? amount0 - ethOwed : amount1 - ethOwed; - emit PositionLiquidated(tickLower, tickUpper, liquidity, ethReceived); - delete position.liquidity; - delete position.ethOwed; - delete position.feeGrowthInside0LastX128; - delete position.feeGrowthInside1LastX128; - } - - // function compareTokenToEthBalance(uint256 ethAmountInPosition, uint256 tokenAmountInPosition) external view returns (bool hasMoreToken) { - // // Fetch the current sqrtPriceX96 from the pool - // (uint160 sqrtPriceX96,,,) = uniswapV3Pool.slot0(); - - // // Convert sqrtPriceX96 to a conventional price format - // // Note: The price is calculated as (sqrtPriceX96^2 / 2^192), simplified here as (price / 2^96) for the sake of example - // uint256 price = uint256(sqrtPriceX96) * uint256(sqrtPriceX96) / (1 << 96); - - // // Calculate the equivalent token amount for the ETH in the position at the current price - // // Assuming price is expressed as the amount of token per ETH - // uint256 equivalentTokenAmountForEth = ethAmountInPosition * price; - - // // Compare to the actual token amount in the position - // hasMoreToken = tokenAmountInPosition > equivalentTokenAmountForEth; - - // return hasMoreToken; - // } - - //////// - // - check if tick in range, otherwise revert - // - check if the position has more Token or more ETH, at current price - // - if more ETH, - // - calculate the amount of Token needed to be minted to bring the position to 50/50 - // - mint - // - deposit Token into pool - // - if more TOKEN - // - calculate the amount of token needed to be withdrawn from the position, to bring the position to 50/50 - // - withdraw - // - burn tokens - - - - function stretch(int24 tickLower, int24 tickUpper, uint256 deadline, uint256 amount0Min, uint256 amount1Min) external checkDeadline(deadline) { - - // Fetch the current tick from the Uniswap V3 pool - (, int24 currentTick, , , , , ) = pool.slot0(); - - // Check if current tick is within the specified range - int24 centerTick = tickLower + ((tickUpper - tickLower) / 2); - // TODO: add hysteresis - if (token0isWeth) { - require(currentTick > centerTick && currentTick <= tickUpper, "Current tick out of range for stretch"); - } else { - require(currentTick >= tickLower && currentTick < centerTick, "Current tick out of range for stretch"); - } - - (uint256 ethReceived, uint256 oldliquidity) = liquidatePosition(tickLower, tickUpper, amount0Min, amount1Min); - - uint256 liquidity; - if (token0isWeth) { - // extend the range up - uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower); - uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(currentTick + (currentTick - tickLower)); - liquidity = LiquidityAmounts.getLiquidityForAmount0( - sqrtRatioAX96, sqrtRatioBX96, ethReceived - ); - // calculate amount for new liquidity - uint256 newAmount1 = LiquidityAmounts.getAmount1ForLiquidity( - sqrtRatioAX96, sqrtRatioBX96, liquidity - ); - harb.mint(address(this), newAmount1); - // TODO: addLiquidity(...) - } else { - // extend the range down - uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickUpper); - uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(currentTick - (tickUpper - currentTick)); - liquidity = LiquidityAmounts.getLiquidityForAmount1( - sqrtRatioAX96, sqrtRatioBX96, ethReceived - ); - // calculate amount for new liquidity - uint256 newAmount0 = LiquidityAmounts.getAmount1ForLiquidity( - sqrtRatioAX96, sqrtRatioBX96, liquidity - ); - harb.mint(address(this), newAmount0); - // TODO: addLiquidity(...) - } - } - -} diff --git a/onchain/src/Stake.sol b/onchain/src/Stake.sol index 96eb908..8555891 100644 --- a/onchain/src/Stake.sol +++ b/onchain/src/Stake.sol @@ -28,6 +28,9 @@ contract Stake is IStake { error NoPermission(address requester, address owner); error PositionNotFound(); + event PositionCreated(uint256 indexed positionId, address indexed owner, uint256 share, uint32 creationTime, uint32 taxRate); + event PositionRemoved(uint256 indexed positionId, uint256 share, uint32 lastTaxTime); + struct StakingPosition { uint256 share; address owner; @@ -40,7 +43,7 @@ contract Stake is IStake { IERC20Metadata private immutable tokenContract; address private immutable taxPool; uint256 public outstandingStake; - uint256 private lastTokenId; + uint256 public nextPositionId; uint256 public minStake; mapping(uint256 => StakingPosition) public positions; @@ -78,7 +81,7 @@ contract Stake is IStake { */ function snatch(uint256 assets, address receiver, uint32 taxRate, uint256[] calldata positionsToSnatch) public - returns (uint256) + returns (uint256 positionId) { // check lower boundary uint256 sharesWanted = assetsToShares(assets, Math.Rounding.Down); @@ -100,7 +103,7 @@ contract Stake is IStake { // dissolve position // TODO: what if someone calls payTax and exitPosition in the same transaction? _payTax(pos, 0); - _exitPosition(pos); + _exitPosition(positionsToSnatch[i], pos); } // try to make a new position in the free space and hope it is big enough @@ -113,7 +116,8 @@ contract Stake is IStake { SafeERC20.safeTransferFrom(tokenContract, msg.sender, address(this), assets); // mint - StakingPosition storage sp = positions[lastTokenId++]; + positionId = nextPositionId++; + StakingPosition storage sp = positions[positionId]; sp.share = sharesWanted; sp.owner = receiver; sp.lastTaxTime = uint32(block.timestamp); @@ -121,18 +125,17 @@ contract Stake is IStake { sp.taxRate = taxRate; outstandingStake += sharesWanted; - - return lastTokenId; + emit PositionCreated(positionId, sp.owner, sp.share, sp.creationTime, sp.taxRate); } - function exitPosition(uint256 positionID) public { - StakingPosition storage pos = positions[positionID]; + function exitPosition(uint256 positionId) public { + StakingPosition storage pos = positions[positionId]; if (pos.owner != msg.sender) { revert NoPermission(msg.sender, pos.owner); } // to prevent snatch-and-exit grieving attack, pay TAX_FLOOR_DURATION _payTax(pos, TAX_FLOOR_DURATION); - _exitPosition(pos); + _exitPosition(positionId, pos); } function payTax(uint256 positionID) public { @@ -178,10 +181,11 @@ contract Stake is IStake { } } - function _exitPosition(StakingPosition storage pos) private { + function _exitPosition(uint256 positionId, StakingPosition storage pos) private { outstandingStake -= pos.share; address owner = pos.owner; uint256 assets = sharesToAssets(pos.share, Math.Rounding.Down); + emit PositionRemoved(positionId, pos.share, pos.lastTaxTime); delete pos.owner; delete pos.creationTime; SafeERC20.safeTransfer(tokenContract, owner, assets); diff --git a/onchain/test/interfaces/IWETH9.sol b/onchain/src/interfaces/IWETH9.sol similarity index 100% rename from onchain/test/interfaces/IWETH9.sol rename to onchain/src/interfaces/IWETH9.sol diff --git a/onchain/test/Harb.t.sol b/onchain/test/Harb.t.sol index 9b3097a..172db47 100644 --- a/onchain/test/Harb.t.sol +++ b/onchain/test/Harb.t.sol @@ -7,8 +7,9 @@ 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"; import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol"; -import "./interfaces/IWETH9.sol"; +import "../src/interfaces/IWETH9.sol"; import "../src/Harb.sol"; +import {BaseLineLP} from "../src/BaseLineLP.sol"; import {Stake, ExceededAvailableStake} from "../src/Stake.sol"; address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; @@ -23,10 +24,10 @@ contract HarbTest is Test { Harb harb; IUniswapV3Factory factory; Stake stake; - LiquidityManager liquidityManager; + BaseLineLP liquidityManager; function setUp() public { - mainnetFork = vm.createFork(vm.envString("ETH_NODE_URI_MAINNET"), 19433030); + mainnetFork = vm.createFork(vm.envString("ETH_NODE_URI_MAINNET"), 19461248); vm.selectFork(mainnetFork); weth = IWETH9(WETH); TwabController tc = new TwabController(60 * 60 * 24, uint32(block.timestamp)); @@ -35,7 +36,7 @@ contract HarbTest is Test { factory.createPool(address(weth), address(harb), FEE); stake = new Stake(address(harb)); harb.setStakingPool(address(stake)); - liquidityManager = new LiquidityManager(V3_FACTORY, WETH, address(harb)); + liquidityManager = new BaseLineLP(V3_FACTORY, WETH, address(harb)); harb.setLiquidityManager(address(liquidityManager)); } @@ -98,8 +99,9 @@ contract HarbTest is Test { // test stake when stake full { uint256[] memory empty; + vm.expectRevert(); + //vm.expectRevert(abi.encodeWithSelector(ExceededAvailableStake.selector, account, amount, 0)); vm.prank(account); - vm.expectRevert(ExceededAvailableStake.selector); stake.snatch(amount, account, 2, empty); }