From 5bfe1e982c9688c8b5648c73c0a526d840e89b9b Mon Sep 17 00:00:00 2001 From: scab24 Date: Mon, 16 Sep 2024 23:57:34 +0200 Subject: [PATCH] clean --- .gitmodules | 3 + lib/v1-core | 1 + send_logreturns.py | 138 ++--- src/Hook/univ4-risk-neutral-hook.sol | 684 +++++++++++++++++++++++ src/Math/implied volatility_backend.sol | 191 +++++++ src/Math/implied volatility_solidity.sol | 326 +++++++++++ src/hook_test.sol | 570 ------------------- src/math_backend.sol | 191 ------- src/math_solidity.sol | 326 ----------- src/templates/GasPriceFeesHook.sol | 135 ----- src/templates/template1.sol | 131 ----- src/templates/template2.sol | 242 -------- test/GasPriceFeesHook.t.sol | 169 ------ test/Impermanent.t.sol | 181 ------ test/univ4-risk-neutral-hook.t.sol | 68 +++ 15 files changed, 1342 insertions(+), 2014 deletions(-) create mode 160000 lib/v1-core create mode 100644 src/Hook/univ4-risk-neutral-hook.sol create mode 100644 src/Math/implied volatility_backend.sol create mode 100644 src/Math/implied volatility_solidity.sol delete mode 100644 src/hook_test.sol delete mode 100644 src/math_backend.sol delete mode 100644 src/math_solidity.sol delete mode 100644 src/templates/GasPriceFeesHook.sol delete mode 100644 src/templates/template1.sol delete mode 100644 src/templates/template2.sol delete mode 100644 test/GasPriceFeesHook.t.sol delete mode 100644 test/Impermanent.t.sol create mode 100644 test/univ4-risk-neutral-hook.t.sol diff --git a/.gitmodules b/.gitmodules index 0a36cc2..fa73ddf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "lib/abdk-libraries-solidity"] path = lib/abdk-libraries-solidity url = https://github.com/abdk-consulting/abdk-libraries-solidity +[submodule "lib/v1-core"] + path = lib/v1-core + url = https://github.com/gammaswap/v1-core diff --git a/lib/v1-core b/lib/v1-core new file mode 160000 index 0000000..f279205 --- /dev/null +++ b/lib/v1-core @@ -0,0 +1 @@ +Subproject commit f2792055cc84500d8db1b56c9eb3a54f048f317b diff --git a/send_logreturns.py b/send_logreturns.py index 9cf4ce6..5c8151e 100644 --- a/send_logreturns.py +++ b/send_logreturns.py @@ -1,69 +1,69 @@ -import json -from web3 import Web3 -import math -import os -from dotenv import load_dotenv - -# Cargar variables de entorno desde .env -load_dotenv() - -# Configurar conexión a Anvil (nodo local) -w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545")) - -# Verificar conexión -assert w3.isConnected(), "No se pudo conectar al nodo Ethereum" - -# Dirección del contrato desplegado (cambiando según tu despliegue) -contrato_direccion = "0xYourContractAddressHere" - -# Cargar ABI del contrato -with open("out/VolatilityCalculator.sol/VolatilityCalculator.json", "r") as f: - contrato_abi = json.load(f)["abi"] - -# Crear instancia del contrato -contrato = w3.eth.contract(address=contrato_direccion, abi=contrato_abi) - -# Dirección de la cuenta que enviará las transacciones (usar cuenta #0 de Anvil) -account = w3.eth.account.from_key(os.getenv("PRIVATE_KEY")) - -# Precios históricos -precios = [100, 105, 102, 108] - -def calculate_log_returns(prices): - log_returns = [] - for i in range(1, len(prices)): - log_return = math.log(prices[i] / prices[i - 1]) - log_returns.append(log_return) - return log_returns - -def to_64x64(value): - return int(value * (2**64)) - -if __name__ == "__main__": - # Calcular retornos logarítmicos - log_returns = calculate_log_returns(precios) - print("Retornos Logarítmicos:", log_returns) - - # Convertir a 64.64 fija - log_returns_64x64 = [to_64x64(r) for r in log_returns] - print("Retornos Logarítmicos (64.64 fija):", log_returns_64x64) - - # Preparar la transacción - # Para optimizar, se enviarán todos los retornos en una sola transacción - tx = contrato.functions.addLogReturns(log_returns_64x64).buildTransaction({ - 'from': account.address, - 'nonce': w3.eth.get_transaction_count(account.address), - 'gas': 500000, # Ajusta según sea necesario - 'gasPrice': w3.toWei('1', 'gwei') # Ajusta según sea necesario - }) - - # Firmar la transacción - signed_tx = account.sign_transaction(tx) - - # Enviar la transacción - tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) - print(f"Transacción enviada con hash: {tx_hash.hex()}") - - # Esperar a que la transacción sea minada - receipt = w3.eth.wait_for_transaction_receipt(tx_hash) - print(f"Transacción minada en bloque {receipt.blockNumber}") \ No newline at end of file +# import json +# from web3 import Web3 +# import math +# import os +# from dotenv import load_dotenv + +# # Load environment variables from .env +# load_dotenv() + +# # Configure connection to Anvil (local node) +# w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545")) + +# # Verify connection +# assert w3.isConnected(), "Failed to connect to Ethereum node" + +# # Deployed contract address (change according to your deployment) +# contract_address = "0x.." + +# # Load contract ABI +# with open("out/VolatilityCalculator.sol/VolatilityCalculator.json", "r") as f: +# contract_abi = json.load(f)["abi"] + +# # Create contract instance +# contract = w3.eth.contract(address=contract_address, abi=contract_abi) + +# # Address of the account that will send transactions (use Anvil's account #0) +# account = w3.eth.account.from_key(os.getenv("PRIVATE_KEY")) + +# # Historical prices +# prices = [100, 105, 102, 108] + +# def calculate_log_returns(prices): +# log_returns = [] +# for i in range(1, len(prices)): +# log_return = math.log(prices[i] / prices[i - 1]) +# log_returns.append(log_return) +# return log_returns + +# def to_64x64(value): +# return int(value * (2**64)) + +# if __name__ == "__main__": +# # Calculate logarithmic returns +# log_returns = calculate_log_returns(prices) +# print("Logarithmic Returns:", log_returns) + +# # Convert to 64.64 fixed point +# log_returns_64x64 = [to_64x64(r) for r in log_returns] +# print("Logarithmic Returns (64.64 fixed point):", log_returns_64x64) + +# # Prepare the transaction +# # To optimize, all returns will be sent in a single transaction +# tx = contract.functions.addLogReturns(log_returns_64x64).buildTransaction({ +# 'from': account.address, +# 'nonce': w3.eth.get_transaction_count(account.address), +# 'gas': 500000, # Adjust as necessary +# 'gasPrice': w3.toWei('1', 'gwei') # Adjust as necessary +# }) + +# # Sign the transaction +# signed_tx = account.sign_transaction(tx) + +# # Send the transaction +# tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) +# print(f"Transaction sent with hash: {tx_hash.hex()}") + +# # Wait for the transaction to be mined +# receipt = w3.eth.wait_for_transaction_receipt(tx_hash) +# print(f"Transaction mined in block {receipt.blockNumber}") \ No newline at end of file diff --git a/src/Hook/univ4-risk-neutral-hook.sol b/src/Hook/univ4-risk-neutral-hook.sol new file mode 100644 index 0000000..0733672 --- /dev/null +++ b/src/Hook/univ4-risk-neutral-hook.sol @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +// Necessary imports from Uniswap v4 +import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol"; +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; +import {Hooks} from "v4-core/libraries/Hooks.sol"; +import {PoolKey} from "v4-core/types/PoolKey.sol"; +import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; +import {LPFeeLibrary} from "v4-core/libraries/LPFeeLibrary.sol"; +import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/types/BeforeSwapDelta.sol"; + +// OpenZeppelin imports for access control +import "@openzeppelin/contracts/access/Ownable.sol"; + +// Chainlink imports for obtaining external data +import "lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; +import "abdk-libraries-solidity/ABDKMath64x64.sol"; +import {StateLibrary} from "v4-core/libraries/StateLibrary.sol"; +import {PoolIdLibrary} from "v4-core/types/PoolId.sol"; +import {VolatilityCalculator} from "../Math/implied volatility_solidity.sol"; + +/** + * @title univ4-risk-neutral-hook + * @notice Uniswap v4 Hook to dynamically adjust fees based on gas price and market data. + * Utilizes an Exponential Moving Average (EMA) for gas price and Chainlink oracles to obtain volatility, liquidity, and volume data. + */ +contract univ4riskneutralhook is BaseHook, Ownable { + using LPFeeLibrary for uint24; // Library to handle liquidity fees + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + using StateLibrary for IPoolManager; + using PoolIdLibrary for PoolKey; + + /** + * @notice Structure to store market data obtained from oracles + */ + // [@mevquant]: Does volatility make sense as uint256? Or is it overkill to be so large? + struct MarketData { + uint256 volatility; // Market volatility based on 10000 (e.g., 500 = 5%) + uint256 liquidity; // Total liquidity in the pool (tokens with 18 decimals) + uint256 volume; // Transaction volume in the period (tokens with 18 decimals) + uint256 lastUpdateTimestamp; // Timestamp of the last market data update + } + + /** + * @notice Structure to maintain the context of each individual swap + */ + struct SwapContext { + int256 initialAmountSpecified; // Initial amount specified for the swap + uint256 initialGasPrice; // Gas price at the start of the swap + uint24 fee; // Custom fee applied to the swap + uint256 timestamp; // Timestamp of the swap + uint256 volatility; // Market volatility at the start of the swap + uint256 liquidity; // Pool liquidity at the start of the swap + //@audit => add realized Volatility + } + + // Mapping of market data by pool address + mapping(address => MarketData) public marketDataByPool; + + // Mapping of swap contexts by swapId + mapping(bytes32 => SwapContext) private swapContexts; + + // Exponential Moving Average (EMA) of gas price + uint128 public movingAverageGasPrice; + + // Parameters for calculating EMA + // [@mevquant]: Add Smoothing and Precision to the name for clarity: + uint128 public emaSmoothingFactor = 100; // Smoothing factor (10%) + uint128 public emaPrecisionDivider = 1000; // Divider to maintain precision (100%) + + // Dynamic fee settings + uint24 public constant BASE_FEE = 3000; // Base fee of 0.3% + uint24 public constant MAX_FEE = 10000; // Maximum fee of 1% + uint24 public constant MIN_FEE = 500; // Minimum fee of 0.05% + + // Adjustable parameters by the owner + uint256 public gasPriceThreshold = 20; // Gas price difference threshold (%) + uint256 public maxSwapSize = 1_000_000e18; // Maximum swap size (1 million tokens) + uint256 public volatilityThreshold = 500; // Volatility threshold (5%) + uint256 public lowLiquidityThreshold = 100_000e18; // Low liquidity threshold (100,000 tokens) + uint256 public highVolumeThreshold = 300_000e18; // High volume threshold (300,000 tokens) + uint256 public lowVolumeThreshold = 100_000e18; // Low volume threshold (100,000 tokens) + + // Chainlink oracle interfaces for obtaining data + AggregatorV3Interface internal volatilityOracle; + AggregatorV3Interface internal realizedVolatility; //@audit new + AggregatorV3Interface internal priceFeed; //@audit new + AggregatorV3Interface internal liquidityOracle; + AggregatorV3Interface internal volumeOracle; + + VolatilityCalculator public volatilityCalculator; + + // Nonce to generate unique swapId and avoid collisions + uint256 private swapNonce; + + /** + * @notice Constructor that initializes the hook and sets the oracle addresses. + * @param _poolManager Address of the Uniswap v4 PoolManager. + * @param _volatilityOracle Address of the Chainlink oracle for volatility. + * @param _realizedVolatility Address of the Chainlink oracle for realized volatility. + * @param _priceFeed Address of the Chainlink price feed. + * @param _liquidityOracle Address of the Chainlink oracle for liquidity. + * @param _volumeOracle Address of the Chainlink oracle for volume. + */ + constructor( + IPoolManager _poolManager, + address _volatilityOracle, + address _realizedVolatility, + address _priceFeed, + address _liquidityOracle, + address _volumeOracle + ) BaseHook(_poolManager) Ownable(msg.sender){ + // Initialize Chainlink oracle interfaces + volatilityOracle = AggregatorV3Interface(_volatilityOracle); + realizedVolatility = AggregatorV3Interface(_realizedVolatility); //@audit new + priceFeed = AggregatorV3Interface(_priceFeed); //@audit new + liquidityOracle = AggregatorV3Interface(_liquidityOracle); + volumeOracle = AggregatorV3Interface(_volumeOracle); + volatilityCalculator = new VolatilityCalculator(); + + // Initialize the gas price moving average with the current gas price + uint128 initialGasPrice = uint128(tx.gasprice); + initializeEMA(initialGasPrice); + } + + /** + * @notice Initializes the Exponential Moving Average (EMA) with the first gas price. + * @param initialGasPrice Initial gas price. + */ + function initializeEMA(uint128 initialGasPrice) internal { + movingAverageGasPrice = initialGasPrice; + } + + /** + * @notice Defines which hooks are enabled in this contract. + * @return permissions Structure with hook permissions. + */ + function getHookPermissions() + public + pure + override + returns (Hooks.Permissions memory) + { + return + Hooks.Permissions({ + beforeInitialize: false, + afterInitialize: false, + beforeAddLiquidity: false, + beforeRemoveLiquidity: false, + afterAddLiquidity: false, + afterRemoveLiquidity: false, + beforeSwap: true, + afterSwap: true, + beforeDonate: false, + afterDonate: false, + beforeSwapReturnDelta: false, + afterSwapReturnDelta: false, + afterAddLiquidityReturnDelta: false, + afterRemoveLiquidityReturnDelta: false + }); + } + + + // function beforeInitialize( + // address, + // PoolKey calldata key, + // uint160, + // bytes calldata + // ) external pure override returns (bytes4) { + // // `.isDynamicFee()` function comes from using + // // the `SwapFeeLibrary` for `uint24` + // if (!key.fee.isDynamicFee()) revert MustUseDynamicFee(); + // return this.beforeInitialize.selector; + // } + + /** + * @notice Function executed before a swap. + * Calculates and adjusts the dynamic fee based on market data and gas price. + * @param sender Address calling the hook (PoolManager). + * @param key Key information of the pool. + * @param params Swap parameters. + * @param data Additional data (unused). + * @return selector Function selector. + * @return customDelta Custom delta to adjust the swap (no adjustments in this example). + * @return customFee Custom fee for the swap. + */ + function beforeSwap( + address sender, + PoolKey calldata key, + IPoolManager.SwapParams calldata params, + bytes calldata data + ) + external + override + /*onlyByPoolManager*/ + returns ( + bytes4 selector, + BeforeSwapDelta customDelta, + uint24 customFee + ) + { + //@audit => Research not found + //import {PoolKey} from "v4-core/types/PoolKey.sol"; + // address poolAddress = address(key.pool); + address poolAddress; + + // Validate that the specified amount is not zero + require(params.amountSpecified != 0, "Amount specified cannot be zero"); + + //@audit-ok TODO - => gamma + // Obtain liquidity from Uniswap v4 + uint256 liquidity = poolManager.getLiquidity(key.toId()); + + // Update market data using real oracles + updateMarketData(poolAddress); + + // Calculate custom fee based on market data and gas price + customFee = calculateCustomFee(poolAddress, params.amountSpecified); + + // Get the current fee of the pool to compare with the newly calculated fee + uint128 currentFee = uint128(tx.gasprice); + + // If the calculated fee is different from the current fee, update the fee in the PoolManager + + // [@mevquant]: Could this cause decimal issues? Maybe better to set a x% difference + // parameter uint24 _tolerance or something like 10000 + // e.g. [100%], 1000 [10%], 100 [1%], 10 [0.1%], 1 [0.01%] + // Q: Is the fee modified per swap or per pool? That is, do we change it for all swaps in that block? + if (customFee != currentFee) { + poolManager.updateDynamicLPFee(key, customFee); + // emit FeeAdjusted(poolAddress, currentFee, customFee); + } + + // Generate a unique swapId by combining the pool address and the swapNonce + // [@mevquant]: Maybe add some "salt" like block.number / timestamp? [future block same nonce] + bytes32 swapId = keccak256(abi.encodePacked(poolAddress, swapNonce)); + swapNonce += 1; // Increment the nonce for the next swap + + // Store the swap context for adjustments later in 'afterSwap' + swapContexts[swapId] = SwapContext({ + initialAmountSpecified: params.amountSpecified, + initialGasPrice: tx.gasprice, + fee: customFee, + timestamp: block.timestamp, + volatility: marketDataByPool[poolAddress].volatility, + liquidity: marketDataByPool[poolAddress].liquidity + }); + + // Create BeforeSwapDelta (no adjustment in this example) + customDelta = BeforeSwapDeltaLibrary.ZERO_DELTA; + + // Return the function selector, custom delta, and calculated fee + // Add Delta / Gamma + /** + * struct Greeks { + * int24 delta; + * int24 gamma; + * } + */ + // mapping (address => Greeks greeks) + // updateGreeks() + return (this.beforeSwap.selector, customDelta, customFee); + } + + /** + * @notice Function executed after a swap. + * Updates the moving average of the gas price and performs post-swap adjustments. + * @param sender Address calling the hook (PoolManager). + * @param key Key information of the pool. + * @param params Swap parameters. + * @param actualDelta Actual delta of the swap. + * @param data Additional data (unused). + * @return selector Function selector. + * @return adjustment Post-swap adjustment (unused in this example). + */ + // [@mevquant]: Maybe here it's a good idea to only save the 30d EMA of the IV + function afterSwap( + address sender, + PoolKey calldata key, + IPoolManager.SwapParams calldata params, + BalanceDelta actualDelta, + bytes calldata data + ) + external + override + /*onlyByPoolManager*/ + returns (bytes4 selector, int128 adjustment) + { + //@audit => Research not found + //import {PoolKey} from "v4-core/types/PoolKey.sol"; + // address poolAddress = address(key.pool); + address poolAddress; + + // Generate the corresponding swapId for the current swap using 'swapNonce - 1' + bytes32 swapId = keccak256(abi.encodePacked(poolAddress, swapNonce - 1)); + SwapContext memory context = swapContexts[swapId]; + + // Validate that the swap context exists + require(context.timestamp != 0, "Swap context not found"); + + // Calculate fee adjustment based on gas price difference, slippage, and changes in volatility and liquidity + adjustment = calculateAdjustment(context, actualDelta, poolAddress); + + // Delete the swap context to free storage and avoid future references + delete swapContexts[swapId]; + + // Emit post-swap adjustment event + // emit SwapAdjusted(poolAddress, adjustment); + + // Update the gas price moving average with the gas price of this swap + updateMovingAverage(); + + // Return the function selector and the calculated adjustment + return (this.afterSwap.selector, adjustment); + } + + /** + * @notice Calculates the custom fee based on multiple factors: volatility, volume, swap size, liquidity, and gas price. + * @param poolAddress Address of the pool. + * @param amountSpecified Specified amount of the swap. + * @return fee Custom fee based on 10_000 (e.g., 3000 = 30%) + */ + //[@mevquant] Q, wouldn't it be: 10_000 = 100%, 1,000 = 10%, 100 = 1%, 10 = 0.1%, 1 = 0.01%. + function calculateCustomFee(address poolAddress, int256 amountSpecified) + internal + view + returns (uint24 fee) + { + fee = BASE_FEE; + MarketData memory data = marketDataByPool[poolAddress]; + + // Adjust for volatility: increase the fee proportionally to volatility + //@audit => change + // fee = (fee * (10000 + data.volatility)) / 10000; + fee = uint24((uint256(fee) * (10000 + data.volatility)) / 10000); + + // Adjust for transaction volume + if (data.volume > highVolumeThreshold) { + fee = (fee * 90) / 100; // Reduce by 10% if volume is high + } else if (data.volume < lowVolumeThreshold) { + fee = (fee * 110) / 100; // Increase by 10% if volume is low + } + + // Adjust for swap size + if (isLargeSwap(amountSpecified)) { + fee = (fee * 120) / 100; // Increase by 20% for large swaps + } + + // Adjust for liquidity + if (data.liquidity < lowLiquidityThreshold) { + fee = (fee * 150) / 100; // Increase by 50% in low liquidity + } + + // Adjust for gas price using EMA + uint256 gasPriceDifference = calculateGasPriceDifference(); + if (gasPriceDifference > gasPriceThreshold) { + if (tx.gasprice > movingAverageGasPrice) { + fee = (fee * 80) / 100; // Reduce by 20% if gas is significantly higher + } else { + fee = (fee * 120) / 100; // Increase by 20% if gas is significantly lower + } + } + + // Ensure the final fee is within the established limits + fee = uint24(min(max(uint256(fee), MIN_FEE), MAX_FEE)); + + return fee; + } + + /** + * @notice Calculates the fee adjustment post-swap based on various factors: gas price difference, slippage, and changes in volatility and liquidity. + * @param context Swap context stored in 'swapContexts'. + * @param actualDelta Actual delta of the swap. + * @param poolAddress Address of the pool. + * @return adjustment Calculated adjustment (can be positive or negative). + */ + function calculateAdjustment( + SwapContext memory context, + BalanceDelta actualDelta, + address poolAddress + ) + internal + view + returns (int128 adjustment) + { + // Gas price difference between the current swap and the average + int256 gasPriceDifference = int256(tx.gasprice) - int256(context.initialGasPrice); + //@audit => change + // int128 baseAdjustment = int128((gasPriceDifference * int256(context.fee)) / 1e9); + int128 baseAdjustment = int128((gasPriceDifference * int256(uint256(context.fee))) / 1e9); + + // Slippage calculation: difference between the actual and specified amount + int256 actualAmount = int256(actualDelta.amount0()) + int256(actualDelta.amount1()); + int256 slippage = actualAmount - context.initialAmountSpecified; + int128 slippageAdjustment = int128(slippage / 1000); // 0.1% of the slippage + + // Adjustment for changes in volatility and liquidity since the start of the swap + MarketData memory currentData = marketDataByPool[poolAddress]; + int256 volatilityChange = int256(currentData.volatility) - int256(context.volatility); + int256 liquidityChange = int256(currentData.liquidity) - int256(context.liquidity); + + // Initialize additional adjustment + int128 marketConditionAdjustment = 0; + + // Adjust for volatility if there have been changes + if (volatilityChange != 0) { + // Adjust the fee proportionally to the volatility difference + //@audit => change + // marketConditionAdjustment += int128((volatilityChange * int256(context.fee)) / 10000); + marketConditionAdjustment += int128((volatilityChange * int256(uint256(context.fee))) / 10000); + } + + // Adjust for liquidity if there have been changes + if (liquidityChange != 0) { + // Adjust the fee proportionally to the liquidity difference (large scale to avoid overflows) + //@audit => change + // marketConditionAdjustment += int128((liquidityChange * int256(context.fee)) / 1e22); + marketConditionAdjustment += int128((liquidityChange * int256(uint256(context.fee))) / 1e22); + } + + // Sum all adjustments to get the total adjustment + adjustment = baseAdjustment + slippageAdjustment + marketConditionAdjustment; + } + + /** + * @notice Updates the Exponential Moving Average (EMA) of the gas price. + * Incorporates the current gas price with a smoothing factor. + */ + function updateMovingAverage() internal { + uint128 currentGasPrice = uint128(tx.gasprice); + if (movingAverageGasPrice == 0) { + // If EMA is not initialized, initialize it with the current gas price + initializeEMA(currentGasPrice); + } else { + // Calculate the new EMA with the current gas price + movingAverageGasPrice = + ((currentGasPrice * emaSmoothingFactor) + (movingAverageGasPrice * (emaPrecisionDivider - emaSmoothingFactor))) / + emaPrecisionDivider; + } + } + + /** + * @notice Calculates the percentage difference between the current gas price and the moving average. + * @return gasPriceDifference Percentage difference (%). + */ + function calculateGasPriceDifference() internal view returns (uint256 gasPriceDifference) { + // [@mevquant]: For example, we have tx.gasprice, would it make sense to look at the middle of the block? + uint128 gasPrice = uint128(tx.gasprice); + if (movingAverageGasPrice == 0) return 0; // Avoid division by zero + + uint256 difference; + if (gasPrice > movingAverageGasPrice) { + // Calculate percentage difference when gasPrice is greater than average + difference = ((gasPrice - movingAverageGasPrice) * 100) / movingAverageGasPrice; + } else { + // Calculate percentage difference when gasPrice is less than average + difference = ((movingAverageGasPrice - gasPrice) * 100) / movingAverageGasPrice; + } + + gasPriceDifference = difference; + } + + /** + * @notice Checks if the swap is considered large based on the specified size. + * @param amountSpecified Specified amount of the swap. + * @return bool Indicator if it's a large swap. + */ + function isLargeSwap(int256 amountSpecified) internal view returns (bool) { + // Consider a swap large if the specified amount is greater than half the maximum swap size + //@audit => change + // return abs(amountSpecified) > (int256(maxSwapSize) / 2); + return uint256(abs(amountSpecified)) > (maxSwapSize / 2); + } + + /** + * @notice Calculates the absolute value of an integer. + * @param x Integer. + * @return absValue Absolute value of x. + */ + function abs(int256 x) internal pure returns (uint256 absValue) { + return x >= 0 ? uint256(x) : uint256(-x); + } + + /** + * @notice Returns the minimum of two numbers. + * @param a First number. + * @param b Second number. + * @return minValue Minimum value between a and b. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256 minValue) { + return a < b ? a : b; + } + + /** + * @notice Returns the maximum of two numbers. + * @param a First number. + * @param b Second number. + * @return maxValue Maximum value between a and b. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256 maxValue) { + return a > b ? a : b; + } + + /** + * @notice Updates the market data using real Chainlink oracles. + * @param poolAddress Address of the pool. + */ + function updateMarketData(address poolAddress) internal { + // Obtain new market data from the oracles + MarketData memory newData = fetchMarketData(poolAddress); + + // Store the new market data in the mapping corresponding to the pool + marketDataByPool[poolAddress] = newData; + + // Emit the 'MarketDataUpdated' event with the new information + // emit MarketDataUpdated( + // poolAddress, + // newData.volatility, + // newData.liquidity, + // newData.volume, + // newData.lastUpdateTimestamp + // ); + } + + /** + * @notice Retrieves market data from Chainlink oracles. + * @param poolAddress Address of the pool. + * @return data Structure with the updated market data. + */ + function fetchMarketData(address poolAddress) internal view returns (MarketData memory data) { + + //@audit-ok => TODO in BREVIS ? + // Obtain volatility from Chainlink's volatility oracle + (, int256 volatilityPrice, , uint256 volatilityUpdatedAt, ) = volatilityOracle.latestRoundData(); + uint256 v_volatility = uint256(volatilityPrice); // Assuming the oracle returns volatility based on 10000 + + //@@audit-ok => OK + // Obtain realized volatility from Chainlink + // https://docs.chain.link/data-feeds/rates-feeds/addresses?network=ethereum&page=1 + (, int volatility,,,) = realizedVolatility.latestRoundData(); + int r_volatility = volatility; // Corrected: conversion not necessary + + //@@audit-ok => OK + // Obtain Price Feeds + // https://docs.chain.link/data-feeds/price-feeds/addresses?network=ethereum&page=1 + (, int price, , ,) = priceFeed.latestRoundData(); + // No need to convert price, already an int + + //@@audit-ok TODO - => gamma OK + // Obtain liquidity from Uniswap v4 ====>>> IN BEFORE_SWAP + // uint256 liquidity = poolManager.getLiquidity(key.toId()); + uint256 liquidity; + + //@audit => TODO => implied volatility => OK (Adjust) + // Convert values to 64.64 fixed format and adjust decimals as needed + int128 muPool = ABDKMath64x64.fromInt(price).div(ABDKMath64x64.fromUInt(1e8)); // if 'currentPrice' has 8 decimals + + uint256 t = 1; // Time in years + + // Parameters for iteration + uint256 maxIterations = 10; + int128 tolerance = ABDKMath64x64.div(ABDKMath64x64.fromUInt(1), ABDKMath64x64.fromUInt(1e6)); // Tolerance of 0.000001 + + // Iteratively calculate sigma and u + (int128 sigma, int128 u) = computeImpliedVolatilityAndDriftIterative(muPool, t, maxIterations, tolerance); + + + //@audit TODO => BREVIS + // Obtain volume + (, int256 volumePrice, , uint256 volumeUpdatedAt, ) = volumeOracle.latestRoundData(); + uint256 volume = uint256(volumePrice); + + // Create the 'MarketData' structure with the obtained values + //@audit => TODO add marketdata + data = MarketData({ + volatility: uint256(r_volatility), // Assuming MarketData.volatility is uint256 + liquidity: liquidity, + volume: volume, + lastUpdateTimestamp: block.timestamp + }); + } + + // IV + + // Function to calculate implied volatility and drift iteratively + function computeImpliedVolatilityAndDriftIterative(int128 muPool, uint256 t, uint256 maxIterations, int128 tolerance) internal view returns (int128 sigma, int128 u) { + // Initial estimate of u + u = muPool; + int128 previousU; + int128 difference; + + for (uint256 i = 0; i < maxIterations; i++) { + // Save the previous value of u + previousU = u; + + // Calculate sigma given u + sigma = computeSigma(muPool, u, t); + + // Calculate new u given sigma + u = volatilityCalculator.calculateDrift(muPool, sigma); + + // Calculate the absolute difference + difference = absv(u.sub(previousU)); + + // Check for convergence + if (difference < tolerance) { + break; // Converged + } + } + } + + // Function to calculate sigma given u + function computeSigma(int128 muPool, int128 u, uint256 t) internal view returns (int128 sigma) { + // Calculate u * t / 2 + int128 utOver2 = u.mul(ABDKMath64x64.fromUInt(t)).div(ABDKMath64x64.fromUInt(2)); + + // Calculate cosh(u * t / 2) using VolatilityCalculator + int128 coshUtOver2 = volatilityCalculator.cosh(utOver2); + + // Calculate ln(cosh(u * t / 2)) using VolatilityCalculator + int128 lnCoshUtOver2 = volatilityCalculator.approximateLn(coshUtOver2); + + // Calculate [mu_pool * t - ln(cosh(u * t / 2))] + int128 muPoolTimesT = muPool.mul(ABDKMath64x64.fromUInt(t)); + int128 innerExpression = muPoolTimesT.sub(lnCoshUtOver2); + + // Calculate 8 / t + int128 eightOverT = ABDKMath64x64.fromUInt(8).div(ABDKMath64x64.fromUInt(t)); + + // Calculate multiplicand + int128 multiplicand = eightOverT.mul(innerExpression); + + // Calculate sigma as the square root of the multiplicand + sigma = sqrt(multiplicand); + } + + // Function to calculate the square root + function sqrt(int128 x) internal view returns (int128) { + return volatilityCalculator.sqrt(x); + } + + // Function to calculate the absolute value + function absv(int128 x) internal pure returns (int128) { + return x >= 0 ? x : x.neg(); + } + + + /////////////////// + // Brevis + /////////////////// + + //volume => TODO + // function handleProofResult( + // bytes32 /*_requestId*/, + // bytes32 _vkHash, + // bytes calldata _circuitOutput + // ) internal override { + + // require(vkHash == _vkHash, "invalid vk"); + // (bytes32 _, uint64 sumVolume, uint64 minBlockNum, address addr) = decodeOutput(_circuitOutput); + // emit TradingVolumeAttested(addr, sumVolume, minBlockNum); + // } + + // // In guest circuit we have: + // // api.OutputBytes32(Salt) + // // api.OutputUint(248, sumVolume) + // // api.OutputUint(64, minBlockNum) + // // api.OutputAddress(c.UserAddr) + // function decodeOutput(bytes calldata o) internal pure returns (bytes32, uint256, uint64, address) { + // bytes32 salt = bytes32(o[0:32]); + + // uint256 sumVolume = uint256(bytes31(o[32:63])); + // uint64 minBlockNum = uint64(bytes8(o[63:71])); + // address addr = address(bytes20(o[71:91])); + // return (salt, sumVolume, minBlockNum, addr); + // } + + // function setVkHash(bytes32 _vkHash) external onlyOwner { + // vkHash = _vkHash; + // } + +} \ No newline at end of file diff --git a/src/Math/implied volatility_backend.sol b/src/Math/implied volatility_backend.sol new file mode 100644 index 0000000..20fc3a1 --- /dev/null +++ b/src/Math/implied volatility_backend.sol @@ -0,0 +1,191 @@ +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.19; + +// // Importa la biblioteca ABDKMath64x64 desde GitHub +// import "abdk-libraries-solidity/ABDKMath64x64.sol"; + +// /** +// * @title VolatilityCalculator +// * @notice Contrato para calcular la volatilidad implícita y drift basado en retornos logarítmicos. +// */ +// contract VolatilityCalculator { +// using ABDKMath64x64 for int128; +// using ABDKMath64x64 for uint256; + +// // Número de retornos logarítmicos ingresados +// uint256 public count; + +// // Media acumulada en formato 64.64 fija +// int128 public mean; + +// // Varianza acumulada (M2) en formato 64.64 fija +// int128 public M2; + +// // Máximo número de retornos permitidos +// uint256 public constant MAX_RETURNS = 1000; + +// // Evento emitido cuando se agrega un nuevo retorno +// event LogReturnAdded(int128 logReturn, uint256 newCount); + +// // Event emitido cuando se calcula la volatilidad y drift +// event VolatilityAndDriftCalculated(int128 sigma, int128 drift); + +// /** +// * @notice Calcula el coseno hiperbólico de x. +// * @param x Valor en formato 64.64 fija. +// * @return cosh_x Coseno hiperbólico de x en formato 64.64 fija. +// */ +// function cosh(int128 x) internal pure returns (int128) { +// // e^x +// int128 expx = ABDKMath64x64.exp(x); +// // e^-x +// int128 expNegx = ABDKMath64x64.exp(ABDKMath64x64.neg(x)); +// // (e^x + e^-x) / 2 +// return ABDKMath64x64.div(ABDKMath64x64.add(expx, expNegx), ABDKMath64x64.fromUInt(2)); +// } + +// /** +// * @notice Calcula el logaritmo natural de x. +// * @param x Valor en formato 64.64 fija. +// * @return ln_x Logaritmo natural de x en formato 64.64 fija. +// */ +// function naturalLog(int128 x) internal pure returns (int128) { +// return ABDKMath64x64.ln(x); +// } + +// /** +// * @notice Calcula la raíz cuadrada de x. +// * @param x Valor en formato 64.64 fija. +// * @return sqrt_x Raíz cuadrada de x en formato 64.64 fija. +// */ +// function sqrt(int128 x) internal pure returns (int128) { +// return ABDKMath64x64.sqrt(x); +// } + +// /** +// * @notice Agrega un nuevo retorno logarítmico y actualiza la media y varianza. +// * @param logReturn Retorno logarítmico en formato 64.64 fija. +// */ +// function addLogReturn(int128 logReturn) external /* onlyOwner */ { +// require(count < MAX_RETURNS, "Se ha alcanzado el maximo de retornos"); +// count += 1; + +// if (count == 1) { +// mean = logReturn; +// M2 = ABDKMath64x64.fromInt(0); // varianza no definida para 1 dato +// emit LogReturnAdded(logReturn, count); +// return; +// } + +// // Welford's Algorithm +// int128 delta = logReturn - mean; +// mean = mean + ABDKMath64x64.div(delta, ABDKMath64x64.fromUInt(count)); +// int128 delta2 = logReturn - mean; +// M2 = ABDKMath64x64.add(M2, ABDKMath64x64.mul(delta, delta2)); + +// emit LogReturnAdded(logReturn, count); +// } + +// /** +// * @notice Calcula la volatilidad implícita sigma y el drift u. +// * @param u Drift del activo subyacente (u) en formato 64.64 fija. +// * @return sigma Volatilidad implícita en formato 64.64 fija. +// * @return drift Drift calculado en formato 64.64 fija. +// */ +// function calculateSigmaAndDrift(int128 u) external /* onlyOwner */ returns (int128 sigma, int128 drift) { +// require(count >= 2, "Se requieren al menos 2 retornos para calcular varianza"); + +// // Calcular varianza: varianza = M2 / (n - 1) +// int128 variance = ABDKMath64x64.div(M2, ABDKMath64x64.fromUInt(count - 1)); + +// // Calcular la raíz cuadrada de la varianza (std dev) +// int128 stdDev = sqrt(variance); + +// // Calcular sigma = stdDev * sqrt(252) +// // sqrt(252) ≈ 15.8745 +// // En 64.64 fija, 15.8745 ≈ ABDKMath64x64.fromUInt(15) + 8745/10000 = 15.8745 +// int128 sqrt252 = ABDKMath64x64.add(ABDKMath64x64.fromUInt(15), ABDKMath64x64.divu(8745, 10000)); // Aproximación + +// sigma = ABDKMath64x64.mul(stdDev, sqrt252); + +// // Calcular drift u = muPool - (sigma^2 / 2) +// int128 sigmaSquared = ABDKMath64x64.mul(sigma, sigma); +// int128 sigmaSquaredOver2 = ABDKMath64x64.div(sigmaSquared, ABDKMath64x64.fromUInt(2)); +// drift = ABDKMath64x64.sub(mean, sigmaSquaredOver2); + +// emit VolatilityAndDriftCalculated(sigma, drift); +// } + +// /** +// * @notice Calcula el drift u usando la fórmula: +// * u = muPool - (sigma^2 / 2) +// * @param muPool Retorno medio en fees de la pool durante el tiempo t (μ_pool) en formato 64.64 fija. +// * @param sigma Volatilidad implícita σ en formato 64.64 fija. +// * @return u Drift calculado en formato 64.64 fija. +// */ +// function calculateDrift(int128 muPool, int128 sigma) public pure returns (int128 u) { +// // Calcular sigma^2 +// int128 sigmaSquared = ABDKMath64x64.mul(sigma, sigma); + +// // Calcular sigma^2 / 2 +// int128 sigmaSquaredOver2 = ABDKMath64x64.div(sigmaSquared, ABDKMath64x64.fromUInt(2)); + +// // Calcular u = muPool - (sigma^2 / 2) +// u = ABDKMath64x64.sub(muPool, sigmaSquaredOver2); +// } + +// /** +// * @notice Obtiene la media de los retornos logarítmicos. +// * @return mean_64x64 Media en formato 64.64 fija. +// */ +// function getMean() external view /* onlyOwner */ returns (int128) { +// return mean; +// } + +// /** +// * @notice Obtiene la varianza acumulada. +// * @return M2_64x64 Varianza acumulada en formato 64.64 fija. +// */ +// function getM2() external view /* onlyOwner */ returns (int128) { +// return M2; +// } + +// /** +// * @notice Calcula y devuelve la volatilidad implícita y drift sin almacenarlos. +// * @param muPool Retorno medio en fees de la pool durante el tiempo t (μ_pool) en formato 64.64 fija. +// * @param u Drift del activo subyacente (u) en formato 64.64 fija. +// * @param t Tiempo en años (t), asumimos t = 1. +// * @return sigma Volatilidad implícita en formato 64.64 fija. +// * @return drift Drift calculado en formato 64.64 fija. +// */ +// function computeImpliedVolatilityAndDrift(int128 muPool, int128 u, uint256 t) external pure returns (int128 sigma, int128 drift) { +// require(t > 0, "Tiempo t debe ser mayor que cero"); + +// // Calcular u * t / 2 +// int128 ut = ABDKMath64x64.mul(u, ABDKMath64x64.fromUInt(t)); +// int128 utOver2 = ABDKMath64x64.div(ut, ABDKMath64x64.fromUInt(2)); + +// // Calcular cosh(u * t / 2) +// int128 coshUtOver2 = cosh(utOver2); + +// // Calcular ln(cosh(u * t / 2)) +// int128 lnCoshUtOver2 = naturalLog(coshUtOver2); + +// // Calcular [mu_pool * t - ln(cosh(u * t / 2))] +// int128 innerExpression = ABDKMath64x64.sub(muPool, lnCoshUtOver2); + +// // Calcular 8 / t +// int128 eightOverT = ABDKMath64x64.div(ABDKMath64x64.fromUInt(8), ABDKMath64x64.fromUInt(t)); + +// // Multiplicar 8/t * [mu_pool * t - ln(cosh(u * t / 2))] +// int128 multiplicand = ABDKMath64x64.mul(eightOverT, innerExpression); + +// // Calcular la raíz cuadrada de multiplicand +// sigma = sqrt(multiplicand); + +// // Calcular drift u = muPool - (sigma^2 / 2) +// int128 sigmaSquared = ABDKMath64x64.mul(sigma, sigma); +// int128 sigmaSquaredOver2 = ABDKMath64x64.div(sigmaSquared, ABDKMath64x64.fromUInt(2)); +// drift = ABDKMath64x64.sub(muPool, sigmaSquaredOver2); +// } +// } \ No newline at end of file diff --git a/src/Math/implied volatility_solidity.sol b/src/Math/implied volatility_solidity.sol new file mode 100644 index 0000000..5411e1f --- /dev/null +++ b/src/Math/implied volatility_solidity.sol @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +// Import the ABDKMath64x64 library from GitHub +import "abdk-libraries-solidity/ABDKMath64x64.sol"; + +/** + * @title VolatilityCalculator + * @notice Contract to calculate implied volatility and drift based on logarithmic returns. + * Includes the calculation of logReturn using a Taylor series approximation. + */ +contract VolatilityCalculator { + using ABDKMath64x64 for int128; + using ABDKMath64x64 for uint256; + + // Number of logarithmic returns entered + uint256 public count; + + // Accumulated mean in 64.64 fixed format + int128 public mean; + + // Accumulated variance (M2) in 64.64 fixed format + int128 public M2; + + // Maximum number of allowed returns + uint256 public constant MAX_RETURNS = 1000; + + // Array to store asset prices + uint256[] public prices; + + // Array to store logarithmic returns in 64.64 fixed format + int128[] public logReturns; + + // Events to monitor actions + event PriceAdded(uint256 price, uint256 newCount); + event LogReturnAdded(int128 logReturn, uint256 newCount); + event VolatilityAndDriftCalculated(int128 sigma, int128 drift); + + /** + * @notice Calculates the hyperbolic cosine of x. + * @param x Value in 64.64 fixed format. + * @return cosh_x Hyperbolic cosine of x in 64.64 fixed format. + */ + function cosh(int128 x) public pure returns (int128) { + // e^x + int128 expx = ABDKMath64x64.exp(x); + // e^-x + int128 expNegx = ABDKMath64x64.exp(ABDKMath64x64.neg(x)); + // (e^x + e^-x) / 2 + return ABDKMath64x64.div(ABDKMath64x64.add(expx, expNegx), ABDKMath64x64.fromUInt(2)); + } + + /** + * @notice Calculates the natural logarithm of x using the ABDKMath64x64 library. + * @param x Value in 64.64 fixed format. + * @return ln_x Natural logarithm of x in 64.64 fixed format. + */ + function naturalLog(int128 x) internal pure returns (int128) { + return ABDKMath64x64.ln(x); + } + + /** + * @notice Calculates the square root of x using the ABDKMath64x64 library. + * @param x Value in 64.64 fixed format. + * @return sqrt_x Square root of x in 64.64 fixed format. + */ + function sqrt(int128 x) public pure returns (int128) { + return ABDKMath64x64.sqrt(x); + } + + /** + * @notice Calculates the natural logarithm of x using a Taylor series approximation. + * @param x Value for which ln(x) will be calculated in 64.64 fixed format. + * @return ln_x Approximated natural logarithm of x in 64.64 fixed format. + */ + function approximateLn(int128 x) public pure returns (int128 ln_x) { + require(x > 0, "x must be positive"); + + // Number of terms in the Taylor series + uint256 terms = 6; + + // Normalization: find k such that x = y * 2^k, where y is in [0.5, 1.5] + int256 k = 0; // Counter for the exponent of 2 + int128 y = x; + + // Limits for normalization + int128 onePointFive = ABDKMath64x64.divu(3, 2); // 1.5 in 64.64 fixed format + int128 zeroPointFive = ABDKMath64x64.divu(1, 2); // 0.5 in 64.64 fixed format + + // Adjust y and k so that y is in [0.5, 1.5] + while (y > onePointFive) { + y = y.div(ABDKMath64x64.fromUInt(2)); // Divide y by 2 + k += 1; + } + + while (y < zeroPointFive) { + y = y.mul(ABDKMath64x64.fromUInt(2)); // Multiply y by 2 + k -= 1; + } + + // Now, y is in [0.5, 1.5] + // We can write y = 1 + z, where z is in [-0.5, 0.5] + int128 one = ABDKMath64x64.fromUInt(1); + int128 z = y.sub(one); + + // Initialize ln_x with the first term of the Taylor series + ln_x = z; + + // Variables for the series expansion + int128 term = z; // Current term initialized to z^1 / 1 + int128 z_power = z; // z raised to the power n + int128 sign = ABDKMath64x64.fromInt(-1); // Alternating sign starts negative + + // Calculate the sum of the Taylor series + for (uint256 n = 2; n <= terms; n++) { + // Calculate z_power = z^n + z_power = z_power.mul(z); + + // term = z^n / n + term = z_power.div(ABDKMath64x64.fromUInt(n)); + + // Alternate the sign for each term + term = term.mul(sign); + + // Add the term to the result + ln_x = ln_x.add(term); + + // Change the sign for the next term + sign = sign.neg(); + } + + // Add ln(2^k) = k * ln(2) + // ln(2) ≈ 0.69314718056 in decimal + int128 LN2 = 0xB17217F7D1CF79AB; // ln(2) in 64.64 fixed format + int128 kLn2 = ABDKMath64x64.fromInt(k).mul(LN2); + + ln_x = ln_x.add(kLn2); + } + + /** + * @notice Adds a new price and calculates the logarithmic return relative to the previous price. + * @param newPrice Asset price in the new period (without decimals). + */ + function addPrice(uint256 newPrice) external /* onlyOwner */ { + require(prices.length - 1 < MAX_RETURNS, "Exceeds maximum returns"); + + // Add the new price to the array + prices.push(newPrice); + emit PriceAdded(newPrice, prices.length); + + // If it's the first price, there's no return to calculate + if (prices.length == 1) { + return; + } + + // Get the previous and current prices + uint256 prevPrice = prices[prices.length - 2]; + uint256 currentPrice = prices[prices.length - 1]; + + // Convert prices to 64.64 fixed format + int128 pi = ABDKMath64x64.fromUInt(currentPrice); + int128 pi_prev = ABDKMath64x64.fromUInt(prevPrice); + + // Calculate the ratio: Pi / P_{i-1} + int128 ratio = pi.div(pi_prev); + + // Calculate ln(ratio) using the Taylor series approximation + int128 logReturn = approximateLn(ratio); + + // Add the return to the statistical calculation + _addLogReturn_internal(logReturn); + } + + /** + * @notice Adds a new logarithmic return and updates the mean and variance. + * @param logReturn Logarithmic return in 64.64 fixed format. + */ + function addLogReturn(int128 logReturn) external /* onlyOwner */ { + require(count < MAX_RETURNS, "Maximum returns reached"); + _addLogReturn_internal(logReturn); + } + + /** + * @notice Internal function to add a logarithmic return and update statistics. + * @param logReturn Logarithmic return in 64.64 fixed format. + */ + function _addLogReturn_internal(int128 logReturn) internal { + logReturns.push(logReturn); + count += 1; + + if (count == 1) { + mean = logReturn; + M2 = ABDKMath64x64.fromInt(0); // Variance undefined for 1 data point + emit LogReturnAdded(logReturn, count); + return; + } + + // Welford's algorithm to update mean and M2 + int128 delta = logReturn.sub(mean); + mean = mean.add(delta.div(ABDKMath64x64.fromUInt(count))); + int128 delta2 = logReturn.sub(mean); + M2 = M2.add(delta.mul(delta2)); + + emit LogReturnAdded(logReturn, count); + } + + /** + * @notice Calculates the implied volatility sigma and drift u. + * @return sigma Implied volatility in 64.64 fixed format. + * @return drift Calculated drift in 64.64 fixed format. + */ + function calculateSigmaAndDrift() external /* onlyOwner */ returns (int128 sigma, int128 drift) { + require(count >= 2, "At least 2 returns required to calculate variance"); + + // Calculate variance: variance = M2 / (n - 1) + int128 variance = M2.div(ABDKMath64x64.fromUInt(count - 1)); + + // Calculate standard deviation (std dev) = sqrt(variance) + int128 stdDev = sqrt(variance); + + // Annualize the standard deviation: sigma = stdDev * sqrt(252) + // sqrt(252) ≈ 15.87401 + int128 sqrt252 = ABDKMath64x64.fromUInt(15).add(ABDKMath64x64.divu(87401, 100000)); // Approximation + + sigma = stdDev.mul(sqrt252); + + // Calculate drift u = mean - (sigma^2 / 2) + int128 sigmaSquared = sigma.mul(sigma); + int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2)); + drift = mean.sub(sigmaSquaredOver2); + + emit VolatilityAndDriftCalculated(sigma, drift); + } + + /** + * @notice Calculates the drift u using the formula: + * u = muPool - (sigma^2 / 2) + * @param muPool Mean return in pool fees over time t (μ_pool) in 64.64 fixed format. + * @param sigma Implied volatility σ in 64.64 fixed format. + * @return u Calculated drift in 64.64 fixed format. + */ + function calculateDrift(int128 muPool, int128 sigma) public pure returns (int128 u) { + // Calculate sigma^2 + int128 sigmaSquared = sigma.mul(sigma); + + // Calculate sigma^2 / 2 + int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2)); + + // Calculate u = muPool - (sigma^2 / 2) + u = muPool.sub(sigmaSquaredOver2); + } + + /** + * @notice Calculates and returns the implied volatility and drift without storing them. + * @param muPool Mean return in pool fees over time t (μ_pool) in 64.64 fixed format. + * @param u Drift of the underlying asset (u) in 64.64 fixed format. + * @param t Time in years (t), assuming t = 1. + * @return sigma Implied volatility in 64.64 fixed format. + * @return drift Calculated drift in 64.64 fixed format. + */ + function computeImpliedVolatilityAndDrift(int128 muPool, int128 u, uint256 t) external pure returns (int128 sigma, int128 drift) { + require(t > 0, "Time t must be greater than zero"); + + // Calculate u * t / 2 + int128 ut = u.mul(ABDKMath64x64.fromUInt(t)); + int128 utOver2 = ut.div(ABDKMath64x64.fromUInt(2)); + + // Calculate cosh(u * t / 2) + int128 coshUtOver2 = cosh(utOver2); + + // Calculate ln(cosh(u * t / 2)) using the logarithm approximation + int128 lnCoshUtOver2 = approximateLn(coshUtOver2); + + // Calculate [mu_pool * t - ln(cosh(u * t / 2))] + int128 muPoolTimesT = muPool.mul(ABDKMath64x64.fromUInt(t)); + int128 innerExpression = muPoolTimesT.sub(lnCoshUtOver2); + + // Calculate 8 / t + int128 eightOverT = ABDKMath64x64.fromUInt(8).div(ABDKMath64x64.fromUInt(t)); + + // Multiply 8/t * [mu_pool * t - ln(cosh(u * t / 2))] + int128 multiplicand = eightOverT.mul(innerExpression); + + // Calculate the square root of multiplicand + sigma = sqrt(multiplicand); + + // Calculate drift u = muPool - (sigma^2 / 2) + int128 sigmaSquared = sigma.mul(sigma); + int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2)); + drift = muPool.sub(sigmaSquaredOver2); + } + + /** + * @notice Retrieves the mean of the logarithmic returns. + * @return mean_64x64 Mean in 64.64 fixed format. + */ + function getMean() external view /* onlyOwner */ returns (int128) { + return mean; + } + + /** + * @notice Retrieves the accumulated variance (M2). + * @return M2_64x64 Accumulated variance in 64.64 fixed format. + */ + function getM2() external view /* onlyOwner */ returns (int128) { + return M2; + } + + /** + * @notice Retrieves a specific logarithmic return by its index. + * @param index Index of the logarithmic return (starting from 0). + * @return logReturn Logarithmic return in 64.64 fixed format. + */ + function getLogReturn(uint256 index) external view /* onlyOwner */ returns (int128 logReturn) { + require(index < logReturns.length, "Index out of range"); + return logReturns[index]; + } + + /** + * @notice Retrieves all logarithmic returns. + * @return allLogReturns Array of logarithmic returns in 64.64 fixed format. + */ + function getAllLogReturns() external view /* onlyOwner */ returns (int128[] memory allLogReturns) { + return logReturns; + } +} \ No newline at end of file diff --git a/src/hook_test.sol b/src/hook_test.sol deleted file mode 100644 index e5c813c..0000000 --- a/src/hook_test.sol +++ /dev/null @@ -1,570 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -// Importaciones necesarias de Uniswap v4 -import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol"; -import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; -import {Hooks} from "v4-core/libraries/Hooks.sol"; -import {PoolKey} from "v4-core/types/PoolKey.sol"; -import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; -import {LPFeeLibrary} from "v4-core/libraries/LPFeeLibrary.sol"; -import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/types/BeforeSwapDelta.sol"; - -// Importaciones de OpenZeppelin para control de acceso -import "@openzeppelin/contracts/access/Ownable.sol"; - -// Importaciones de Chainlink para obtener datos externos -import "lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; -import "abdk-libraries-solidity/ABDKMath64x64.sol"; -import {StateLibrary} from "v4-core/libraries/StateLibrary.sol"; -import {PoolIdLibrary} from "v4-core/types/PoolId.sol"; -/** - * @title GasPriceFeesHook - * @notice Hook de Uniswap v4 para ajustar dinámicamente las tarifas basadas en el precio del gas y datos de mercado. - * Utiliza un Promedio Móvil Exponencial (EMA) para el precio del gas y oráculos de Chainlink para obtener datos de volatilidad, liquidez y volumen. - */ -contract GasPriceFeesHook is BaseHook, Ownable { - using LPFeeLibrary for uint24; // Biblioteca para manejar tarifas de liquidez - using ABDKMath64x64 for int128; - using ABDKMath64x64 for uint256; - using StateLibrary for IPoolManager; - using PoolIdLibrary for PoolKey; - - /** - * @notice Estructura para almacenar datos de mercado obtenidos de oráculos - */ - // [@mevquant]: Tiene sentido volatility como uint256? O es overkill que sea tan grande? - struct MarketData { - uint256 volatility; // Volatilidad del mercado en base 10000 (e.g., 500 = 5%) - uint256 liquidity; // Liquidez total en el pool (tokens con 18 decimales) - uint256 volume; // Volumen de transacciones en el periodo (tokens con 18 decimales) - uint256 lastUpdateTimestamp; // Timestamp de la última actualización de datos de mercado - } - - /** - * @notice Estructura para mantener el contexto de cada swap individual - */ - struct SwapContext { - int256 initialAmountSpecified; // Monto inicial especificado para el swap - uint256 initialGasPrice; // Precio del gas al inicio del swap - uint24 fee; // Tarifa personalizada aplicada al swap - uint256 timestamp; // Timestamp del swap - uint256 volatility; // Volatilidad del mercado al inicio del swap - uint256 liquidity; // Liquidez del pool al inicio del swap - //@audit => add realiced Volatility - } - - // Mapeo de datos de mercado por dirección de pool - mapping(address => MarketData) public marketDataByPool; - - // Mapeo de contexto de swaps por swapId - mapping(bytes32 => SwapContext) private swapContexts; - - // Promedio Móvil Exponencial (EMA) del precio del gas - uint128 public movingAverageGasPrice; - - // Parámetros para calcular el EMA - // [@mevquant]: Añadir Smoothing y Precision al nombre por claridad: - uint128 public emaSmoothingFactor = 100; // Factor de suavizado (10%) - uint128 public emaPrecisionDivider = 1000; // Denominador para mantener la precisión (100%) - - // Configuración de tarifas dinámicas - uint24 public constant BASE_FEE = 3000; // Tarifa base de 0.3% - uint24 public constant MAX_FEE = 10000; // Tarifa máxima de 1% - uint24 public constant MIN_FEE = 500; // Tarifa mínima de 0.05% - - // Parámetros ajustables por el propietario (owner) - uint256 public gasPriceThreshold = 20; // Umbral de diferencia del precio del gas (%) - uint256 public maxSwapSize = 1_000_000e18; // Tamaño máximo del swap (1 millón de tokens) - uint256 public volatilityThreshold = 500; // Umbral de volatilidad (5%) - uint256 public lowLiquidityThreshold = 100_000e18; // Umbral de liquidez baja (100,000 tokens) - uint256 public highVolumeThreshold = 300_000e18; // Umbral de volumen alto (300,000 tokens) - uint256 public lowVolumeThreshold = 100_000e18; // Umbral de volumen bajo (100,000 tokens) - - // Interfaces de Chainlink para obtener datos de oráculos - AggregatorV3Interface internal volatilityOracle; - AggregatorV3Interface internal realizedVolatility; //@audit new - AggregatorV3Interface internal priceFeed; //@audit new - AggregatorV3Interface internal liquidityOracle; - AggregatorV3Interface internal volumeOracle; - - // Nonce para generar swapId único y evitar colisiones - uint256 private swapNonce; - - /** - * @notice Constructor que inicializa el hook y establece las direcciones de los oráculos. - * @param _poolManager Dirección del PoolManager de Uniswap v4. - * @param _volatilityOracle Dirección del oráculo de Chainlink para volatilidad. - * @param _liquidityOracle Dirección del oráculo de Chainlink para liquidez. - * @param _volumeOracle Dirección del oráculo de Chainlink para volumen. - */ - constructor( - IPoolManager _poolManager, - address _volatilityOracle, - address _realizedVolatility, - address _priceFeed, - address _liquidityOracle, - address _volumeOracle - ) BaseHook(_poolManager) Ownable(msg.sender){ - // Inicializar las interfaces de los oráculos de Chainlink - volatilityOracle = AggregatorV3Interface(_volatilityOracle); - realizedVolatility = AggregatorV3Interface(_realizedVolatility); //@audit new - priceFeed = AggregatorV3Interface(_priceFeed); //@audit new - liquidityOracle = AggregatorV3Interface(_liquidityOracle); - volumeOracle = AggregatorV3Interface(_volumeOracle); - - // Inicializar el promedio móvil del gas con el precio del gas actual - uint128 initialGasPrice = uint128(tx.gasprice); - initializeEMA(initialGasPrice); - } - - /** - * @notice Inicializa el Promedio Móvil Exponencial (EMA) con el primer precio del gas. - * @param initialGasPrice Precio del gas inicial. - */ - function initializeEMA(uint128 initialGasPrice) internal { - movingAverageGasPrice = initialGasPrice; - } - - /** - * @notice Define qué hooks están habilitados en este contrato. - * @return permissions Estructura con las permisos de los hooks. - */ - function getHookPermissions() - public - pure - override - returns (Hooks.Permissions memory) - { - return - Hooks.Permissions({ - beforeInitialize: false, - afterInitialize: false, - beforeAddLiquidity: false, - beforeRemoveLiquidity: false, - afterAddLiquidity: false, - afterRemoveLiquidity: false, - beforeSwap: true, - afterSwap: true, - beforeDonate: false, - afterDonate: false, - beforeSwapReturnDelta: false, - afterSwapReturnDelta: false, - afterAddLiquidityReturnDelta: false, - afterRemoveLiquidityReturnDelta: false - }); - } - - - // function beforeInitialize( - // address, - // PoolKey calldata key, - // uint160, - // bytes calldata - // ) external pure override returns (bytes4) { - // // `.isDynamicFee()` function comes from using - // // the `SwapFeeLibrary` for `uint24` - // if (!key.fee.isDynamicFee()) revert MustUseDynamicFee(); - // return this.beforeInitialize.selector; - // } - - /** - * @notice Función que se ejecuta antes de un swap. - * Calcula y ajusta la tarifa dinámica basada en datos de mercado y precio del gas. - * @param sender Dirección que llama al hook (PoolManager). - * @param key Información clave del pool. - * @param params Parámetros del swap. - * @param data Datos adicionales (no utilizados). - * @return selector Selector de la función. - * @return customDelta Delta personalizado para ajustar el swap (sin ajustes en este ejemplo). - * @return customFee Tarifa personalizada para el swap. - */ - function beforeSwap( - address sender, - PoolKey calldata key, - IPoolManager.SwapParams calldata params, - bytes calldata data - ) - external - override - /*onlyByPoolManager*/ - returns ( - bytes4 selector, - BeforeSwapDelta customDelta, - uint24 customFee - ) - { - //@audit => Research no found - //import {PoolKey} from "v4-core/types/PoolKey.sol"; - // address poolAddress = address(key.pool); - address poolAddress; - - // Validar que el monto especificado no sea cero - require(params.amountSpecified != 0, "Amount specified cannot be zero"); - - //@audit TODO - => gamma - // Obtener liquidez desde uniswapV4 - uint256 liquidity = poolManager.getLiquidity(key.toId()); - - // Actualizar datos de mercado usando oráculos reales - updateMarketData(poolAddress); - - // Calcular tarifa personalizada basada en los datos de mercado y precio del gas - customFee = calculateCustomFee(poolAddress, params.amountSpecified); - - // Obtener la tarifa actual del pool para compararla con la nueva tarifa calculada - //@audit => pending => (tx.gasprice) - // uint24 currentFee = poolManager.getFee(key); - uint128 currentFee = uint128(tx.gasprice); - - // Si la tarifa calculada es diferente a la actual, actualizar la tarifa en el PoolManager - - // [@mevquant]: Aqui se puede liar por decimales? Igual mejor poner un x% de diferencia - // parametro uint24 _tolerance o algo asi 10000 - // e.g. [100%], 1000 [10%], 100 [1%], 10 [0.1%], 1 [0.01%] - // Q: La fee cuando la modificamos es por swap o por pool? Es decir, la cambiamos para todos los swaps de ese bloque? - if (customFee != currentFee) { - poolManager.updateDynamicLPFee(key, customFee); - // emit FeeAdjusted(poolAddress, currentFee, customFee); - } - - // Generar un swapId único combinando la dirección del pool y el swapNonce - // [@mevquant]: Igual aqui añadir algo de "salt" como el block.number / timestamp ?????????? [future block same nonce] - bytes32 swapId = keccak256(abi.encodePacked(poolAddress, swapNonce)); - swapNonce += 1; // Incrementar el nonce para el siguiente swap - - // Almacenar el contexto del swap para ajustes posteriores en 'afterSwap' - swapContexts[swapId] = SwapContext({ - initialAmountSpecified: params.amountSpecified, - initialGasPrice: tx.gasprice, - fee: customFee, - timestamp: block.timestamp, - volatility: marketDataByPool[poolAddress].volatility, - liquidity: marketDataByPool[poolAddress].liquidity - }); - - // Crear BeforeSwapDelta (sin ajuste en este ejemplo) - customDelta = BeforeSwapDeltaLibrary.ZERO_DELTA; - - // Retornar el selector de la función, el delta personalizado y la tarifa calculada - // Add Delta / Gamma - /** - * struct Greeks { - * int24 delta; - * int24 gamma; - * } - */ - // mapping (address => Greeks greeks) - // updateGreeks() - return (this.beforeSwap.selector, customDelta, customFee); - } - -/** - * @notice Función que se ejecuta después de un swap. - * Actualiza el promedio móvil del precio del gas y realiza ajustes post-swap. - * @param sender Dirección que llama al hook (PoolManager). - * @param key Información clave del pool. - * @param params Parámetros del swap. - * @param actualDelta Delta real del swap. - * @param data Datos adicionales (no utilizados). - * @return selector Selector de la función. - * @return adjustment Ajuste post-swap (no utilizado en este ejemplo). - */ -// [@mevquant]: Igual aqui es buena idea solo guardar la 30d EMA de la IV - function afterSwap( - address sender, - PoolKey calldata key, - IPoolManager.SwapParams calldata params, - BalanceDelta actualDelta, - bytes calldata data - ) - external - override - /*onlyByPoolManager*/ - returns (bytes4 selector, int128 adjustment) - { - //@audit => Research no found - //import {PoolKey} from "v4-core/types/PoolKey.sol"; - // address poolAddress = address(key.pool); - address poolAddress; - - // Generar el swapId correspondiente al swap actual usando 'swapNonce - 1' - bytes32 swapId = keccak256(abi.encodePacked(poolAddress, swapNonce - 1)); - SwapContext memory context = swapContexts[swapId]; - - // Validar que el contexto del swap existe - require(context.timestamp != 0, "Swap context not found"); - - // Calcular ajuste del fee basado en la diferencia del precio del gas, slippage y cambios en volatilidad y liquidez - adjustment = calculateAdjustment(context, actualDelta, poolAddress); - - // Eliminar el contexto del swap para liberar almacenamiento y evitar referencias futuras - delete swapContexts[swapId]; - - // Emitir evento de ajuste post-swap - // emit SwapAdjusted(poolAddress, adjustment); - - // Actualizar el promedio móvil del gas price con el precio del gas de este swap - updateMovingAverage(); - - // Retornar el selector de la función y el ajuste calculado - return (this.afterSwap.selector, adjustment); - } - - /** - * @notice Calcula la tarifa personalizada basada en múltiples factores: volatilidad, volumen, tamaño del swap, liquidez y precio del gas. - * @param poolAddress Dirección del pool. - * @param amountSpecified Cantidad especificada del swap. - * @return fee Tarifa personalizada en base 10_000 (e.g., 3000 = 30%) - */ - //[@mevquant] Q, no sería: 10_000 = 100%, 1_000 = 10%, 100 = 1%, 10 = 0.1%, 1 = 0.01%. - function calculateCustomFee(address poolAddress, int256 amountSpecified) - internal - view - returns (uint24 fee) - { - fee = BASE_FEE; - MarketData memory data = marketDataByPool[poolAddress]; - - // Ajustar por volatilidad: incrementa la tarifa proporcionalmente a la volatilidad - //@audit => change - // fee = (fee * (10000 + data.volatility)) / 10000; - fee = uint24((uint256(fee) * (10000 + data.volatility)) / 10000); - - // Ajustar por volumen de transacciones - if (data.volume > highVolumeThreshold) { - fee = (fee * 90) / 100; // Reducir 10% si el volumen es alto - } else if (data.volume < lowVolumeThreshold) { - fee = (fee * 110) / 100; // Aumentar 10% si el volumen es bajo - } - - // Ajustar por tamaño del swap - if (isLargeSwap(amountSpecified)) { - fee = (fee * 120) / 100; // Aumentar un 20% para swaps grandes - } - - // Ajustar por liquidez - if (data.liquidity < lowLiquidityThreshold) { - fee = (fee * 150) / 100; // Aumentar un 50% en baja liquidez - } - - // Ajustar por precio del gas utilizando el EMA - uint256 gasPriceDifference = calculateGasPriceDifference(); - if (gasPriceDifference > gasPriceThreshold) { - if (tx.gasprice > movingAverageGasPrice) { - fee = (fee * 80) / 100; // Reducir un 20% si el gas es significativamente más alto - } else { - fee = (fee * 120) / 100; // Aumentar un 20% si el gas es significativamente más bajo - } - } - - // Asegurar que la tarifa final esté dentro de los límites establecidos - fee = uint24(min(max(uint256(fee), MIN_FEE), MAX_FEE)); - - return fee; - } - - /** - * @notice Calcula el ajuste sobre la fee post-swap basado en varios factores: diferencia del precio del gas, slippage y cambios en volatilidad y liquidez. - * @param context Contexto del swap almacenado en 'swapContexts'. - * @param actualDelta Delta real del swap. - * @param poolAddress Dirección del pool. - * @return adjustment Ajuste calculado (puede ser positivo o negativo). - */ - function calculateAdjustment( - SwapContext memory context, - BalanceDelta actualDelta, - address poolAddress - ) - internal - view - returns (int128 adjustment) - { - // Diferencia del precio del gas entre el swap actual y el promedio - int256 gasPriceDifference = int256(tx.gasprice) - int256(context.initialGasPrice); - //@audit => change - // int128 baseAdjustment = int128((gasPriceDifference * int256(context.fee)) / 1e9); - int128 baseAdjustment = int128((gasPriceDifference * int256(uint256(context.fee))) / 1e9); - - // Cálculo de slippage: diferencia entre el monto real y el especificado - int256 actualAmount = int256(actualDelta.amount0()) + int256(actualDelta.amount1()); - int256 slippage = actualAmount - context.initialAmountSpecified; - int128 slippageAdjustment = int128(slippage / 1000); // 0.1% del slippage - - // Ajuste por cambios en volatilidad y liquidez desde el inicio del swap - MarketData memory currentData = marketDataByPool[poolAddress]; - int256 volatilityChange = int256(currentData.volatility) - int256(context.volatility); - int256 liquidityChange = int256(currentData.liquidity) - int256(context.liquidity); - - // Inicializar ajuste adicional - int128 marketConditionAdjustment = 0; - - // Ajustar por volatilidad si ha habido cambios - if (volatilityChange != 0) { - // Ajustar la tarifa proporcionalmente a la diferencia de volatilidad - //@audit => change - // marketConditionAdjustment += int128((volatilityChange * int256(context.fee)) / 10000); - marketConditionAdjustment += int128((volatilityChange * int256(uint256(context.fee))) / 10000); - - } - - // Ajustar por liquidez si ha habido cambios - if (liquidityChange != 0) { - // Ajustar la tarifa proporcionalmente a la diferencia de liquidez (escala grande para evitar overflows) - //@audit => change - // marketConditionAdjustment += int128((liquidityChange * int256(context.fee)) / 1e22); - marketConditionAdjustment += int128((liquidityChange * int256(uint256(context.fee))) / 1e22); - - } - - // Sumar todos los ajustes para obtener el ajuste total - adjustment = baseAdjustment + slippageAdjustment + marketConditionAdjustment; - } - - /** - * @notice Actualiza el Promedio Móvil Exponencial (EMA) del precio del gas. - * Incorporando el precio del gas actual con un factor de suavizado. - */ - function updateMovingAverage() internal { - uint128 currentGasPrice = uint128(tx.gasprice); - if (movingAverageGasPrice == 0) { - // Si el EMA no está inicializado, inicializarlo con el precio del gas actual - initializeEMA(currentGasPrice); - } else { - // Calcular el nuevo EMA con el precio del gas actual - movingAverageGasPrice = - ((currentGasPrice * emaSmoothingFactor) + (movingAverageGasPrice * (emaPrecisionDivider - emaSmoothingFactor))) / - emaPrecisionDivider; - } - } - - /** - * @notice Calcula la diferencia porcentual entre el precio del gas actual y el promedio móvil. - * @return gasPriceDifference Diferencia porcentual (%). - */ - function calculateGasPriceDifference() internal view returns (uint256 gasPriceDifference) { - // [@mevquant]: Aqui por ejemplo tenemos tx.gasprice, tendria sentido mirar el medio del bloque? - uint128 gasPrice = uint128(tx.gasprice); - if (movingAverageGasPrice == 0) return 0; // Evitar división por cero - - uint256 difference; - if (gasPrice > movingAverageGasPrice) { - // Calcular diferencia porcentual cuando el gasPrice es mayor que el promedio - difference = ((gasPrice - movingAverageGasPrice) * 100) / movingAverageGasPrice; - } else { - // Calcular diferencia porcentual cuando el gasPrice es menor que el promedio - difference = ((movingAverageGasPrice - gasPrice) * 100) / movingAverageGasPrice; - } - - gasPriceDifference = difference; - } - - /** - * @notice Verifica si el swap es considerado grande basado en el tamaño especificado. - * @param amountSpecified Cantidad especificada del swap. - * @return bool Indicador de si es un swap grande. - */ - function isLargeSwap(int256 amountSpecified) internal view returns (bool) { - // Considera un swap grande si el monto especificado es mayor a la mitad del tamaño máximo del swap - //@audit => change - // return abs(amountSpecified) > (int256(maxSwapSize) / 2); - return uint256(abs(amountSpecified)) > (maxSwapSize / 2); - - } - - /** - * @notice Calcula el valor absoluto de un número entero. - * @param x Número entero. - * @return absValue Valor absoluto de x. - */ - function abs(int256 x) internal pure returns (uint256 absValue) { - return x >= 0 ? uint256(x) : uint256(-x); - } - - /** - * @notice Retorna el mínimo de dos números. - * @param a Primer número. - * @param b Segundo número. - * @return minValue Valor mínimo entre a y b. - */ - function min(uint256 a, uint256 b) internal pure returns (uint256 minValue) { - return a < b ? a : b; - } - - /** - * @notice Retorna el máximo de dos números. - * @param a Primer número. - * @param b Segundo número. - * @return maxValue Valor máximo entre a y b. - */ - function max(uint256 a, uint256 b) internal pure returns (uint256 maxValue) { - return a > b ? a : b; - } - - /** - * @notice Actualiza los datos de mercado usando oráculos reales de Chainlink. - * @param poolAddress Dirección del pool. - */ - function updateMarketData(address poolAddress) internal { - // Obtener los nuevos datos de mercado desde los oráculos - MarketData memory newData = fetchMarketData(poolAddress); - - // Almacenar los nuevos datos de mercado en el mapeo correspondiente al pool - marketDataByPool[poolAddress] = newData; - - // Emitir el evento 'MarketDataUpdated' con la nueva información - // emit MarketDataUpdated( - // poolAddress, - // newData.volatility, - // newData.liquidity, - // newData.volume, - // newData.lastUpdateTimestamp - // ); - } - - /** - * @notice Obtiene los datos de mercado desde los oráculos de Chainlink. - * @param poolAddress Dirección del pool. - * @return data Estructura con los datos de mercado actualizados. - */ -function fetchMarketData(address poolAddress) internal view returns (MarketData memory data) { - - //@audit => TODO in BREVIS ? - // Obtener volatilidad desde el oráculo de volatilidad de Chainlink - (, int256 volatilityPrice, , uint256 volatilityUpdatedAt, ) = volatilityOracle.latestRoundData(); - uint256 v_volatility = uint256(volatilityPrice); // Suponiendo que el oráculo retorna volatilidad en base 10000 - - //@audit => add - // Obtener volatilidad realizada desde chainlink - // https://docs.chain.link/data-feeds/rates-feeds/addresses?network=ethereum&page=1 - (, int volatility,,,) = realizedVolatility.latestRoundData(); - int r_volatility = volatility; // Corregido: no es necesaria la conversión - - //@audit => add - // Obtener Price Feeds - // https://docs.chain.link/data-feeds/price-feeds/addresses?network=ethereum&page=1 - (, int price, , ,) = priceFeed.latestRoundData(); - // No es necesario convertir price, ya es int - - //@audit TODO - => gamma - // Obtener liquidez desde uniswapV4 ====>>> IN BEFORESWAP - // uint256 liquidity = poolManager.getLiquidity(key.toId()); - uint256 liquidity; - - //@audit TODO => - // Obtener volumen desde el oráculo de volumen de Chainlink - (, int256 volumePrice, , uint256 volumeUpdatedAt, ) = volumeOracle.latestRoundData(); - uint256 volume = uint256(volumePrice); // Supuesto: volumen en tokens (con decimal 18) - - // Crear la estructura 'MarketData' con los valores obtenidos - //@audit => TODO add marketdata - data = MarketData({ - volatility: uint256(r_volatility), // Asumiendo que MarketData.volatility es uint256 - liquidity: liquidity, - volume: volume, - lastUpdateTimestamp: block.timestamp - }); -} - - -} \ No newline at end of file diff --git a/src/math_backend.sol b/src/math_backend.sol deleted file mode 100644 index cbd20fc..0000000 --- a/src/math_backend.sol +++ /dev/null @@ -1,191 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -// Importa la biblioteca ABDKMath64x64 desde GitHub -import "abdk-libraries-solidity/ABDKMath64x64.sol"; - -/** - * @title VolatilityCalculator - * @notice Contrato para calcular la volatilidad implícita y drift basado en retornos logarítmicos. - */ -contract VolatilityCalculator { - using ABDKMath64x64 for int128; - using ABDKMath64x64 for uint256; - - // Número de retornos logarítmicos ingresados - uint256 public count; - - // Media acumulada en formato 64.64 fija - int128 public mean; - - // Varianza acumulada (M2) en formato 64.64 fija - int128 public M2; - - // Máximo número de retornos permitidos - uint256 public constant MAX_RETURNS = 1000; - - // Evento emitido cuando se agrega un nuevo retorno - event LogReturnAdded(int128 logReturn, uint256 newCount); - - // Event emitido cuando se calcula la volatilidad y drift - event VolatilityAndDriftCalculated(int128 sigma, int128 drift); - - /** - * @notice Calcula el coseno hiperbólico de x. - * @param x Valor en formato 64.64 fija. - * @return cosh_x Coseno hiperbólico de x en formato 64.64 fija. - */ - function cosh(int128 x) internal pure returns (int128) { - // e^x - int128 expx = ABDKMath64x64.exp(x); - // e^-x - int128 expNegx = ABDKMath64x64.exp(ABDKMath64x64.neg(x)); - // (e^x + e^-x) / 2 - return ABDKMath64x64.div(ABDKMath64x64.add(expx, expNegx), ABDKMath64x64.fromUInt(2)); - } - - /** - * @notice Calcula el logaritmo natural de x. - * @param x Valor en formato 64.64 fija. - * @return ln_x Logaritmo natural de x en formato 64.64 fija. - */ - function naturalLog(int128 x) internal pure returns (int128) { - return ABDKMath64x64.ln(x); - } - - /** - * @notice Calcula la raíz cuadrada de x. - * @param x Valor en formato 64.64 fija. - * @return sqrt_x Raíz cuadrada de x en formato 64.64 fija. - */ - function sqrt(int128 x) internal pure returns (int128) { - return ABDKMath64x64.sqrt(x); - } - - /** - * @notice Agrega un nuevo retorno logarítmico y actualiza la media y varianza. - * @param logReturn Retorno logarítmico en formato 64.64 fija. - */ - function addLogReturn(int128 logReturn) external /* onlyOwner */ { - require(count < MAX_RETURNS, "Se ha alcanzado el maximo de retornos"); - count += 1; - - if (count == 1) { - mean = logReturn; - M2 = ABDKMath64x64.fromInt(0); // varianza no definida para 1 dato - emit LogReturnAdded(logReturn, count); - return; - } - - // Welford's Algorithm - int128 delta = logReturn - mean; - mean = mean + ABDKMath64x64.div(delta, ABDKMath64x64.fromUInt(count)); - int128 delta2 = logReturn - mean; - M2 = ABDKMath64x64.add(M2, ABDKMath64x64.mul(delta, delta2)); - - emit LogReturnAdded(logReturn, count); - } - - /** - * @notice Calcula la volatilidad implícita sigma y el drift u. - * @param u Drift del activo subyacente (u) en formato 64.64 fija. - * @return sigma Volatilidad implícita en formato 64.64 fija. - * @return drift Drift calculado en formato 64.64 fija. - */ - function calculateSigmaAndDrift(int128 u) external /* onlyOwner */ returns (int128 sigma, int128 drift) { - require(count >= 2, "Se requieren al menos 2 retornos para calcular varianza"); - - // Calcular varianza: varianza = M2 / (n - 1) - int128 variance = ABDKMath64x64.div(M2, ABDKMath64x64.fromUInt(count - 1)); - - // Calcular la raíz cuadrada de la varianza (std dev) - int128 stdDev = sqrt(variance); - - // Calcular sigma = stdDev * sqrt(252) - // sqrt(252) ≈ 15.8745 - // En 64.64 fija, 15.8745 ≈ ABDKMath64x64.fromUInt(15) + 8745/10000 = 15.8745 - int128 sqrt252 = ABDKMath64x64.add(ABDKMath64x64.fromUInt(15), ABDKMath64x64.divu(8745, 10000)); // Aproximación - - sigma = ABDKMath64x64.mul(stdDev, sqrt252); - - // Calcular drift u = muPool - (sigma^2 / 2) - int128 sigmaSquared = ABDKMath64x64.mul(sigma, sigma); - int128 sigmaSquaredOver2 = ABDKMath64x64.div(sigmaSquared, ABDKMath64x64.fromUInt(2)); - drift = ABDKMath64x64.sub(mean, sigmaSquaredOver2); - - emit VolatilityAndDriftCalculated(sigma, drift); - } - - /** - * @notice Calcula el drift u usando la fórmula: - * u = muPool - (sigma^2 / 2) - * @param muPool Retorno medio en fees de la pool durante el tiempo t (μ_pool) en formato 64.64 fija. - * @param sigma Volatilidad implícita σ en formato 64.64 fija. - * @return u Drift calculado en formato 64.64 fija. - */ - function calculateDrift(int128 muPool, int128 sigma) public pure returns (int128 u) { - // Calcular sigma^2 - int128 sigmaSquared = ABDKMath64x64.mul(sigma, sigma); - - // Calcular sigma^2 / 2 - int128 sigmaSquaredOver2 = ABDKMath64x64.div(sigmaSquared, ABDKMath64x64.fromUInt(2)); - - // Calcular u = muPool - (sigma^2 / 2) - u = ABDKMath64x64.sub(muPool, sigmaSquaredOver2); - } - - /** - * @notice Obtiene la media de los retornos logarítmicos. - * @return mean_64x64 Media en formato 64.64 fija. - */ - function getMean() external view /* onlyOwner */ returns (int128) { - return mean; - } - - /** - * @notice Obtiene la varianza acumulada. - * @return M2_64x64 Varianza acumulada en formato 64.64 fija. - */ - function getM2() external view /* onlyOwner */ returns (int128) { - return M2; - } - - /** - * @notice Calcula y devuelve la volatilidad implícita y drift sin almacenarlos. - * @param muPool Retorno medio en fees de la pool durante el tiempo t (μ_pool) en formato 64.64 fija. - * @param u Drift del activo subyacente (u) en formato 64.64 fija. - * @param t Tiempo en años (t), asumimos t = 1. - * @return sigma Volatilidad implícita en formato 64.64 fija. - * @return drift Drift calculado en formato 64.64 fija. - */ - function computeImpliedVolatilityAndDrift(int128 muPool, int128 u, uint256 t) external pure returns (int128 sigma, int128 drift) { - require(t > 0, "Tiempo t debe ser mayor que cero"); - - // Calcular u * t / 2 - int128 ut = ABDKMath64x64.mul(u, ABDKMath64x64.fromUInt(t)); - int128 utOver2 = ABDKMath64x64.div(ut, ABDKMath64x64.fromUInt(2)); - - // Calcular cosh(u * t / 2) - int128 coshUtOver2 = cosh(utOver2); - - // Calcular ln(cosh(u * t / 2)) - int128 lnCoshUtOver2 = naturalLog(coshUtOver2); - - // Calcular [mu_pool * t - ln(cosh(u * t / 2))] - int128 innerExpression = ABDKMath64x64.sub(muPool, lnCoshUtOver2); - - // Calcular 8 / t - int128 eightOverT = ABDKMath64x64.div(ABDKMath64x64.fromUInt(8), ABDKMath64x64.fromUInt(t)); - - // Multiplicar 8/t * [mu_pool * t - ln(cosh(u * t / 2))] - int128 multiplicand = ABDKMath64x64.mul(eightOverT, innerExpression); - - // Calcular la raíz cuadrada de multiplicand - sigma = sqrt(multiplicand); - - // Calcular drift u = muPool - (sigma^2 / 2) - int128 sigmaSquared = ABDKMath64x64.mul(sigma, sigma); - int128 sigmaSquaredOver2 = ABDKMath64x64.div(sigmaSquared, ABDKMath64x64.fromUInt(2)); - drift = ABDKMath64x64.sub(muPool, sigmaSquaredOver2); - } -} \ No newline at end of file diff --git a/src/math_solidity.sol b/src/math_solidity.sol deleted file mode 100644 index 02d3318..0000000 --- a/src/math_solidity.sol +++ /dev/null @@ -1,326 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -// Importa la biblioteca ABDKMath64x64 desde GitHub -import "abdk-libraries-solidity/ABDKMath64x64.sol"; - -/** - * @title VolatilityCalculator - * @notice Contrato para calcular la volatilidad implícita y drift basado en retornos logarítmicos. - * Incluye el cálculo de logReturn utilizando una aproximación de la serie de Taylor. - */ -contract VolatilityCalculator { - using ABDKMath64x64 for int128; - using ABDKMath64x64 for uint256; - - // Número de retornos logarítmicos ingresados - uint256 public count; - - // Media acumulada en formato 64.64 fija - int128 public mean; - - // Varianza acumulada (M2) en formato 64.64 fija - int128 public M2; - - // Máximo número de retornos permitidos - uint256 public constant MAX_RETURNS = 1000; - - // Array para almacenar precios del activo - uint256[] public prices; - - // Array para almacenar retornos logarítmicos en formato 64.64 fija - int128[] public logReturns; - - // Eventos para monitorear acciones - event PriceAdded(uint256 price, uint256 newCount); - event LogReturnAdded(int128 logReturn, uint256 newCount); - event VolatilityAndDriftCalculated(int128 sigma, int128 drift); - - /** - * @notice Calcula el coseno hiperbólico de x. - * @param x Valor en formato 64.64 fija. - * @return cosh_x Coseno hiperbólico de x en formato 64.64 fija. - */ - function cosh(int128 x) internal pure returns (int128) { - // e^x - int128 expx = ABDKMath64x64.exp(x); - // e^-x - int128 expNegx = ABDKMath64x64.exp(ABDKMath64x64.neg(x)); - // (e^x + e^-x) / 2 - return ABDKMath64x64.div(ABDKMath64x64.add(expx, expNegx), ABDKMath64x64.fromUInt(2)); - } - - /** - * @notice Calcula el logaritmo natural de x utilizando la biblioteca ABDKMath64x64. - * @param x Valor en formato 64.64 fija. - * @return ln_x Logaritmo natural de x en formato 64.64 fija. - */ - function naturalLog(int128 x) internal pure returns (int128) { - return ABDKMath64x64.ln(x); - } - - /** - * @notice Calcula la raíz cuadrada de x utilizando la biblioteca ABDKMath64x64. - * @param x Valor en formato 64.64 fija. - * @return sqrt_x Raíz cuadrada de x en formato 64.64 fija. - */ - function sqrt(int128 x) internal pure returns (int128) { - return ABDKMath64x64.sqrt(x); - } - - /** - * @notice Calcula el logaritmo natural de x utilizando una aproximación de la serie de Taylor. - * @param x Valor para el cual se calculará ln(x) en formato 64.64 fija. - * @return ln_x Logaritmo natural aproximado de x en formato 64.64 fija. - */ - function approximateLn(int128 x) internal pure returns (int128 ln_x) { - require(x > 0, "x debe ser positivo"); - - // Número de términos de la serie de Taylor - uint256 terms = 6; - - // Normalización: encontrar k tal que x = y * 2^k, donde y está en [0.5, 1.5] - int256 k = 0; // Contador para el exponente de 2 - int128 y = x; - - // Límites para la normalización - int128 onePointFive = ABDKMath64x64.divu(3, 2); // 1.5 en formato 64.64 fija - int128 zeroPointFive = ABDKMath64x64.divu(1, 2); // 0.5 en formato 64.64 fija - - // Ajustar y y k para que y esté en [0.5, 1.5] - while (y > onePointFive) { - y = y.div(ABDKMath64x64.fromUInt(2)); // Dividir y por 2 - k += 1; - } - - while (y < zeroPointFive) { - y = y.mul(ABDKMath64x64.fromUInt(2)); // Multiplicar y por 2 - k -= 1; - } - - // Ahora, y está en [0.5, 1.5] - // Podemos escribir y = 1 + z, donde z está en [-0.5, 0.5] - int128 one = ABDKMath64x64.fromUInt(1); - int128 z = y.sub(one); - - // Inicializar ln_x con el primer término de la serie de Taylor - ln_x = z; - - // Variables para la expansión de la serie - int128 term = z; // Término actual inicializado a z^1 / 1 - int128 z_power = z; // z elevado a la potencia n - int128 sign = ABDKMath64x64.fromInt(-1); // Signo alternante inicia en negativo - - // Calcular la suma de la serie de Taylor - for (uint256 n = 2; n <= terms; n++) { - // Calcular z_power = z^n - z_power = z_power.mul(z); - - // term = z^n / n - term = z_power.div(ABDKMath64x64.fromUInt(n)); - - // Alternar el signo para cada término - term = term.mul(sign); - - // Agregar el término al resultado - ln_x = ln_x.add(term); - - // Cambiar el signo para el siguiente término - sign = sign.neg(); - } - - // Agregar ln(2^k) = k * ln(2) - // ln(2) ≈ 0.69314718056 en decimal - int128 LN2 = 0xB17217F7D1CF79AB; // ln(2) en formato 64.64 fija - int128 kLn2 = ABDKMath64x64.fromInt(k).mul(LN2); - - ln_x = ln_x.add(kLn2); - } - - /** - * @notice Agrega un nuevo precio y calcula el retorno logarítmico respecto al precio anterior. - * @param newPrice Precio del activo en el nuevo periodo (sin decimales). - */ - function addPrice(uint256 newPrice) external /* onlyOwner */ { - require(prices.length - 1 < MAX_RETURNS, "Excede el maximo de retornos"); - - // Agregar el nuevo precio al array - prices.push(newPrice); - emit PriceAdded(newPrice, prices.length); - - // Si es el primer precio, no hay retorno que calcular - if (prices.length == 1) { - return; - } - - // Obtener el precio anterior y el actual - uint256 prevPrice = prices[prices.length - 2]; - uint256 currentPrice = prices[prices.length - 1]; - - // Convertir los precios a formato 64.64 fija - int128 pi = ABDKMath64x64.fromUInt(currentPrice); - int128 pi_prev = ABDKMath64x64.fromUInt(prevPrice); - - // Calcular la relación: Pi / P_{i-1} - int128 ratio = pi.div(pi_prev); - - // Calcular ln(ratio) usando la aproximación de la serie de Taylor - int128 logReturn = approximateLn(ratio); - - // Agregar el retorno al cálculo estadístico - _addLogReturn_internal(logReturn); - } - - /** - * @notice Agrega un nuevo retorno logarítmico y actualiza la media y varianza. - * @param logReturn Retorno logarítmico en formato 64.64 fija. - */ - function addLogReturn(int128 logReturn) external /* onlyOwner */ { - require(count < MAX_RETURNS, "Se ha alcanzado el maximo de retornos"); - _addLogReturn_internal(logReturn); - } - - /** - * @notice Función interna para agregar un retorno logarítmico y actualizar estadísticas. - * @param logReturn Retorno logarítmico en formato 64.64 fija. - */ - function _addLogReturn_internal(int128 logReturn) internal { - logReturns.push(logReturn); - count += 1; - - if (count == 1) { - mean = logReturn; - M2 = ABDKMath64x64.fromInt(0); // varianza no definida para 1 dato - emit LogReturnAdded(logReturn, count); - return; - } - - // Algoritmo de Welford para actualizar la media y M2 - int128 delta = logReturn.sub(mean); - mean = mean.add(delta.div(ABDKMath64x64.fromUInt(count))); - int128 delta2 = logReturn.sub(mean); - M2 = M2.add(delta.mul(delta2)); - - emit LogReturnAdded(logReturn, count); - } - - /** - * @notice Calcula la volatilidad implícita sigma y el drift u. - * @return sigma Volatilidad implícita en formato 64.64 fija. - * @return drift Drift calculado en formato 64.64 fija. - */ - function calculateSigmaAndDrift() external /* onlyOwner */ returns (int128 sigma, int128 drift) { - require(count >= 2, "Se requieren al menos 2 retornos para calcular varianza"); - - // Calcular varianza: varianza = M2 / (n - 1) - int128 variance = M2.div(ABDKMath64x64.fromUInt(count - 1)); - - // Calcular la desviación estándar (std dev) = sqrt(varianza) - int128 stdDev = sqrt(variance); - - // Anualizar la desviación estándar: sigma = stdDev * sqrt(252) - // sqrt(252) ≈ 15.87401 - int128 sqrt252 = ABDKMath64x64.fromUInt(15).add(ABDKMath64x64.divu(87401, 100000)); // Aproximación - - sigma = stdDev.mul(sqrt252); - - // Calcular drift u = mean - (sigma^2 / 2) - int128 sigmaSquared = sigma.mul(sigma); - int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2)); - drift = mean.sub(sigmaSquaredOver2); - - emit VolatilityAndDriftCalculated(sigma, drift); - } - - /** - * @notice Calcula el drift u usando la fórmula: - * u = muPool - (sigma^2 / 2) - * @param muPool Retorno medio en fees de la pool durante el tiempo t (μ_pool) en formato 64.64 fija. - * @param sigma Volatilidad implícita σ en formato 64.64 fija. - * @return u Drift calculado en formato 64.64 fija. - */ - function calculateDrift(int128 muPool, int128 sigma) public pure returns (int128 u) { - // Calcular sigma^2 - int128 sigmaSquared = sigma.mul(sigma); - - // Calcular sigma^2 / 2 - int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2)); - - // Calcular u = muPool - (sigma^2 / 2) - u = muPool.sub(sigmaSquaredOver2); - } - - /** - * @notice Calcula y devuelve la volatilidad implícita y drift sin almacenarlos. - * @param muPool Retorno medio en fees de la pool durante el tiempo t (μ_pool) en formato 64.64 fija. - * @param u Drift del activo subyacente (u) en formato 64.64 fija. - * @param t Tiempo en años (t), asumimos t = 1. - * @return sigma Volatilidad implícita en formato 64.64 fija. - * @return drift Drift calculado en formato 64.64 fija. - */ - function computeImpliedVolatilityAndDrift(int128 muPool, int128 u, uint256 t) external pure returns (int128 sigma, int128 drift) { - require(t > 0, "Tiempo t debe ser mayor que cero"); - - // Calcular u * t / 2 - int128 ut = u.mul(ABDKMath64x64.fromUInt(t)); - int128 utOver2 = ut.div(ABDKMath64x64.fromUInt(2)); - - // Calcular cosh(u * t / 2) - int128 coshUtOver2 = cosh(utOver2); - - // Calcular ln(cosh(u * t / 2)) utilizando la aproximación de ln(x) - int128 lnCoshUtOver2 = approximateLn(coshUtOver2); - - // Calcular [mu_pool * t - ln(cosh(u * t / 2))] - int128 muPoolTimesT = muPool.mul(ABDKMath64x64.fromUInt(t)); - int128 innerExpression = muPoolTimesT.sub(lnCoshUtOver2); - - // Calcular 8 / t - int128 eightOverT = ABDKMath64x64.fromUInt(8).div(ABDKMath64x64.fromUInt(t)); - - // Multiplicar 8/t * [mu_pool * t - ln(cosh(u * t / 2))] - int128 multiplicand = eightOverT.mul(innerExpression); - - // Calcular la raíz cuadrada de multiplicand - sigma = sqrt(multiplicand); - - // Calcular drift u = muPool - (sigma^2 / 2) - int128 sigmaSquared = sigma.mul(sigma); - int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2)); - drift = muPool.sub(sigmaSquaredOver2); - } - - /** - * @notice Obtiene la media de los retornos logarítmicos. - * @return mean_64x64 Media en formato 64.64 fija. - */ - function getMean() external view /* onlyOwner */ returns (int128) { - return mean; - } - - /** - * @notice Obtiene la varianza acumulada (M2). - * @return M2_64x64 Varianza acumulada en formato 64.64 fija. - */ - function getM2() external view /* onlyOwner */ returns (int128) { - return M2; - } - - /** - * @notice Obtiene un retorno logarítmico específico por su índice. - * @param index Índice del retorno logarítmico (empezando desde 0). - * @return logReturn Retorno logarítmico en formato 64.64 fija. - */ - function getLogReturn(uint256 index) external view /* onlyOwner */ returns (int128 logReturn) { - require(index < logReturns.length, "Indice fuera de rango"); - return logReturns[index]; - } - - /** - * @notice Obtiene todos los retornos logarítmicos. - * @return allLogReturns Array de retornos logarítmicos en formato 64.64 fija. - */ - function getAllLogReturns() external view /* onlyOwner */ returns (int128[] memory allLogReturns) { - return logReturns; - } -} \ No newline at end of file diff --git a/src/templates/GasPriceFeesHook.sol b/src/templates/GasPriceFeesHook.sol deleted file mode 100644 index 40c44d1..0000000 --- a/src/templates/GasPriceFeesHook.sol +++ /dev/null @@ -1,135 +0,0 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.0; - -// import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol"; -// import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; -// import {Hooks} from "v4-core/libraries/Hooks.sol"; -// import {PoolKey} from "v4-core/types/PoolKey.sol"; -// import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; -// import {LPFeeLibrary} from "v4-core/libraries/LPFeeLibrary.sol"; -// import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/types/BeforeSwapDelta.sol"; - -// contract GasPriceFeesHook is BaseHook { -// using LPFeeLibrary for uint24; - -// // Keeping track of the moving average gas price -// uint128 public movingAverageGasPrice; -// // How many times has the moving average been updated? -// // Needed as the denominator to update it the next time based on the moving average formula -// uint104 public movingAverageGasPriceCount; - -// // The default base fees we will charge -// uint24 public constant BASE_FEE = 5000; // 0.5% - -// error MustUseDynamicFee(); - -// // Initialize BaseHook parent contract in the constructor -// constructor(IPoolManager _poolManager) BaseHook(_poolManager) { -// updateMovingAverage(); -// } - -// // Required override function for BaseHook to let the PoolManager know which hooks are implemented -// function getHookPermissions() -// public -// pure -// override -// returns (Hooks.Permissions memory) -// { -// return -// Hooks.Permissions({ -// beforeInitialize: true, -// afterInitialize: false, -// beforeAddLiquidity: false, -// beforeRemoveLiquidity: false, -// afterAddLiquidity: false, -// afterRemoveLiquidity: false, -// beforeSwap: true, -// afterSwap: true, -// beforeDonate: false, -// afterDonate: false, -// beforeSwapReturnDelta: false, -// afterSwapReturnDelta: false, -// afterAddLiquidityReturnDelta: false, -// afterRemoveLiquidityReturnDelta: false -// }); -// } - -// function beforeInitialize( -// address, -// PoolKey calldata key, -// uint160, -// bytes calldata -// ) external pure override returns (bytes4) { -// // `.isDynamicFee()` function comes from using -// // the `SwapFeeLibrary` for `uint24` -// if (!key.fee.isDynamicFee()) revert MustUseDynamicFee(); -// return this.beforeInitialize.selector; -// } - -// function beforeSwap( -// address, -// PoolKey calldata key, -// IPoolManager.SwapParams calldata, -// bytes calldata -// ) -// external -// override -// onlyByPoolManager -// returns (bytes4, BeforeSwapDelta, uint24) -// { -// uint24 fee = getFee(); -// poolManager.updateDynamicLPFee(key, fee); -// return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); -// // * BeforeSwapDeltaLibrary.ZERO_DELTA => Calcular delta -// // .. Modificar la cantidad de tokens que se intercambiarán en el swap -// // .. + ^token / - !token - -// // * 0 => TArifa LP => tarifas dinamicas en funcion de -// // .. tamaño, V, LP.. - - -// } - -// function afterSwap( -// address, -// PoolKey calldata, -// IPoolManager.SwapParams calldata, -// BalanceDelta, -// bytes calldata -// ) external override returns (bytes4, int128) { -// updateMovingAverage(); -// return (this.afterSwap.selector, 0); - -// // 0 => Variacion del delta -// // .. Reembolso dinamico -// // Compensación de Slippage, Incentivos.. -// } - -// function getFee() internal view returns (uint24) { -// uint128 gasPrice = uint128(tx.gasprice); - -// // if gasPrice > movingAverageGasPrice * 1.1, then half the fees -// if (gasPrice > (movingAverageGasPrice * 11) / 10) { -// return BASE_FEE / 2; -// } - -// // if gasPrice < movingAverageGasPrice * 0.9, then double the fees -// if (gasPrice < (movingAverageGasPrice * 9) / 10) { -// return BASE_FEE * 2; -// } - -// return BASE_FEE; -// } - -// // Update our moving average gas price -// function updateMovingAverage() internal { -// uint128 gasPrice = uint128(tx.gasprice); - -// // New Average = ((Old Average * # of Txns Tracked) + Current Gas Price) / (# of Txns Tracked + 1) -// movingAverageGasPrice = -// ((movingAverageGasPrice * movingAverageGasPriceCount) + gasPrice) / -// (movingAverageGasPriceCount + 1); - -// movingAverageGasPriceCount++; -// } -// } diff --git a/src/templates/template1.sol b/src/templates/template1.sol deleted file mode 100644 index dc5d087..0000000 --- a/src/templates/template1.sol +++ /dev/null @@ -1,131 +0,0 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.0; - -// import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol"; -// import {Hooks} from "v4-core/libraries/Hooks.sol"; -// import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; -// import {PoolKey} from "v4-core/types/PoolKey.sol"; -// import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; -// import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; -// import {Currency, CurrencyLibrary} from "v4-core/types/Currency.sol"; -// import {TickMath} from "v4-core/libraries/TickMath.sol"; -// import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol"; -// import {BeforeSwapDelta, toBeforeSwapDelta} from "v4-core/types/BeforeSwapDelta.sol"; - -// contract template1 is BaseHook { -// using PoolIdLibrary for PoolKey; -// using CurrencyLibrary for Currency; -// using FixedPointMathLib for uint256; - -// uint256 public constant GAMMA_THRESHOLD = 2; -// uint256 public constant PRICE_RANGE_FACTOR = 10000; - -// mapping(PoolId => int24) public lastTicks; -// mapping(PoolId => int256) public deltas; -// mapping(PoolId => int256) public gammas; - -// constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} - -// function getHookPermissions() public pure override returns (Hooks.Permissions memory) { -// return Hooks.Permissions({ -// beforeInitialize: false, -// afterInitialize: false, -// beforeAddLiquidity: false, -// afterAddLiquidity: false, -// beforeRemoveLiquidity: false, -// afterRemoveLiquidity: false, -// beforeSwap: true, -// afterSwap: false, -// beforeDonate: false, -// afterDonate: false, -// beforeSwapReturnDelta: true, -// afterSwapReturnDelta: false, -// afterAddLiquidityReturnDelta: false, -// afterRemoveLiquidityReturnDelta: false -// }); -// } - -// function beforeSwap( -// address sender, -// PoolKey calldata key, -// IPoolManager.SwapParams calldata params, -// bytes calldata hookData -// ) external override returns (bytes4, BeforeSwapDelta, uint24) { -// PoolId poolId = key.toId(); -// if (isLargeSwap(params)) { -// (int128 deltaAdjustment, int128 liquidityDelta) = adjustLiquidityForDeltaGammaHedge(key, params); -// BeforeSwapDelta beforeSwapDelta = toBeforeSwapDelta(deltaAdjustment, liquidityDelta); -// return (BaseHook.beforeSwap.selector, beforeSwapDelta, 0); -// } else { - -// int256 deltaImpact = int256(params.amountSpecified) / 10; -// deltas[poolId] += deltaImpact; -// } -// return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0); -// } - -// function isLargeSwap(IPoolManager.SwapParams calldata params) internal pure returns (bool) { -// return params.amountSpecified > 1000000; -// } - -// function adjustLiquidityForDeltaGammaHedge(PoolKey calldata key, IPoolManager.SwapParams calldata params) internal returns (int128, int128) { -// PoolId poolId = key.toId(); -// int256 gammaImpact = calculateGammaImpact(params); -// int128 liquidityDelta = 0; -// if (gammaImpact != 0) { -// liquidityDelta = int128(distributeGammaHedgingLiquidity(key, gammaImpact)); -// gammas[poolId] += gammaImpact; -// } -// int128 deltaAdjustment = int128(adjustDeltaHedge(key, params)); -// deltas[poolId] += int256(deltaAdjustment); -// return (deltaAdjustment, liquidityDelta); -// } - -// function calculateGammaImpact(IPoolManager.SwapParams calldata params) internal pure returns (int256) { -// return int256(params.amountSpecified) * int256(GAMMA_THRESHOLD) / 1000000; -// } - -// function distributeGammaHedgingLiquidity(PoolKey calldata key, int256 gammaImpact) internal returns (uint128) { -// int24 tickLower = -887272; -// int24 tickUpper = 887272; -// uint128 liquidity = uint128(uint256(abs(gammaImpact) * 1000)); - -// IPoolManager.ModifyLiquidityParams memory params = IPoolManager.ModifyLiquidityParams({ -// tickLower: tickLower, -// tickUpper: tickUpper, -// liquidityDelta: int256(uint256(liquidity)), -// salt: bytes32(0) -// }); - -// (BalanceDelta callerDelta, BalanceDelta feeDelta) = poolManager.modifyLiquidity( -// key, -// params, -// abi.encode(0) -// ); - - -// return liquidity; -// } - -// function adjustDeltaHedge(PoolKey calldata key, IPoolManager.SwapParams calldata params) internal returns (int256) { -// int256 deltaAdjustment = int256(params.amountSpecified) / 2; -// poolManager.swap( -// key, -// IPoolManager.SwapParams({ -// zeroForOne: !params.zeroForOne, -// amountSpecified: deltaAdjustment, -// sqrtPriceLimitX96: 0 -// }), -// abi.encode(0) -// ); -// return deltaAdjustment; -// } - -// function getDeltaGamma(PoolId poolId) public view returns (int256, int256) { -// return (deltas[poolId], gammas[poolId]); -// } - -// function abs(int256 x) internal pure returns (int256) { -// return x >= 0 ? x : -x; -// } -// } \ No newline at end of file diff --git a/src/templates/template2.sol b/src/templates/template2.sol deleted file mode 100644 index fac3cb7..0000000 --- a/src/templates/template2.sol +++ /dev/null @@ -1,242 +0,0 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.0; - -// import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol"; -// import {Hooks} from "v4-core/libraries/Hooks.sol"; -// import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; -// import {PoolKey} from "v4-core/types/PoolKey.sol"; -// import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; -// import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; -// import {Currency, CurrencyLibrary} from "v4-core/types/Currency.sol"; -// import {TickMath} from "v4-core/libraries/TickMath.sol"; -// import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol"; -// import {BeforeSwapDelta, toBeforeSwapDelta} from "v4-core/types/BeforeSwapDelta.sol"; - -// /** -// * @title DeltaGammaHedgingHook -// * @dev Implements Delta-Gamma hedging strategies for Uniswap V4 pools -// * @notice This contract aims to mitigate impermanent loss through dynamic hedging -// */ -// contract DeltaGammaHedgingHook is BaseHook { -// using PoolIdLibrary for PoolKey; -// using CurrencyLibrary for Currency; -// using FixedPointMathLib for uint256; - -// using PoolIdLibrary for PoolKey; -// using CurrencyLibrary for Currency; -// using FixedPointMathLib for uint256; - -// // Thresholds and factors for hedging calculations -// uint256 public constant GAMMA_THRESHOLD = 2; -// uint256 public constant PRICE_RANGE_FACTOR = 10000; - -// // Mappings to store metrics per pool -// mapping(PoolId => int24) public lastTicks; -// mapping(PoolId => int256) public deltas; -// mapping(PoolId => int256) public gammas; - -// /** -// * @dev Constructor for DeltaGammaHedgingHook -// * @param _poolManager Address of the Uniswap V4 pool manager -// */ -// constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} - -// /** -// * @dev Defines the permissions for this hook -// * @return Hooks.Permissions struct with allowed hook points -// */ -// function getHookPermissions() public pure override returns (Hooks.Permissions memory) { -// return Hooks.Permissions({ -// beforeInitialize: true, -// afterInitialize: false, -// beforeAddLiquidity: false, -// afterAddLiquidity: false, -// beforeRemoveLiquidity: false, -// afterRemoveLiquidity: false, -// beforeSwap: true, -// afterSwap: true, -// beforeDonate: false, -// afterDonate: false, -// beforeSwapReturnDelta: true, -// afterSwapReturnDelta: true, -// afterAddLiquidityReturnDelta: false, -// afterRemoveLiquidityReturnDelta: false -// }); -// } - -// /** -// * @dev Hook called before pool initialization -// * @param sender Address initiating the pool -// * @param key Pool key -// * @param sqrtPriceX96 Initial sqrt price -// * @param hookData Additional data for the hook -// * @return bytes4 Function selector -// */ -// function beforeInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96, bytes calldata hookData) external override returns (bytes4) { -// require(key.fee >= 10000, "Fee must be at least 1%"); -// return BaseHook.beforeInitialize.selector; -// } - -// /** -// * @dev Hook called before a swap -// * @param sender Address initiating the swap -// * @param key Pool key -// * @param params Swap parameters -// * @param hookData Additional data for the hook -// * @return bytes4 Function selector -// * @return BeforeSwapDelta Delta adjustments before swap -// * @return uint24 Additional fee (if any) -// */ -// function beforeSwap(address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata hookData) external override returns (bytes4, BeforeSwapDelta, uint24) { -// PoolId poolId = key.toId(); -// if (isLargeSwap(params)) { -// (int128 deltaAdjustment, int128 liquidityDelta) = adjustLiquidityForDeltaGammaHedge(key, params); -// BeforeSwapDelta beforeSwapDelta = toBeforeSwapDelta(deltaAdjustment, liquidityDelta); -// return (BaseHook.beforeSwap.selector, beforeSwapDelta, 0); -// } else { -// int256 deltaImpact = int256(params.amountSpecified) / 10; -// deltas[poolId] += deltaImpact; -// } -// return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0); -// } - -// /** -// * @dev Hook called after a swap -// * @param sender Address that initiated the swap -// * @param key Pool key -// * @param params Swap parameters -// * @param delta Balance delta from the swap -// * @param hookData Additional data for the hook -// * @return bytes4 Function selector -// * @return int128 Additional balance delta (if any) -// */ -// function afterSwap( -// address sender, -// PoolKey calldata key, -// IPoolManager.SwapParams calldata params, -// BalanceDelta delta, -// bytes calldata hookData -// ) external override returns (bytes4, int128) { -// _rebalanceHedgingPositions(key, params); -// _updateMetrics(key, params); -// return (BaseHook.afterSwap.selector, 0); -// } - -// /** -// * @dev Determines if a swap is considered large -// * @param params Swap parameters -// * @return bool True if the swap is large, false otherwise -// */ -// function isLargeSwap(IPoolManager.SwapParams calldata params) internal pure returns (bool) { -// return params.amountSpecified > 1000000; -// } - -// /** -// * @dev Adjusts liquidity for Delta-Gamma hedging -// * @param key Pool key -// * @param params Swap parameters -// * @return int128 Delta adjustment -// * @return int128 Liquidity delta -// */ -// function adjustLiquidityForDeltaGammaHedge(PoolKey calldata key, IPoolManager.SwapParams calldata params) internal returns (int128, int128) { -// PoolId poolId = key.toId(); -// int256 gammaImpact = calculateGammaImpact(params); -// int128 liquidityDelta = 0; -// if (gammaImpact != 0) { -// liquidityDelta = int128(distributeGammaHedgingLiquidity(key, gammaImpact)); -// gammas[poolId] += gammaImpact; -// } -// int128 deltaAdjustment = int128(adjustDeltaHedge(key, params)); -// deltas[poolId] += int256(deltaAdjustment); -// return (deltaAdjustment, liquidityDelta); -// } - -// /** -// * @dev Calculates the gamma impact of a swap -// * @param params Swap parameters -// * @return int256 Calculated gamma impact -// */ -// function calculateGammaImpact(IPoolManager.SwapParams calldata params) internal pure returns (int256) { -// return int256(params.amountSpecified) * int256(GAMMA_THRESHOLD) / 1000000; -// } - -// /** -// * @dev Distributes liquidity for gamma hedging -// * @param key Pool key -// * @param gammaImpact Calculated gamma impact -// * @return uint128 Amount of distributed liquidity -// */ -// function distributeGammaHedgingLiquidity(PoolKey calldata key, int256 gammaImpact) internal returns (uint128) { -// int24 tickLower = -887272; -// int24 tickUpper = 887272; -// uint128 liquidity = uint128(uint256(abs(gammaImpact) * 1000)); - -// IPoolManager.ModifyLiquidityParams memory params = IPoolManager.ModifyLiquidityParams({ -// tickLower: tickLower, -// tickUpper: tickUpper, -// liquidityDelta: int256(uint256(liquidity)), -// salt: bytes32(0) -// }); - -// poolManager.modifyLiquidity(key, params, abi.encode(0)); - -// return liquidity; -// } - -// /** -// * @dev Adjusts the delta hedge -// * @param key Pool key -// * @param params Swap parameters -// * @return int256 Delta adjustment amount -// */ -// function adjustDeltaHedge(PoolKey calldata key, IPoolManager.SwapParams calldata params) internal returns (int256) { -// int256 deltaAdjustment = int256(params.amountSpecified) / 2; -// poolManager.swap( -// key, -// IPoolManager.SwapParams({ -// zeroForOne: !params.zeroForOne, -// amountSpecified: deltaAdjustment, -// sqrtPriceLimitX96: 0 -// }), -// abi.encode(0) -// ); -// return deltaAdjustment; -// } - -// /** -// * @dev Retrieves current delta and gamma values for a pool -// * @param poolId ID of the pool -// * @return int256 Current delta value -// * @return int256 Current gamma value -// */ -// function getDeltaGamma(PoolId poolId) public view returns (int256, int256) { -// return (deltas[poolId], gammas[poolId]); -// } - -// /** -// * @dev Calculates the absolute value of an integer -// * @param x Input integer -// * @return int256 Absolute value of x -// */ -// function abs(int256 x) internal pure returns (int256) { -// return x >= 0 ? x : -x; -// } - -// /** -// * @dev Internal function to rebalance hedging positions -// * @param key Pool key -// * @param params Swap parameters -// */ -// function _rebalanceHedgingPositions(PoolKey calldata key, IPoolManager.SwapParams calldata params) internal { -// // Implementar lógica para reequilibrar posiciones de hedging -// } - -// /** -// * @dev Internal function to update metrics after a swap -// * @param key Pool key -// * @param params Swap parameters -// */ -// function _updateMetrics(PoolKey calldata key, IPoolManager.SwapParams calldata params) internal { -// // Actualizar métricas importantes -// } -// } \ No newline at end of file diff --git a/test/GasPriceFeesHook.t.sol b/test/GasPriceFeesHook.t.sol deleted file mode 100644 index af4eb6c..0000000 --- a/test/GasPriceFeesHook.t.sol +++ /dev/null @@ -1,169 +0,0 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.0; - -// import {Test} from "forge-std/Test.sol"; -// 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 {Currency, CurrencyLibrary} from "v4-core/types/Currency.sol"; -// import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; -// import {LPFeeLibrary} from "v4-core/libraries/LPFeeLibrary.sol"; -// import {PoolKey} from "v4-core/types/PoolKey.sol"; -// import {Hooks} from "v4-core/libraries/Hooks.sol"; -// import {PoolSwapTest} from "v4-core/test/PoolSwapTest.sol"; -// import {GasPriceFeesHook} from "../src/GasPriceFeesHook.sol"; -// import {TickMath} from "v4-core/libraries/TickMath.sol"; -// import {console} from "forge-std/console.sol"; - -// contract TestGasPriceFeesHook is Test, Deployers { -// using CurrencyLibrary for Currency; -// using PoolIdLibrary for PoolKey; - -// GasPriceFeesHook hook; - -// function setUp() public { -// // Deploy v4-core -// deployFreshManagerAndRouters(); - -// // Deploy, mint tokens, and approve all periphery contracts for two tokens -// deployMintAndApprove2Currencies(); - -// // Deploy our hook with the proper flags -// address hookAddress = address( -// uint160( -// Hooks.BEFORE_INITIALIZE_FLAG | -// Hooks.BEFORE_SWAP_FLAG | -// Hooks.AFTER_SWAP_FLAG -// ) -// ); - -// // Set gas price = 10 gwei and deploy our hook -// vm.txGasPrice(10 gwei); -// deployCodeTo("GasPriceFeesHook", abi.encode(manager), hookAddress); -// hook = GasPriceFeesHook(hookAddress); - -// // Initialize a pool -// (key, ) = initPool( -// currency0, -// currency1, -// hook, -// LPFeeLibrary.DYNAMIC_FEE_FLAG, // Set the `DYNAMIC_FEE_FLAG` in place of specifying a fixed fee -// SQRT_PRICE_1_1, -// ZERO_BYTES -// ); - -// // Add some liquidity -// modifyLiquidityRouter.modifyLiquidity( -// key, -// IPoolManager.ModifyLiquidityParams({ -// tickLower: -60, -// tickUpper: 60, -// liquidityDelta: 100 ether, -// salt: bytes32(0) -// }), -// ZERO_BYTES -// ); -// } - -// function test_feeUpdatesWithGasPrice() public { -// // Set up our swap parameters -// PoolSwapTest.TestSettings memory testSettings = PoolSwapTest -// .TestSettings({takeClaims: false, settleUsingBurn: false}); - -// IPoolManager.SwapParams memory params = IPoolManager.SwapParams({ -// zeroForOne: true, -// amountSpecified: -0.00001 ether, -// sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1 -// }); - -// // Current gas price is 10 gwei -// // Moving average should also be 10 -// uint128 gasPrice = uint128(tx.gasprice); -// uint128 movingAverageGasPrice = hook.movingAverageGasPrice(); -// uint104 movingAverageGasPriceCount = hook.movingAverageGasPriceCount(); -// assertEq(gasPrice, 10 gwei); -// assertEq(movingAverageGasPrice, 10 gwei); -// assertEq(movingAverageGasPriceCount, 1); - -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- - -// // 1. Conduct a swap at gasprice = 10 gwei -// // This should just use `BASE_FEE` since the gas price is the same as the current average -// uint256 balanceOfToken1Before = currency1.balanceOfSelf(); -// swapRouter.swap(key, params, testSettings, ZERO_BYTES); -// uint256 balanceOfToken1After = currency1.balanceOfSelf(); -// uint256 outputFromBaseFeeSwap = balanceOfToken1After - -// balanceOfToken1Before; - -// assertGt(balanceOfToken1After, balanceOfToken1Before); - -// // Our moving average shouldn't have changed -// // only the count should have incremented -// movingAverageGasPrice = hook.movingAverageGasPrice(); -// movingAverageGasPriceCount = hook.movingAverageGasPriceCount(); -// assertEq(movingAverageGasPrice, 10 gwei); -// assertEq(movingAverageGasPriceCount, 2); - -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- - -// // 2. Conduct a swap at lower gasprice = 4 gwei -// // This should have a higher transaction fees -// vm.txGasPrice(4 gwei); -// balanceOfToken1Before = currency1.balanceOfSelf(); -// swapRouter.swap(key, params, testSettings, ZERO_BYTES); -// balanceOfToken1After = currency1.balanceOfSelf(); - -// uint256 outputFromIncreasedFeeSwap = balanceOfToken1After - -// balanceOfToken1Before; - -// assertGt(balanceOfToken1After, balanceOfToken1Before); - -// // Our moving average should now be (10 + 10 + 4) / 3 = 8 Gwei -// movingAverageGasPrice = hook.movingAverageGasPrice(); -// movingAverageGasPriceCount = hook.movingAverageGasPriceCount(); -// assertEq(movingAverageGasPrice, 8 gwei); -// assertEq(movingAverageGasPriceCount, 3); - -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- -// // ---------------------------------------------------------------------- - -// // 3. Conduct a swap at higher gas price = 12 gwei -// // This should have a lower transaction fees -// vm.txGasPrice(12 gwei); -// balanceOfToken1Before = currency1.balanceOfSelf(); -// swapRouter.swap(key, params, testSettings, ZERO_BYTES); -// balanceOfToken1After = currency1.balanceOfSelf(); - -// uint outputFromDecreasedFeeSwap = balanceOfToken1After - -// balanceOfToken1Before; - -// assertGt(balanceOfToken1After, balanceOfToken1Before); - -// // Our moving average should now be (10 + 10 + 4 + 12) / 4 = 9 Gwei -// movingAverageGasPrice = hook.movingAverageGasPrice(); -// movingAverageGasPriceCount = hook.movingAverageGasPriceCount(); - -// assertEq(movingAverageGasPrice, 9 gwei); -// assertEq(movingAverageGasPriceCount, 4); - -// // ------ - -// // 4. Check all the output amounts - -// console.log("Base Fee Output", outputFromBaseFeeSwap); -// console.log("Increased Fee Output", outputFromIncreasedFeeSwap); -// console.log("Decreased Fee Output", outputFromDecreasedFeeSwap); - -// assertGt(outputFromDecreasedFeeSwap, outputFromBaseFeeSwap); -// assertGt(outputFromBaseFeeSwap, outputFromIncreasedFeeSwap); -// } -// } diff --git a/test/Impermanent.t.sol b/test/Impermanent.t.sol deleted file mode 100644 index 2f60a5d..0000000 --- a/test/Impermanent.t.sol +++ /dev/null @@ -1,181 +0,0 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.0; - -// import {Test} from "forge-std/Test.sol"; -// import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; -// import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; -// import {PoolManager} from "v4-core/PoolManager.sol"; -// import {PoolKey} from "v4-core/types/PoolKey.sol"; -// import {CurrencyLibrary, Currency} from "v4-core/types/Currency.sol"; -// import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; -// import {Hooks} from "v4-core/libraries/Hooks.sol"; -// import {BalanceDelta} from "v4-core/types/BalanceDelta.sol"; -// import {template1} from "../src/template1.sol"; -// import {TickMath} from "v4-core/libraries/TickMath.sol"; - - -// import {PoolSwapTest} from "v4-core/test/PoolSwapTest.sol"; -// import {StateLibrary} from "v4-core/libraries/StateLibrary.sol"; - - - -// contract DeltaGammaHedgingHookTest is Test, Deployers { -// using CurrencyLibrary for Currency; -// using PoolIdLibrary for PoolKey; - -// template1 hook; - -// function setUp() public { -// // Desplegar contratos core de v4 -// deployFreshManagerAndRouters(); - -// // Desplegar dos tokens de prueba -// (currency0, currency1) = deployMintAndApprove2Currencies(); - -// // Configurar los flags hook -// uint160 flags = uint160( -// Hooks.BEFORE_SWAP_FLAG | -// Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG -// ); -// address hookAddress = address(flags); - -// // Desplegar hook -// deployCodeTo("template1.sol", abi.encode(manager), hookAddress); -// hook = template1(hookAddress); - -// // Inicializar un pool -// (key, ) = initPool( -// currency0, -// currency1, -// hook, -// 3000, -// SQRT_PRICE_1_1, -// ZERO_BYTES -// ); - -// // Agregar liquidez inicial -// modifyLiquidityRouter.modifyLiquidity( -// key, -// IPoolManager.ModifyLiquidityParams({ -// tickLower: -60, -// tickUpper: 60, -// liquidityDelta: 1000 ether, -// salt: bytes32(0) -// }), -// ZERO_BYTES -// ); -// } -// function testBeforeSwap() public { -// PoolSwapTest.TestSettings memory settings = PoolSwapTest.TestSettings({ -// takeClaims: false, -// settleUsingBurn: false -// }); - -// // Registra los balances iniciales -// uint balanceOfToken0Before = key.currency0.balanceOfSelf(); -// uint balanceOfToken1Before = key.currency1.balanceOfSelf(); - -// // Realiza el swap -// swapRouter.swap( -// key, -// IPoolManager.SwapParams({ -// zeroForOne: true, -// amountSpecified: -1000000, // Swap exacto de 1 millón de unidades del token0 -// sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1 -// }), -// settings, -// ZERO_BYTES -// ); - -// // Registra los balances después del swap -// uint balanceOfToken0After = key.currency0.balanceOfSelf(); -// uint balanceOfToken1After = key.currency1.balanceOfSelf(); - -// // Verifica que el swap se realizó correctamente -// assertEq(balanceOfToken0Before - balanceOfToken0After, 1000000, "El balance de token0 no cambio como se esperaba"); -// assertTrue(balanceOfToken1After > balanceOfToken1Before, "El balance de token1 no aumento"); -// } - -// // function alignTick(int24 tick, int24 tickSpacing) internal pure returns (int24) { -// // int24 compressed = tick / tickSpacing; -// // return compressed * tickSpacing; -// // } - -// // function testDeltaGammaAdjustment() public { -// // PoolSwapTest.TestSettings memory settings = PoolSwapTest.TestSettings({ -// // takeClaims: false, -// // settleUsingBurn: false -// // }); - -// // (int256 initialDelta, int256 initialGamma) = hook.getDeltaGamma(key.toId()); - -// // uint128 liquidity = uint128(StateLibrary.getLiquidity(manager, key.toId())); -// // int256 swapAmount = int256(uint256(liquidity) / 10); - -// // (, int24 currentTick, , ) = StateLibrary.getSlot0(manager, key.toId()); - -// // int24 tickSpacing = key.tickSpacing; -// // int24 tickLower = alignTick(currentTick - 887272, tickSpacing); -// // int24 tickUpper = alignTick(currentTick + 887272, tickSpacing); - -// // tickLower = tickLower < TickMath.MIN_TICK ? TickMath.MIN_TICK : tickLower; -// // tickUpper = tickUpper > TickMath.MAX_TICK ? TickMath.MAX_TICK : tickUpper; - -// // try swapRouter.swap( -// // key, -// // IPoolManager.SwapParams({ -// // zeroForOne: true, -// // amountSpecified: swapAmount, -// // sqrtPriceLimitX96: TickMath.getSqrtPriceAtTick(tickLower) -// // }), -// // settings, -// // abi.encode(tickLower, tickUpper) -// // ) { -// // (int256 newDelta, int256 newGamma) = hook.getDeltaGamma(key.toId()); -// // assertLt(newDelta, initialDelta, "Delta deberia haber disminuido"); -// // assertGt(newGamma, initialGamma, "Gamma deberia haber aumentado"); - -// // uint256 hookBalance0After = currency0.balanceOf(address(hook)); -// // uint256 hookBalance1After = currency1.balanceOf(address(hook)); -// // int256 actualBalanceChange = int256(hookBalance1After) - int256(hookBalance0After); -// // assertApproxEqRel(actualBalanceChange, initialDelta - newDelta, 1e16, "El ajuste de posicion del hook no coincide con el cambio de delta"); - -// // for (int24 i = 1; i <= 5; i++) { -// // (int256 preDelta, int256 preGamma) = hook.getDeltaGamma(key.toId()); -// // swapRouter.swap( -// // key, -// // IPoolManager.SwapParams({ -// // zeroForOne: true, -// // amountSpecified: swapAmount / 5, -// // sqrtPriceLimitX96: TickMath.getSqrtPriceAtTick(alignTick(currentTick - i * 10 * tickSpacing, tickSpacing)) -// // }), -// // settings, -// // abi.encode(tickLower, tickUpper) -// // ); -// // (int256 postDelta, int256 postGamma) = hook.getDeltaGamma(key.toId()); -// // assertLt(postDelta, preDelta, "Delta deberia disminuir en cada swap"); -// // assertGt(postGamma, preGamma, "Gamma deberia aumentar en cada swap"); -// // } - -// // swapRouter.swap( -// // key, -// // IPoolManager.SwapParams({ -// // zeroForOne: true, -// // amountSpecified: swapAmount, -// // sqrtPriceLimitX96: TickMath.getSqrtPriceAtTick(tickLower + tickSpacing) -// // }), -// // settings, -// // abi.encode(tickLower, tickUpper) -// // ); - -// // (int256 finalDelta, int256 finalGamma) = hook.getDeltaGamma(key.toId()); -// // assertLt(finalDelta, initialDelta, "Delta final deberia ser menor que la inicial"); -// // assertGt(finalGamma, initialGamma, "Gamma final deberia ser mayor que la inicial"); - -// // } catch Error(string memory reason) { -// // revert(string(abi.encodePacked("Test fallido: ", reason))); -// // } -// // } - - -// } \ No newline at end of file diff --git a/test/univ4-risk-neutral-hook.t.sol b/test/univ4-risk-neutral-hook.t.sol new file mode 100644 index 0000000..f8bd254 --- /dev/null +++ b/test/univ4-risk-neutral-hook.t.sol @@ -0,0 +1,68 @@ +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.0; + +// import {Test} from "forge-std/Test.sol"; +// 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 {Currency, CurrencyLibrary} from "v4-core/types/Currency.sol"; +// import {PoolId, PoolIdLibrary} from "v4-core/types/PoolId.sol"; +// import {LPFeeLibrary} from "v4-core/libraries/LPFeeLibrary.sol"; +// import {PoolKey} from "v4-core/types/PoolKey.sol"; +// import {Hooks} from "v4-core/libraries/Hooks.sol"; +// import {PoolSwapTest} from "v4-core/test/PoolSwapTest.sol"; +// import {univ4-risk-neutral-hook} from "../src/GasPriceFeesHook.sol"; +// import {TickMath} from "v4-core/libraries/TickMath.sol"; +// import {console} from "forge-std/console.sol"; + +// contract univ4riskneutralhookTest is Test, Deployers { +// using CurrencyLibrary for Currency; +// using PoolIdLibrary for PoolKey; + +// GasPriceFeesHook hook; + +// function setUp() public { +// // Deploy v4-core +// deployFreshManagerAndRouters(); + +// // Deploy, mint tokens, and approve all periphery contracts for two tokens +// deployMintAndApprove2Currencies(); + +// // Deploy our hook with the proper flags +// address hookAddress = address( +// uint160( +// Hooks.BEFORE_INITIALIZE_FLAG | +// Hooks.BEFORE_SWAP_FLAG | +// Hooks.AFTER_SWAP_FLAG +// ) +// ); + +// vm.txGasPrice(10 gwei); +// deployCodeTo("GasPriceFeesHook", abi.encode(manager), hookAddress); +// hook = GasPriceFeesHook(hookAddress); + +// // Initialize a pool +// (key, ) = initPool( +// currency0, +// currency1, +// hook, +// LPFeeLibrary.DYNAMIC_FEE_FLAG, // Set the `DYNAMIC_FEE_FLAG` in place of specifying a fixed fee +// SQRT_PRICE_1_1, +// ZERO_BYTES +// ); + +// modifyLiquidityRouter.modifyLiquidity( +// key, +// IPoolManager.ModifyLiquidityParams({ +// tickLower: -60, +// tickUpper: 60, +// liquidityDelta: 100 ether, +// salt: bytes32(0) +// }), +// ZERO_BYTES +// ); +// } + + +// }