From ffdc1cae59f9285086773558e9a7df6062102b2f Mon Sep 17 00:00:00 2001 From: naman mohnani Date: Fri, 6 Dec 2024 12:38:50 +0530 Subject: [PATCH] governance token mechanism --- .gitmodules | 4 +- remappings.txt | 14 +++++-- src/JITHook.sol | 74 +++++++++++++++++++-------------- src/governance/GovToken.sol | 6 +-- test/JITHook.t.sol | 83 ++++++++++++++++++++++++++++++++++++- testJITHook.t.sol | 0 6 files changed, 141 insertions(+), 40 deletions(-) create mode 100644 testJITHook.t.sol diff --git a/.gitmodules b/.gitmodules index 86b6c8a..77972af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,10 +3,10 @@ url = https://github.com/foundry-rs/forge-std [submodule "lib/v4-periphery"] path = lib/v4-periphery - url = https://github.com/Uniswap/v4-periphery + url = https://github.com/uniswap/v4-periphery [submodule "lib/v4-core"] path = lib/v4-core - url = https://github.com/Uniswap/v4-core + url = https://github.com/uniswap/v4-core [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/Openzeppelin/openzeppelin-contracts diff --git a/remappings.txt b/remappings.txt index c8d7bc8..0a32351 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,16 +1,24 @@ +@chainlink-testing/=lib/foundry-starter-kit/lib/foundry-chainlink-toolkit/lib/chainlink-testing-framework/contracts/ethereum/ +@chainlink/=lib/foundry-starter-kit/lib/chainlink-brownie-contracts/ +@clones/=lib/foundry-starter-kit/lib/clones-with-immutable-args/src/ @ensdomains/=lib/v4-core/node_modules/@ensdomains/ @openzeppelin/=lib/v4-core/lib/openzeppelin-contracts/ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ +@solmate/=lib/foundry-starter-kit/lib/solmate/src/ +@std/=lib/foundry-starter-kit/lib/forge-std/src/ @uniswap/v4-core/=lib/v4-periphery/lib/v4-core/ -ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/ +chainlink-brownie-contracts/=lib/foundry-starter-kit/lib/foundry-chainlink-toolkit/lib/chainlink-brownie-contracts/ +chainlink-testing-framework/=lib/foundry-starter-kit/lib/foundry-chainlink-toolkit/lib/chainlink-testing-framework/contracts/ +ds-test/=lib/foundry-starter-kit/lib/solmate/lib/ds-test/src/ erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/ forge-gas-snapshot/=lib/v4-periphery/lib/forge-gas-snapshot/src/ forge-std/=lib/forge-std/src/ +foundry-chainlink-toolkit/=lib/foundry-starter-kit/lib/foundry-chainlink-toolkit/ +foundry-starter-kit/=lib/foundry-starter-kit/ halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/ hardhat/=lib/v4-core/node_modules/hardhat/ openzeppelin-contracts/=lib/openzeppelin-contracts/ permit2/=lib/v4-periphery/lib/permit2/ solmate/=lib/v4-core/lib/solmate/ -v4-core/=lib/v4-periphery/lib/v4-core/ +v4-core/=lib/v4-core/src/ v4-periphery/=lib/v4-periphery/ -@chainlink/=lib/chainlink-brownie-contracts/ \ No newline at end of file diff --git a/src/JITHook.sol b/src/JITHook.sol index b2aa642..64a7eaa 100644 --- a/src/JITHook.sol +++ b/src/JITHook.sol @@ -48,7 +48,7 @@ contract JITHook is BaseHook, Owned { uint256 public currentActiveStrategyId; // mapping(PoolKey => GovToken) public govTokens; - mapping(PoolId => GovToken) public govTokens; + GovToken public govToken; constructor( IPoolManager _manager, @@ -59,6 +59,7 @@ contract JITHook is BaseHook, Owned { controller = IStrategiesController(_strategiesController); swapThreshold = _threshold; positionManager = IPositionManager(_posManager); + govToken = new GovToken("GovTkn", "GVT"); } /** @@ -100,30 +101,31 @@ contract JITHook is BaseHook, Owned { /** * @dev hook to initialize the governance token for a pool + * note not creating different tokens for each pool */ - function afterInitialize( - address, - PoolKey calldata key, - uint160, - int24 - ) external override returns (bytes4) { - string memory name = string.concat( - "Governance token: ", - ERC20(Currency.unwrap(key.currency0)).symbol(), - "-", - ERC20(Currency.unwrap(key.currency1)).symbol(), - Strings.toString(key.fee) - ); - string memory symbol = string.concat( - ERC20(Currency.unwrap(key.currency0)).symbol(), - "-", - ERC20(Currency.unwrap(key.currency1)).symbol(), - Strings.toString(key.fee) - ); - govTokens[key.toId()] = new GovToken(name, symbol); - - return this.afterInitialize.selector; - } + // function afterInitialize( + // address, + // PoolKey calldata key, + // uint160, + // int24 + // ) external override returns (bytes4) { + // string memory name = string.concat( + // "Governance token: ", + // ERC20(Currency.unwrap(key.currency0)).symbol(), + // "-", + // ERC20(Currency.unwrap(key.currency1)).symbol(), + // Strings.toString(key.fee) + // ); + // string memory symbol = string.concat( + // ERC20(Currency.unwrap(key.currency0)).symbol(), + // "-", + // ERC20(Currency.unwrap(key.currency1)).symbol(), + // Strings.toString(key.fee) + // ); + // govTokens[key.toId()] = new GovToken(name, symbol); + + // return this.afterInitialize.selector; + // } /** * @dev detect big swap and provide liquidity to the pool @@ -142,6 +144,7 @@ contract JITHook is BaseHook, Owned { ); } + // Converting swap amount into USD as well to compare if (_getSwapAmount(key, params) >= swapThreshold) { // withdraw funds from external swap before adding to the pool and // check if internal swap is required before adding liquidity @@ -199,6 +202,7 @@ contract JITHook is BaseHook, Owned { Currency currency, uint256 amount ) external { + ERC20(Currency.unwrap(currency)).transferFrom( msg.sender, address(this), @@ -207,8 +211,13 @@ contract JITHook is BaseHook, Owned { // TODO instead of minting amounts directly, calculate the total USD value and mint that if (amount > 0) { + (, int256 price, , ,) = priceFeeds[currency].latestRoundData(); + uint256 usdValue = (uint256(price) * amount) / 1e8; + + // deposit to strategy (currency => amount) _depositToStrategy(currentActiveStrategyId, currency, amount); - govTokens[key.toId()].mint(msg.sender, amount0); + // mint gove Token => usdValue + govToken.mint(msg.sender, usdValue); } // todo emit event @@ -218,11 +227,12 @@ contract JITHook is BaseHook, Owned { // note what if when the user withdraw pair of tokens, that ratio is changed // we accept deposit in proper ratio so we must withdraw in current ratio as well // INTERNAL SWAP + // key function withdraw(PoolKey calldata key) external { // TODO (later) amount specific withdrawl // TODO give tokens on basis of staked tokens ratio // Ex: staked $1000 USDC and $1000 ETH so give 50-50 - GovToken govToken = govTokens[key.toId()]; + // GovToken govToken = govTokens[key.toId()]; uint256 userBalance = govToken.balanceOf(msg.sender); uint256 totalSupply = govToken.totalSupply(); uint256 userShare = (userBalance * 1e8) / totalSupply; @@ -282,7 +292,7 @@ contract JITHook is BaseHook, Owned { function _getBalanceFromStrategy( uint256 _id, PoolKey calldata key - ) internal returns (uint256 balanceOfToken0, uint256 balanceOfToken1) { + ) internal view returns (uint256 balanceOfToken0, uint256 balanceOfToken1) { address token0 = Currency.unwrap(key.currency0); address token1 = Currency.unwrap(key.currency1); (balanceOfToken0, balanceOfToken1) = IStrategy( @@ -291,7 +301,7 @@ contract JITHook is BaseHook, Owned { } /** - * @dev mints a new LP position + * @dev mints a new LP position, used in beforeSwap hook */ function _addLiquidityToPool( PoolKey memory key, @@ -336,7 +346,7 @@ contract JITHook is BaseHook, Owned { } /** - * @dev burns this contract's LP position + * @dev burns this contract's LP position, used in afterSwap hook */ function _removeLiquidityFromPool( uint128 amount0Min, @@ -401,6 +411,9 @@ contract JITHook is BaseHook, Owned { uint256 token1Balance ) = _getBalanceFromStrategy(currentActiveStrategyId, key); + + // not withdrawing the entire balance, + // because in future we will allow user to withdraw some specific amount of funds [right now it is complete balance] _withdrawFromStrategy( currentActiveStrategyId, key.currency0, @@ -411,7 +424,7 @@ contract JITHook is BaseHook, Owned { key.currency1, token1Balance ); - + // current price ratio (, int24 tick, , ) = StateLibrary.getSlot0(poolManager, key.toId()); uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(tick); uint256 currentPoolPrice = (uint256(sqrtPriceX96) * @@ -423,6 +436,7 @@ contract JITHook is BaseHook, Owned { // (x - dx) / (y + dy) = pool ratio // TODO re-calcutate the amount to swap + // amount pair in this hook contract => in ratio wrt to the pool ratio /* uint256 amount0 = (token1Balance * 1e18) / currentPoolPrice; diff --git a/src/governance/GovToken.sol b/src/governance/GovToken.sol index cc49c3c..a247b1e 100644 --- a/src/governance/GovToken.sol +++ b/src/governance/GovToken.sol @@ -17,15 +17,15 @@ contract GovToken is ERC20, Owned { _burn(from, amount); } - function approve(address spender, uint256 amount) public override returns (bool) { + function approve(address spender, uint256 amount) public pure override returns (bool) { revert Not_Allowed(); } - function transferFrom(address from, address to, uint256 amount) public override returns (bool) { + function transferFrom(address from, address to, uint256 amount) public pure override returns (bool) { revert Not_Allowed(); } - function transfer(address to, uint256 amount) public override returns (bool) { + function transfer(address to, uint256 amount) public pure override returns (bool) { revert Not_Allowed(); } } diff --git a/test/JITHook.t.sol b/test/JITHook.t.sol index b177db4..6ce7d29 100644 --- a/test/JITHook.t.sol +++ b/test/JITHook.t.sol @@ -3,6 +3,85 @@ pragma solidity ^0.8.26; import {Test} from "forge-std/Test.sol"; -contract JITHookTest is Test { - function setUp() external {} +import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; +import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol"; + +import {PoolManager} from "v4-core/PoolManager.sol"; +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; + +import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; +import {Currency, CurrencyLibrary} from "v4-core/types/Currency.sol"; +import {StateLibrary} from "v4-core/libraries/StateLibrary.sol"; +import {PoolKey} from "v4-core/types/PoolKey.sol"; + +import {Hooks} from "v4-core/libraries/Hooks.sol"; +import {TickMath} from "v4-core/libraries/TickMath.sol"; + +import {JITHook} from "../src/JITHook.sol"; + +// add mock pricefeed contract + + +contract JITHookTest is Test, Deployers { + + using StateLibrary for IPoolManager; + using PoolIdLibrary for PoolKey; + using CurrencyLibrary for Currency; + + Currency public token0; + Currency public token1; + JITHook public hook; + // MockPriceFeed priceFeed0; + // MockPriceFeed priceFeed1; + + function setUp() public { + + deployFreshManagerAndRouters(); + + (token0, token1) = deployAndMint2Currencies(); + uint160 flags = uint160(Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG); + address hookAddress = address(flags); + deployCodeTo("JITHook.sol", abi.encode(), hookAddress); + hook = JITHook(hookAddress); + + MockERC20(Currency.unwrap(token0)).approve( + address(hook), + type(uint256).max + ); + MockERC20(Currency.unwrap(token1)).approve( + address(hook), + type(uint256).max + ); + + // priceFeed0 = new MockPriceFeed(); + // priceFeed1 = new MockPriceFeed(); + // hook.setPriceFeeds(token0, priceFeed0); + // hook.setPriceFeeds(token1, priceFeed1); + + // Initialize a pool with these two tokens + (key, ) = initPool(token0, token1, hook, 3000, SQRT_PRICE_1_1); + + } + + function test_deposit() public { + + uint256 depositAmount = 1e8; + uint256 initialUserBalance = MockERC20(Currency.unwrap(token0)).balanceOf(address(this)); + uint256 initialHookBalance = MockERC20(Currency.unwrap(token0)).balanceOf(address(hook)); + // deposit + hook.deposit(token0, depositAmount, key); + + // Get final balances + uint256 finalUserBalance = MockERC20(Currency.unwrap(token0)).balanceOf(address(this)); + uint256 finalHookBalance = MockERC20(Currency.unwrap(token0)).balanceOf(address(hook)); + + // Check token transfer + assertEq(finalUserBalance, initialUserBalance - depositAmount); + assertEq(finalHookBalance, initialHookBalance + depositAmount); + + uint256 expectedGovTokens; + assertEq(hook.govToken.balanceOf(address(this)), expectedGovTokens); + + } + } diff --git a/testJITHook.t.sol b/testJITHook.t.sol new file mode 100644 index 0000000..e69de29