From c53630afa691418b637cf6bedcf8aeef9cefcd59 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Thu, 27 Mar 2025 15:38:11 -0400 Subject: [PATCH 1/2] added tests to repro the issue no target improved price estimation using a uniswap lib clean up lib fix deploy scripts update deploy script remove target folder add rust target folders to gitignore --- .gitignore | 4 + .../LenderCommitmentGroup_Smart.sol | 8 +- .../libraries/UniswapPricingLibraryV2.sol | 167 ++++++++++++++++++ .../uniswap_pricing_libraryV2.ts | 19 ++ ...4_upgrade_lender_groups_pricing_library.ts | 101 +++++++++++ packages/contracts/lib/forge-std | 2 +- .../UniswapPricingLibrary_Test.sol | 117 ++++++++++-- 7 files changed, 397 insertions(+), 21 deletions(-) create mode 100644 packages/contracts/contracts/libraries/UniswapPricingLibraryV2.sol create mode 100644 packages/contracts/deploy/lender_commitment_forwarder/uniswap_pricing_libraryV2.ts create mode 100644 packages/contracts/deploy/upgrades/24_upgrade_lender_groups_pricing_library.ts diff --git a/.gitignore b/.gitignore index 416059a62..95d280fca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. **/node_modules + +# rust target folders +**/target + packages/contracts/generated packages/contracts/deployments/hardhat packages/contracts/deployments/tenderly diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 0f14ba1fc..0deed5b76 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -54,7 +54,7 @@ import { ILenderCommitmentGroup } from "../../../interfaces/ILenderCommitmentGro import { Payment } from "../../../TellerV2Storage.sol"; import {IUniswapPricingLibrary} from "../../../interfaces/IUniswapPricingLibrary.sol"; -import {UniswapPricingLibrary} from "../../../libraries/UniswapPricingLibrary.sol"; +import {UniswapPricingLibraryV2} from "../../../libraries/UniswapPricingLibraryV2.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; @@ -884,7 +884,7 @@ contract LenderCommitmentGroup_Smart is uint256 principalAmount ) public view virtual returns (uint256 collateralTokensAmountToMatchValue) { - uint256 pairPriceWithTwapFromOracle = UniswapPricingLibrary + uint256 pairPriceWithTwapFromOracle = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolOracleRoutes); @@ -908,7 +908,7 @@ contract LenderCommitmentGroup_Smart is IUniswapPricingLibrary.PoolRouteConfig[] memory poolOracleRoutes ) external view virtual returns (uint256 ) { - uint256 pairPriceWithTwapFromOracle = UniswapPricingLibrary + uint256 pairPriceWithTwapFromOracle = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolOracleRoutes); @@ -919,7 +919,7 @@ contract LenderCommitmentGroup_Smart is IUniswapPricingLibrary.PoolRouteConfig[] memory poolOracleRoutes ) external view virtual returns (uint256 ) { - uint256 pairPriceWithTwapFromOracle = UniswapPricingLibrary + uint256 pairPriceWithTwapFromOracle = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolOracleRoutes); diff --git a/packages/contracts/contracts/libraries/UniswapPricingLibraryV2.sol b/packages/contracts/contracts/libraries/UniswapPricingLibraryV2.sol new file mode 100644 index 000000000..2d31c5b9f --- /dev/null +++ b/packages/contracts/contracts/libraries/UniswapPricingLibraryV2.sol @@ -0,0 +1,167 @@ +pragma solidity >=0.8.0 <0.9.0; +// SPDX-License-Identifier: MIT + + + +import {IUniswapPricingLibrary} from "../interfaces/IUniswapPricingLibrary.sol"; + +import "@openzeppelin/contracts/utils/math/Math.sol"; + + +import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; + +// Libraries +import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol"; + +import "../interfaces/uniswap/IUniswapV3Pool.sol"; + +import "../libraries/uniswap/TickMath.sol"; +import "../libraries/uniswap/FixedPoint96.sol"; +import "../libraries/uniswap/FullMath.sol"; + + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + + +/* + +Only do decimal expansion if it is an ERC20 not anything else !! + +*/ + +library UniswapPricingLibraryV2 +{ + + uint256 constant STANDARD_EXPANSION_FACTOR = 1e18; + + + function getUniswapPriceRatioForPoolRoutes( + IUniswapPricingLibrary.PoolRouteConfig[] memory poolRoutes + ) public view returns (uint256 priceRatio) { + require(poolRoutes.length <= 2, "invalid pool routes length"); + + if (poolRoutes.length == 2) { + uint256 pool0PriceRatio = getUniswapPriceRatioForPool( + poolRoutes[0] + ); + + uint256 pool1PriceRatio = getUniswapPriceRatioForPool( + poolRoutes[1] + ); + + return + FullMath.mulDiv( + pool0PriceRatio, + pool1PriceRatio, + STANDARD_EXPANSION_FACTOR + ); + } else if (poolRoutes.length == 1) { + return getUniswapPriceRatioForPool(poolRoutes[0]); + } + + //else return 0 + } + + /* + The resultant product is expanded by STANDARD_EXPANSION_FACTOR one time + */ + function getUniswapPriceRatioForPool( + IUniswapPricingLibrary.PoolRouteConfig memory _poolRouteConfig + ) public view returns (uint256 priceRatio) { + + + + // this is expanded by 2**96 or 1e28 + uint160 sqrtPriceX96 = getSqrtTwapX96( + _poolRouteConfig.pool, + _poolRouteConfig.twapInterval + ); + + + bool invert = ! _poolRouteConfig.zeroForOne; + + //this output will be expanded by 1e18 + return getQuoteFromSqrtRatioX96( sqrtPriceX96 , uint128( STANDARD_EXPANSION_FACTOR ), invert) ; + + + + } + + + //taken directly from uniswap oracle lib + /** + * @dev Calculates the amount of quote token received for a given amount of base token + * based on the square root of the price ratio (sqrtRatioX96). + * + * @param sqrtRatioX96 The square root of the price ratio(in terms of token1/token0) between two tokens, encoded as a Q64.96 value. + * @param baseAmount The amount of the base token for which the quote is to be calculated. Specify 1e18 for a price(quoteAmount) with 18 decimals of precision. + * @param inverse Specifies the direction of the price quote. If true then baseAmount must be the amount of token1, if false then baseAmount must be the amount for token0 + * + * @return quoteAmount The calculated amount of the quote token for the specified baseAmount + */ + + function getQuoteFromSqrtRatioX96( + uint160 sqrtRatioX96, + uint128 baseAmount, + bool inverse + ) internal pure returns (uint256 quoteAmount) { + // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself + if (sqrtRatioX96 <= type(uint128).max) { + uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; + quoteAmount = !inverse + ? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192) + : FullMath.mulDiv(1 << 192, baseAmount, ratioX192); + } else { + uint256 ratioX128 = FullMath.mulDiv( + sqrtRatioX96, + sqrtRatioX96, + 1 << 64 + ); + quoteAmount = !inverse + ? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128) + : FullMath.mulDiv(1 << 128, baseAmount, ratioX128); + } + } + + + function getSqrtTwapX96(address uniswapV3Pool, uint32 twapInterval) + internal + view + returns (uint160 sqrtPriceX96) + { + if (twapInterval == 0) { + // return the current price if twapInterval == 0 + (sqrtPriceX96, , , , , , ) = IUniswapV3Pool(uniswapV3Pool).slot0(); + } else { + uint32[] memory secondsAgos = new uint32[](2); + secondsAgos[0] = twapInterval + 1; // from (before) + secondsAgos[1] = 1; // one block prior + + (int56[] memory tickCumulatives, ) = IUniswapV3Pool(uniswapV3Pool) + .observe(secondsAgos); + + // tick(imprecise as it's an integer) to price + sqrtPriceX96 = TickMath.getSqrtRatioAtTick( + int24( + (tickCumulatives[1] - tickCumulatives[0]) / + int32(twapInterval) + ) + ); + } + } + + function getPriceX96FromSqrtPriceX96(uint160 sqrtPriceX96) + internal + pure + returns (uint256 priceX96) + { + + + return FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, FixedPoint96.Q96); + } + +} \ No newline at end of file diff --git a/packages/contracts/deploy/lender_commitment_forwarder/uniswap_pricing_libraryV2.ts b/packages/contracts/deploy/lender_commitment_forwarder/uniswap_pricing_libraryV2.ts new file mode 100644 index 000000000..f39387a25 --- /dev/null +++ b/packages/contracts/deploy/lender_commitment_forwarder/uniswap_pricing_libraryV2.ts @@ -0,0 +1,19 @@ +import { DeployFunction } from 'hardhat-deploy/dist/types' + +const deployFn: DeployFunction = async (hre) => { + const { deployer } = await hre.getNamedAccounts() + const uniswapPricingLibraryV2 = await hre.deployments.deploy('UniswapPricingLibraryV2', { + from: deployer, + }) +} + +// tags and deployment +deployFn.id = 'teller-v2:uniswap-pricing-library-v2' +deployFn.tags = ['teller-v2', 'teller-v2:uniswap-pricing-library-v2'] +deployFn.dependencies = [''] + + +deployFn.skip = async (hre) => { + return !hre.network.live || !['sepolia', 'polygon' , 'base','arbitrum','mainnet','mainnet_live_fork'].includes(hre.network.name) +} +export default deployFn \ No newline at end of file diff --git a/packages/contracts/deploy/upgrades/24_upgrade_lender_groups_pricing_library.ts b/packages/contracts/deploy/upgrades/24_upgrade_lender_groups_pricing_library.ts new file mode 100644 index 000000000..5056a8e01 --- /dev/null +++ b/packages/contracts/deploy/upgrades/24_upgrade_lender_groups_pricing_library.ts @@ -0,0 +1,101 @@ +import { DeployFunction } from 'hardhat-deploy/dist/types' + +const deployFn: DeployFunction = async (hre) => { + hre.log('----------') + hre.log('') + hre.log('Lendergroups: Proposing upgrade...') + + + + const lenderCommitmentGroupBeaconProxy = await hre.contracts.get('LenderCommitmentGroupBeacon') + + const tellerV2 = await hre.contracts.get('TellerV2') + const SmartCommitmentForwarder = await hre.contracts.get( + 'SmartCommitmentForwarder' + ) + const tellerV2Address = await tellerV2.getAddress() + + const smartCommitmentForwarderAddress = + await SmartCommitmentForwarder.getAddress() + + let uniswapV3FactoryAddress: string + switch (hre.network.name) { + case 'mainnet': + case 'goerli': + case 'arbitrum': + case 'optimism': + case 'polygon': + case 'localhost': + uniswapV3FactoryAddress = '0x1F98431c8aD98523631AE4a59f267346ea31F984' + break + case 'base': + uniswapV3FactoryAddress = '0x33128a8fC17869897dcE68Ed026d694621f6FDfD' + break + case 'sepolia': + uniswapV3FactoryAddress = '0x0227628f3F023bb0B980b67D528571c95c6DaC1c' + break + default: + throw new Error('No swap factory address found for this network') + } + const uniswapPricingLibraryV2 = await hre.deployments.get('uniswapPricingLibraryV2') + + + +//this is why the owner of the beacon should be timelock controller ! +// so we can upgrade it like this . Using a proposal. This actually goes AROUND the proxy admin, interestingly. + await hre.upgrades.proposeBatchTimelock({ + title: 'LenderGroups: PoolOracle View', + description: ` +# LenderGroups + +* A patch to update the uniswap pricing library v2. +`, + _steps: [ + { + beacon: lenderCommitmentGroupBeaconProxy, + implFactory: await hre.ethers.getContractFactory('LenderCommitmentGroup_Smart', { + libraries: { + uniswapPricingLibraryV2: uniswapPricingLibraryV2.address, + }, + }), + + opts: { + // unsafeSkipStorageCheck: true, + unsafeAllow: [ + 'constructor', + 'state-variable-immutable', + 'external-library-linking', + ], + constructorArgs: [ + tellerV2Address, + smartCommitmentForwarderAddress, + uniswapV3FactoryAddress, + + ], + }, + }, + ], + }) + + hre.log('done.') + hre.log('') + hre.log('----------') + + return true +} + +// tags and deployment +deployFn.id = 'lender-commitment-group-beacon:upgrade-uniswap-price-lib-v2' +deployFn.tags = ['lender-commitment-group-beacon'] +deployFn.dependencies = [ + 'teller-v2:deploy', + 'teller-v2:uniswap-pricing-library-v2', + 'smart-commitment-forwarder:deploy', + 'teller-v2:uniswap-pricing-library', + 'lender-commitment-group-beacon:deploy' +] + +deployFn.skip = async (hre) => { + return !hre.network.live || !['sepolia','polygon'].includes(hre.network.name) +} +export default deployFn diff --git a/packages/contracts/lib/forge-std b/packages/contracts/lib/forge-std index 2f6762e4f..73a504d2c 160000 --- a/packages/contracts/lib/forge-std +++ b/packages/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 2f6762e4f73f3d835457c220b5f62dfeeb6f6341 +Subproject commit 73a504d2cf6f37b7ce285b479f4c681f76e95f1b diff --git a/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibrary_Test.sol b/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibrary_Test.sol index 75f3756bb..28b70bd27 100644 --- a/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibrary_Test.sol +++ b/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibrary_Test.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; //import "forge-std/Test.sol"; -import "../../contracts/libraries/UniswapPricingLibrary.sol"; +import "../../contracts/libraries/UniswapPricingLibraryV2.sol"; import { Testable } from "../Testable.sol"; @@ -42,6 +42,85 @@ contract UniswapPricingLibraryTest is Testable { } + + function test_getUniswapPriceRatioForPool_high_price() public { + + bool zeroForOne = true; // ?? + + mockUniswapPool.set_mockSqrtPriceX96( 1e40 ); + + uint32 twapInterval = 0; + + IUniswapPricingLibrary.PoolRouteConfig + memory routeConfig = IUniswapPricingLibrary.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + assertEq( 15930919111324522770288803977677118055911 , priceRatio , "unexpected price ratio") ; + } + + + function test_getUniswapPriceRatioForPool_low_price() public { + + bool zeroForOne = true; // ?? + + mockUniswapPool.set_mockSqrtPriceX96( 4295128739 ); + + uint32 twapInterval = 0; + + IUniswapPricingLibrary.PoolRouteConfig + memory routeConfig = IUniswapPricingLibrary.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + // assertEq( 0 , priceRatio , "unexpected price ratio") ; + } + + function test_getUniswapPriceRatioForPool_very_low_price() public { + + bool zeroForOne = true; // ?? + + mockUniswapPool.set_mockSqrtPriceX96( 42959 ); + + uint32 twapInterval = 0; + + IUniswapPricingLibrary.PoolRouteConfig + memory routeConfig = IUniswapPricingLibrary.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + // assertEq( 1e18 , priceRatio , "unexpected price ratio") ; + } + + + function test_getUniswapPriceRatioForPool_same_price() public { bool zeroForOne = false; // ?? @@ -59,7 +138,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPool(routeConfig); @@ -88,7 +167,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPool(routeConfig); @@ -121,7 +200,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: principalTokenDecimals }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPool(routeConfig); @@ -168,7 +247,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: principalTokenDecimals }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -214,7 +293,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: principalTokenDecimals }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolRoutes); @@ -257,7 +336,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -288,7 +367,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -299,6 +378,12 @@ contract UniswapPricingLibraryTest is Testable { } +/* + Error: a == b not satisfied [uint] + Left: 1000000000000000000 + Right: 10000000000000000000000000000000000000000000000 + +*/ function test_getUniswapPriceRatioForPoolRoutes_price_scenario_C() public { mockUniswapPool.set_mockSqrtPriceX96(1 * 2**96); @@ -319,7 +404,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -351,7 +436,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary + uint256 priceRatio = UniswapPricingLibraryV2 .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -360,7 +445,7 @@ contract UniswapPricingLibraryTest is Testable { uint256 principalAmount = 1000; - assertEq( 953702069891996282433059426929 , priceRatio , "unexpected price ratio") ; + assertEq( 953702069890566199069022917957 , priceRatio , "unexpected price ratio") ; } @@ -383,7 +468,7 @@ function test_getUniswapPriceRatioForPool_zeroPrice() public { }); vm.expectRevert(); - uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPool(routeConfig) ; + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig) ; } function test_getUniswapPriceRatioForPool_largePrice() public { @@ -401,7 +486,7 @@ function test_getUniswapPriceRatioForPool_largePrice() public { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPool(routeConfig); + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig); assertEq( priceRatio, 0 , "unexpected price ratio") ; @@ -423,7 +508,7 @@ function test_getUniswapPriceRatioForPool_invalidDecimals() public { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPool(routeConfig) ; + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig) ; @@ -447,7 +532,7 @@ function test_getUniswapPriceRatioForPool_twapInterval() public { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPool(routeConfig); + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig); assert(priceRatio > 0); } @@ -481,7 +566,7 @@ function test_getUniswapPriceRatioForPoolRoutes_twoPools_differentPrices() publi token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPoolRoutes(poolRoutes); + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPoolRoutes(poolRoutes); //calculatet this more intelligently uint256 expectedPriceRatio = 15625000000000000; From 137843d39a8bad4792c0378ff36b9de04e10deeb Mon Sep 17 00:00:00 2001 From: andy Date: Fri, 4 Apr 2025 13:14:53 -0400 Subject: [PATCH 2/2] add a fn with custom expansion --- .../LenderCommitmentGroup_Smart.sol | 8 +- .../interfaces/IUniswapPricingLibraryV2.sol | 32 + .../libraries/UniswapPricingLibraryV2.sol | 40 +- packages/contracts/lib/forge-std | 2 +- .../UniswapPricingLibraryV2_Test.sol | 631 ++++++++++++++++++ .../UniswapPricingLibrary_Test.sol | 112 +--- 6 files changed, 714 insertions(+), 111 deletions(-) create mode 100644 packages/contracts/contracts/interfaces/IUniswapPricingLibraryV2.sol create mode 100644 packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibraryV2_Test.sol diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 0deed5b76..0f14ba1fc 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -54,7 +54,7 @@ import { ILenderCommitmentGroup } from "../../../interfaces/ILenderCommitmentGro import { Payment } from "../../../TellerV2Storage.sol"; import {IUniswapPricingLibrary} from "../../../interfaces/IUniswapPricingLibrary.sol"; -import {UniswapPricingLibraryV2} from "../../../libraries/UniswapPricingLibraryV2.sol"; +import {UniswapPricingLibrary} from "../../../libraries/UniswapPricingLibrary.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; @@ -884,7 +884,7 @@ contract LenderCommitmentGroup_Smart is uint256 principalAmount ) public view virtual returns (uint256 collateralTokensAmountToMatchValue) { - uint256 pairPriceWithTwapFromOracle = UniswapPricingLibraryV2 + uint256 pairPriceWithTwapFromOracle = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolOracleRoutes); @@ -908,7 +908,7 @@ contract LenderCommitmentGroup_Smart is IUniswapPricingLibrary.PoolRouteConfig[] memory poolOracleRoutes ) external view virtual returns (uint256 ) { - uint256 pairPriceWithTwapFromOracle = UniswapPricingLibraryV2 + uint256 pairPriceWithTwapFromOracle = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolOracleRoutes); @@ -919,7 +919,7 @@ contract LenderCommitmentGroup_Smart is IUniswapPricingLibrary.PoolRouteConfig[] memory poolOracleRoutes ) external view virtual returns (uint256 ) { - uint256 pairPriceWithTwapFromOracle = UniswapPricingLibraryV2 + uint256 pairPriceWithTwapFromOracle = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolOracleRoutes); diff --git a/packages/contracts/contracts/interfaces/IUniswapPricingLibraryV2.sol b/packages/contracts/contracts/interfaces/IUniswapPricingLibraryV2.sol new file mode 100644 index 000000000..6ef17965b --- /dev/null +++ b/packages/contracts/contracts/interfaces/IUniswapPricingLibraryV2.sol @@ -0,0 +1,32 @@ +// SPDX-Licence-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; + +interface IUniswapPricingLibraryV2 { + + + struct PoolRouteConfig { + address pool; + bool zeroForOne; + uint32 twapInterval; + uint256 token0Decimals; + uint256 token1Decimals; + } + + + + function getUniswapPriceRatioForPoolRoutes( + PoolRouteConfig[] memory poolRoutes + ) external view returns (uint256 priceRatio); + + + function getUniswapPriceRatioForPool( + PoolRouteConfig memory poolRoute + ) external view returns (uint256 priceRatio); + + + function getUniswapPriceRatioForPool( + IUniswapPricingLibraryV2.PoolRouteConfig memory _poolRouteConfig, + uint128 expansion_factor + ) external view returns (uint256 priceRatio); + +} diff --git a/packages/contracts/contracts/libraries/UniswapPricingLibraryV2.sol b/packages/contracts/contracts/libraries/UniswapPricingLibraryV2.sol index 2d31c5b9f..57f986943 100644 --- a/packages/contracts/contracts/libraries/UniswapPricingLibraryV2.sol +++ b/packages/contracts/contracts/libraries/UniswapPricingLibraryV2.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.0 <0.9.0; -import {IUniswapPricingLibrary} from "../interfaces/IUniswapPricingLibrary.sol"; +import {IUniswapPricingLibraryV2} from "../interfaces/IUniswapPricingLibraryV2.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; @@ -40,7 +40,7 @@ library UniswapPricingLibraryV2 function getUniswapPriceRatioForPoolRoutes( - IUniswapPricingLibrary.PoolRouteConfig[] memory poolRoutes + IUniswapPricingLibraryV2.PoolRouteConfig[] memory poolRoutes ) public view returns (uint256 priceRatio) { require(poolRoutes.length <= 2, "invalid pool routes length"); @@ -70,7 +70,7 @@ library UniswapPricingLibraryV2 The resultant product is expanded by STANDARD_EXPANSION_FACTOR one time */ function getUniswapPriceRatioForPool( - IUniswapPricingLibrary.PoolRouteConfig memory _poolRouteConfig + IUniswapPricingLibraryV2.PoolRouteConfig memory _poolRouteConfig ) public view returns (uint256 priceRatio) { @@ -89,6 +89,29 @@ library UniswapPricingLibraryV2 + } + + + function getUniswapPriceRatioForPool( + IUniswapPricingLibraryV2.PoolRouteConfig memory _poolRouteConfig, + uint128 expansion_factor + ) public view returns (uint256 priceRatio) { + + + // this is expanded by 2**96 or 1e28 + uint160 sqrtPriceX96 = getSqrtTwapX96( + _poolRouteConfig.pool, + _poolRouteConfig.twapInterval + ); + + + bool invert = ! _poolRouteConfig.zeroForOne; + + //this output will be expanded by expansion_factor + return getQuoteFromSqrtRatioX96( sqrtPriceX96 , expansion_factor , invert) ; + + + } @@ -154,14 +177,5 @@ library UniswapPricingLibraryV2 } } - function getPriceX96FromSqrtPriceX96(uint160 sqrtPriceX96) - internal - pure - returns (uint256 priceX96) - { - - - return FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, FixedPoint96.Q96); - } - + } \ No newline at end of file diff --git a/packages/contracts/lib/forge-std b/packages/contracts/lib/forge-std index 73a504d2c..2f6762e4f 160000 --- a/packages/contracts/lib/forge-std +++ b/packages/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 73a504d2cf6f37b7ce285b479f4c681f76e95f1b +Subproject commit 2f6762e4f73f3d835457c220b5f62dfeeb6f6341 diff --git a/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibraryV2_Test.sol b/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibraryV2_Test.sol new file mode 100644 index 000000000..c160a46bb --- /dev/null +++ b/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibraryV2_Test.sol @@ -0,0 +1,631 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +//import "forge-std/Test.sol"; +import {UniswapPricingLibraryV2} from "../../contracts/libraries/UniswapPricingLibraryV2.sol"; + + +import { Testable } from "../Testable.sol"; + +import { UniswapV3PoolMock } from "../../contracts/mock/uniswap/UniswapV3PoolMock.sol"; + +import "../../contracts/mock/MarketRegistryMock.sol"; + + +import { IUniswapPricingLibraryV2 } from "../../contracts/interfaces/IUniswapPricingLibraryV2.sol"; + +import "../../contracts/TellerV2Context.sol"; + +import "forge-std/console.sol"; + + + +contract UniswapPricingLibraryTest is Testable { + + LenderCommitmentForwarderTest_TellerV2Mock private tellerV2Mock; + MarketRegistryMock mockMarketRegistry; + + // using UniswapPricingLibrary for IUniswapPricingLibraryV2.PoolRouteConfig[]; + + UniswapV3PoolMock mockUniswapPool; + UniswapV3PoolMock mockUniswapPoolSecondary; + + function setUp() public { + + + tellerV2Mock = new LenderCommitmentForwarderTest_TellerV2Mock(); + mockMarketRegistry = new MarketRegistryMock(); + + mockUniswapPool = new UniswapV3PoolMock(); + + mockUniswapPoolSecondary = new UniswapV3PoolMock(); + + } + + + function test_getUniswapPriceRatioForPool_high_price() public { + + bool zeroForOne = true; // ?? + + mockUniswapPool.set_mockSqrtPriceX96( 1e40 ); + + uint32 twapInterval = 0; + + IUniswapPricingLibraryV2.PoolRouteConfig + memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + assertEq( 15930919111324522770288803977677118055911 , priceRatio , "unexpected price ratio") ; + } + + + function test_getUniswapPriceRatioForPool_low_price() public { + + bool zeroForOne = true; // ?? + + mockUniswapPool.set_mockSqrtPriceX96( 4295128739 ); + + uint32 twapInterval = 0; + + IUniswapPricingLibraryV2.PoolRouteConfig + memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + // assertEq( 0 , priceRatio , "unexpected price ratio") ; + } + + function test_getUniswapPriceRatioForPool_very_low_price() public { + + bool zeroForOne = true; // ?? + + mockUniswapPool.set_mockSqrtPriceX96( 42959 ); + + uint32 twapInterval = 0; + + IUniswapPricingLibraryV2.PoolRouteConfig + memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + // assertEq( 1e18 , priceRatio , "unexpected price ratio") ; + } + + + + function test_getUniswapPriceRatioForPool_same_price() public { + + bool zeroForOne = false; // ?? + + mockUniswapPool.set_mockSqrtPriceX96(1 * 2**96); + + uint32 twapInterval = 0; + + IUniswapPricingLibraryV2.PoolRouteConfig + memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + assertEq( 1e18 , priceRatio , "unexpected price ratio") ; + } + + + function test_getUniswapPriceRatioForPool_different_price() public { + + bool zeroForOne = false; // ?? + + + //i think this means the ratio is 100:1 + mockUniswapPool.set_mockSqrtPriceX96(10 * 2**96); + + + uint32 twapInterval = 0; + + IUniswapPricingLibraryV2.PoolRouteConfig + memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + assertEq( 1e16 , priceRatio , "unexpected price ratio") ; + } + + + + function test_getUniswapPriceRatioForPool_decimal_scenario_A() public { + + bool zeroForOne = false; // ?? + + uint256 principalTokenDecimals = 18; + uint256 collateralTokenDecimals = 6; + + + + mockUniswapPool.set_mockSqrtPriceX96(1 * 2**96); + + uint32 twapInterval = 0; + + //decimals dont affect raw ratios + IUniswapPricingLibraryV2.PoolRouteConfig + memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: collateralTokenDecimals, + token1Decimals: principalTokenDecimals + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPool(routeConfig); + + + + assertEq( 1e18 , priceRatio , "unexpected price ratio") ; + } + + + function test_getUniswapPriceRatioForPoolRoutes_decimal_scenario_B() + public + { + mockUniswapPool.set_mockSqrtPriceX96(1 * 2**96); + + mockUniswapPoolSecondary.set_mockSqrtPriceX96(1 * 2**96); + + uint32 twapInterval = 0; //for now + + bool zeroForOne = true; + + uint256 principalTokenDecimals = 18; + uint256 intermediateTokenDecimals = 18; + uint256 collateralTokenDecimals = 6; + + + + IUniswapPricingLibraryV2.PoolRouteConfig[] + memory poolRoutes = new IUniswapPricingLibraryV2.PoolRouteConfig[]( + 2 + ); + + poolRoutes[0] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: collateralTokenDecimals, + token1Decimals: intermediateTokenDecimals + }); + + poolRoutes[1] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPoolSecondary), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: intermediateTokenDecimals, + token1Decimals: principalTokenDecimals + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPoolRoutes(poolRoutes); + + console.log("price ratio"); + console.logUint(priceRatio); + + assertEq( 1e18 , priceRatio , "unexpected price ratio") ; + } + + function test_getUniswapPriceRatioForPoolRoutes_decimal_scenario_C() + public + { + mockUniswapPool.set_mockSqrtPriceX96(1 * 2**96); + + mockUniswapPoolSecondary.set_mockSqrtPriceX96(1 * 2**96); + + uint32 twapInterval = 0; //for now + + bool zeroForOne = true; + + uint256 principalTokenDecimals = 6; + uint256 intermediateTokenDecimals = 18; + uint256 collateralTokenDecimals = 18; + + + IUniswapPricingLibraryV2.PoolRouteConfig[] + memory poolRoutes = new IUniswapPricingLibraryV2.PoolRouteConfig[]( + 2 + ); + + poolRoutes[0] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: collateralTokenDecimals, + token1Decimals: intermediateTokenDecimals + }); + + poolRoutes[1] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPoolSecondary), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: intermediateTokenDecimals, + token1Decimals: principalTokenDecimals + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPoolRoutes(poolRoutes); + + + console.log("price ratio"); + console.logUint(priceRatio); + + + assertEq( 1e18 , priceRatio , "unexpected price ratio") ; + } + + + function test_getUniswapPriceRatioForPoolRoutes_price_scenario_A() public { + mockUniswapPool.set_mockSqrtPriceX96(10 * 2**96); + + uint160 priceTwo = uint160(1 * 2**96) ; + mockUniswapPoolSecondary.set_mockSqrtPriceX96(priceTwo); + + uint32 twapInterval = 0; //for now + + bool zeroForOne = false; + + IUniswapPricingLibraryV2.PoolRouteConfig[] + memory poolRoutes = new IUniswapPricingLibraryV2.PoolRouteConfig[]( + 2 + ); + + poolRoutes[0] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + poolRoutes[1] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPoolSecondary), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPoolRoutes(poolRoutes); + + console.log("price ratio"); + console.logUint(priceRatio); + + + assertEq( 1e16 , priceRatio , "unexpected price ratio") ; + } + + + function test_getUniswapPriceRatioForPoolRoutes_price_scenario_B() public { + mockUniswapPool.set_mockSqrtPriceX96(1 * 2**96); + + uint32 twapInterval = 0; //for now + + bool zeroForOne = false; + + IUniswapPricingLibraryV2.PoolRouteConfig[] + memory poolRoutes = new IUniswapPricingLibraryV2.PoolRouteConfig[]( + 1 + ); + + poolRoutes[0] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPoolRoutes(poolRoutes); + + console.log("price ratio"); + console.logUint(priceRatio); + + + assertEq( 1e18 , priceRatio , "unexpected price ratio") ; + } + + +/* + Error: a == b not satisfied [uint] + Left: 1000000000000000000 + Right: 10000000000000000000000000000000000000000000000 + +*/ + function test_getUniswapPriceRatioForPoolRoutes_price_scenario_C() public { + mockUniswapPool.set_mockSqrtPriceX96(1 * 2**96); + + uint32 twapInterval = 0; //for now + + bool zeroForOne = true; + + IUniswapPricingLibraryV2.PoolRouteConfig[] + memory poolRoutes = new IUniswapPricingLibraryV2.PoolRouteConfig[]( + 1 + ); + + poolRoutes[0] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPoolRoutes(poolRoutes); + + console.log("price ratio"); + console.logUint(priceRatio); + + + assertEq( 1e18 , priceRatio , "unexpected price ratio") ; + + } + + function test_getUniswapPriceRatioForPoolRoutes_price_scenario_D() public { + mockUniswapPool.set_mockSqrtPriceX96(81128457937705300000000); + + uint32 twapInterval = 0; //for now + + bool zeroForOne = false; + + + IUniswapPricingLibraryV2.PoolRouteConfig[] + memory poolRoutes = new IUniswapPricingLibraryV2.PoolRouteConfig[]( + 1 + ); + + poolRoutes[0] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 6, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2 + .getUniswapPriceRatioForPoolRoutes(poolRoutes); + + console.log("price ratio"); + console.logUint(priceRatio); + + uint256 principalAmount = 1000; + + + assertEq( priceRatio , 953702069890566199069022917957 , "unexpected price ratio") ; + + } + + + + + +function test_getUniswapPriceRatioForPool_zeroPrice() public { + mockUniswapPool.set_mockSqrtPriceX96(0); + + uint32 twapInterval = 0; + bool zeroForOne = false; + + IUniswapPricingLibraryV2.PoolRouteConfig memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + vm.expectRevert(); + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig) ; +} + + +function test_getUniswapPriceRatioForPool_smallPrice() public { + // Setting a large sqrtPriceX96 to test upper bounds. + mockUniswapPool.set_mockSqrtPriceX96( 4295128739 ); + + uint32 twapInterval = 0; + bool zeroForOne = true; + + IUniswapPricingLibraryV2.PoolRouteConfig memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig, 1e18); + + + assertEq( priceRatio, 0 , "unexpected price ratio") ; + +} + + +function test_getUniswapPriceRatioForPool_largePrice() public { + // Setting a large sqrtPriceX96 to test upper bounds. + mockUniswapPool.set_mockSqrtPriceX96(type(uint160).max); + + uint32 twapInterval = 0; + bool zeroForOne = false; + + IUniswapPricingLibraryV2.PoolRouteConfig memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig); + + + assertEq( priceRatio, 0 , "unexpected price ratio") ; + +} + +function test_getUniswapPriceRatioForPool_invalidDecimals() public { + // Simulate mismatched decimals for edge case testing. + mockUniswapPool.set_mockSqrtPriceX96(1 * 2**96); + + uint32 twapInterval = 0; + bool zeroForOne = false; + + IUniswapPricingLibraryV2.PoolRouteConfig memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 0, // Invalid decimal value + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig) ; + + + + assertEq( priceRatio, 1000000000000000000 , "unexpected price ratio") ; + + +} + +function test_getUniswapPriceRatioForPool_twapInterval() public { + // Test with a valid twapInterval to ensure TWAP computation is working. + mockUniswapPool.set_mockSqrtPriceX96(2 * 2**96); + + uint32 twapInterval = 60; // 60 seconds TWAP + bool zeroForOne = false; + + IUniswapPricingLibraryV2.PoolRouteConfig memory routeConfig = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig); + + assert(priceRatio > 0); +} + +function test_getUniswapPriceRatioForPoolRoutes_twoPools_differentPrices() public { + mockUniswapPool.set_mockSqrtPriceX96(4 * 2**96); // Pool 1: sqrtPrice = 4 + mockUniswapPoolSecondary.set_mockSqrtPriceX96(2 * 2**96); // Pool 2: sqrtPrice = 2 + + uint32 twapInterval = 0; + bool zeroForOne = false; + + + IUniswapPricingLibraryV2.PoolRouteConfig[] + memory poolRoutes = new IUniswapPricingLibraryV2.PoolRouteConfig[]( + 2 + ); + + poolRoutes[0] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPool), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + poolRoutes[1] = IUniswapPricingLibraryV2.PoolRouteConfig({ + pool: address(mockUniswapPoolSecondary), + zeroForOne: zeroForOne, + twapInterval: twapInterval, + token0Decimals: 18, + token1Decimals: 18 + }); + + uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPoolRoutes(poolRoutes); + + //calculatet this more intelligently + uint256 expectedPriceRatio = 15625000000000000; + + assertEq(priceRatio, expectedPriceRatio, "Unexpected price ratio for two pools with different prices"); +} + + + + +} + + + + +contract LenderCommitmentForwarderTest_TellerV2Mock is TellerV2Context { + constructor() TellerV2Context(address(0)) {} + + function __setMarketRegistry(address _marketRegistry) external { + marketRegistry = IMarketRegistry(_marketRegistry); + } + + function getSenderForMarket(uint256 _marketId) + external + view + returns (address) + { + return _msgSenderForMarket(_marketId); + } + + function getDataForMarket(uint256 _marketId) + external + view + returns (bytes calldata) + { + return _msgDataForMarket(_marketId); + } +} diff --git a/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibrary_Test.sol b/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibrary_Test.sol index 28b70bd27..55ad8ea8f 100644 --- a/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibrary_Test.sol +++ b/packages/contracts/tests/SmartCommitmentForwarder/UniswapPricingLibrary_Test.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; //import "forge-std/Test.sol"; -import "../../contracts/libraries/UniswapPricingLibraryV2.sol"; +import "../../contracts/libraries/UniswapPricingLibrary.sol"; import { Testable } from "../Testable.sol"; @@ -42,82 +42,8 @@ contract UniswapPricingLibraryTest is Testable { } - - function test_getUniswapPriceRatioForPool_high_price() public { - - bool zeroForOne = true; // ?? - - mockUniswapPool.set_mockSqrtPriceX96( 1e40 ); - - uint32 twapInterval = 0; - - IUniswapPricingLibrary.PoolRouteConfig - memory routeConfig = IUniswapPricingLibrary.PoolRouteConfig({ - pool: address(mockUniswapPool), - zeroForOne: zeroForOne, - twapInterval: twapInterval, - token0Decimals: 18, - token1Decimals: 18 - }); - - uint256 priceRatio = UniswapPricingLibraryV2 - .getUniswapPriceRatioForPool(routeConfig); - - - - assertEq( 15930919111324522770288803977677118055911 , priceRatio , "unexpected price ratio") ; - } - - - function test_getUniswapPriceRatioForPool_low_price() public { - - bool zeroForOne = true; // ?? - - mockUniswapPool.set_mockSqrtPriceX96( 4295128739 ); - - uint32 twapInterval = 0; - - IUniswapPricingLibrary.PoolRouteConfig - memory routeConfig = IUniswapPricingLibrary.PoolRouteConfig({ - pool: address(mockUniswapPool), - zeroForOne: zeroForOne, - twapInterval: twapInterval, - token0Decimals: 18, - token1Decimals: 18 - }); - - uint256 priceRatio = UniswapPricingLibraryV2 - .getUniswapPriceRatioForPool(routeConfig); - - - - // assertEq( 0 , priceRatio , "unexpected price ratio") ; - } - - function test_getUniswapPriceRatioForPool_very_low_price() public { - - bool zeroForOne = true; // ?? - - mockUniswapPool.set_mockSqrtPriceX96( 42959 ); - - uint32 twapInterval = 0; - - IUniswapPricingLibrary.PoolRouteConfig - memory routeConfig = IUniswapPricingLibrary.PoolRouteConfig({ - pool: address(mockUniswapPool), - zeroForOne: zeroForOne, - twapInterval: twapInterval, - token0Decimals: 18, - token1Decimals: 18 - }); - - uint256 priceRatio = UniswapPricingLibraryV2 - .getUniswapPriceRatioForPool(routeConfig); - - - - // assertEq( 1e18 , priceRatio , "unexpected price ratio") ; - } + + @@ -138,7 +64,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPool(routeConfig); @@ -167,7 +93,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPool(routeConfig); @@ -200,7 +126,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: principalTokenDecimals }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPool(routeConfig); @@ -247,7 +173,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: principalTokenDecimals }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -293,7 +219,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: principalTokenDecimals }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolRoutes); @@ -336,7 +262,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -367,7 +293,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -404,7 +330,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -436,7 +362,7 @@ contract UniswapPricingLibraryTest is Testable { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2 + uint256 priceRatio = UniswapPricingLibrary .getUniswapPriceRatioForPoolRoutes(poolRoutes); console.log("price ratio"); @@ -445,8 +371,8 @@ contract UniswapPricingLibraryTest is Testable { uint256 principalAmount = 1000; - assertEq( 953702069890566199069022917957 , priceRatio , "unexpected price ratio") ; - + assertEq( priceRatio , 953702069891996282433059426929 , "unexpected price ratio") ; + } @@ -468,7 +394,7 @@ function test_getUniswapPriceRatioForPool_zeroPrice() public { }); vm.expectRevert(); - uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig) ; + uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPool(routeConfig) ; } function test_getUniswapPriceRatioForPool_largePrice() public { @@ -486,7 +412,7 @@ function test_getUniswapPriceRatioForPool_largePrice() public { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig); + uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPool(routeConfig); assertEq( priceRatio, 0 , "unexpected price ratio") ; @@ -508,7 +434,7 @@ function test_getUniswapPriceRatioForPool_invalidDecimals() public { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig) ; + uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPool(routeConfig) ; @@ -532,7 +458,7 @@ function test_getUniswapPriceRatioForPool_twapInterval() public { token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPool(routeConfig); + uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPool(routeConfig); assert(priceRatio > 0); } @@ -566,7 +492,7 @@ function test_getUniswapPriceRatioForPoolRoutes_twoPools_differentPrices() publi token1Decimals: 18 }); - uint256 priceRatio = UniswapPricingLibraryV2.getUniswapPriceRatioForPoolRoutes(poolRoutes); + uint256 priceRatio = UniswapPricingLibrary.getUniswapPriceRatioForPoolRoutes(poolRoutes); //calculatet this more intelligently uint256 expectedPriceRatio = 15625000000000000;