Skip to content

Commit

Permalink
governance token mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
naman1402 committed Dec 6, 2024
1 parent 3a47d6e commit ffdc1ca
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 40 deletions.
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 11 additions & 3 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -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/
74 changes: 44 additions & 30 deletions src/JITHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -59,6 +59,7 @@ contract JITHook is BaseHook, Owned {
controller = IStrategiesController(_strategiesController);
swapThreshold = _threshold;
positionManager = IPositionManager(_posManager);
govToken = new GovToken("GovTkn", "GVT");
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -199,6 +202,7 @@ contract JITHook is BaseHook, Owned {
Currency currency,
uint256 amount
) external {

ERC20(Currency.unwrap(currency)).transferFrom(
msg.sender,
address(this),
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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(
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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) *
Expand All @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/governance/GovToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
83 changes: 81 additions & 2 deletions test/JITHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);

}

}
Empty file added testJITHook.t.sol
Empty file.

0 comments on commit ffdc1ca

Please sign in to comment.