diff --git a/script/DeployPosm.s.sol b/script/DeployPosm.s.sol index c51c2ca4..1e969df2 100644 --- a/script/DeployPosm.s.sol +++ b/script/DeployPosm.s.sol @@ -17,11 +17,11 @@ contract DeployPosmTest is Script { address permit2, uint256 unsubscribeGasLimit, address wrappedNative, - string memory nativeCurrencyLabel + bytes32 nativeCurrencyLabelBytes ) public returns (IPositionDescriptor positionDescriptor, IPositionManager posm) { vm.startBroadcast(); - positionDescriptor = Deploy.positionDescriptor(poolManager, wrappedNative, nativeCurrencyLabel, hex"00"); + positionDescriptor = Deploy.positionDescriptor(poolManager, wrappedNative, nativeCurrencyLabelBytes, hex"00"); console2.log("PositionDescriptor", address(positionDescriptor)); posm = Deploy.positionManager( diff --git a/snapshots/PosMGasTest.json b/snapshots/PosMGasTest.json index fab212ff..61cbf0c4 100644 --- a/snapshots/PosMGasTest.json +++ b/snapshots/PosMGasTest.json @@ -34,8 +34,8 @@ "PositionManager_mint_withSettlePair": "420939", "PositionManager_multicall_initialize_mint": "458237", "PositionManager_permit": "79175", - "PositionManager_permit_secondPosition": "62063", - "PositionManager_permit_twice": "44975", + "PositionManager_permit_secondPosition": "62075", + "PositionManager_permit_twice": "44963", "PositionManager_subscribe": "87968", "PositionManager_unsubscribe": "62697", "position manager initcode hash (without constructor params, as uint256)": "81827502601055975118808937107769364319765058198432540518516360048852193272922", diff --git a/snapshots/PositionDescriptorTest.json b/snapshots/PositionDescriptorTest.json index df2ff3df..8ca090f1 100644 --- a/snapshots/PositionDescriptorTest.json +++ b/snapshots/PositionDescriptorTest.json @@ -1,4 +1,5 @@ { - "position descriptor initcode hash (without constructor params, as uint256)": "88482580191959945449130293370700011665153263709859488839371600440410373093991", - "positionDescriptor bytecode size": "24110" + "position descriptor initcode hash (without constructor params, as uint256)": "90002686278379416913385532438840669828848441637921711710845035729125835578244", + "positionDescriptor bytecode size": "24138", + "proxy bytecode size": "1488" } \ No newline at end of file diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 05fb0cc2..5b54ff6a 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -26,14 +26,27 @@ contract PositionDescriptor is IPositionDescriptor { address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; address public immutable wrappedNative; - string public nativeCurrencyLabel; + bytes32 public immutable nativeCurrencyLabelBytes; IPoolManager public immutable poolManager; - constructor(IPoolManager _poolManager, address _wrappedNative, string memory _nativeCurrencyLabel) { + constructor(IPoolManager _poolManager, address _wrappedNative, bytes32 _nativeCurrencyLabelBytes) { poolManager = _poolManager; wrappedNative = _wrappedNative; - nativeCurrencyLabel = _nativeCurrencyLabel; + nativeCurrencyLabelBytes = _nativeCurrencyLabelBytes; + } + + /// @notice Returns the native currency label as a string + function nativeCurrencyLabel() public view returns (string memory) { + uint256 len = 0; + while (len < 32 && nativeCurrencyLabelBytes[len] != 0) { + len++; + } + bytes memory b = new bytes(len); + for (uint256 i = 0; i < len; i++) { + b[i] = nativeCurrencyLabelBytes[i]; + } + return string(b); } /// @inheritdoc IPositionDescriptor @@ -66,8 +79,8 @@ contract PositionDescriptor is IPositionDescriptor { tokenId: tokenId, quoteCurrency: quoteCurrency, baseCurrency: baseCurrency, - quoteCurrencySymbol: SafeCurrencyMetadata.currencySymbol(quoteCurrency, nativeCurrencyLabel), - baseCurrencySymbol: SafeCurrencyMetadata.currencySymbol(baseCurrency, nativeCurrencyLabel), + quoteCurrencySymbol: SafeCurrencyMetadata.currencySymbol(quoteCurrency, nativeCurrencyLabel()), + baseCurrencySymbol: SafeCurrencyMetadata.currencySymbol(baseCurrency, nativeCurrencyLabel()), quoteCurrencyDecimals: SafeCurrencyMetadata.currencyDecimals(quoteCurrency), baseCurrencyDecimals: SafeCurrencyMetadata.currencyDecimals(baseCurrency), flipRatio: _flipRatio, diff --git a/src/interfaces/IPositionDescriptor.sol b/src/interfaces/IPositionDescriptor.sol index 92085217..fbaf73c6 100644 --- a/src/interfaces/IPositionDescriptor.sol +++ b/src/interfaces/IPositionDescriptor.sol @@ -32,6 +32,9 @@ interface IPositionDescriptor { /// @return The wrapped native token for this descriptor function wrappedNative() external view returns (address); + /// @return The native currency label bytes for this descriptor + function nativeCurrencyLabelBytes() external view returns (bytes32); + /// @return The native currency label for this descriptor function nativeCurrencyLabel() external view returns (string memory); diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index b215306b..0f4b4edc 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -26,6 +26,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { address public TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; address public WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; string public nativeCurrencyLabel = "ETH"; + bytes32 public nativeCurrencyLabelBytes = "ETH"; struct Token { string description; @@ -51,43 +52,51 @@ contract PositionDescriptorTest is Test, PosmTestSetup { vm.snapshotValue("positionDescriptor bytecode size", address(positionDescriptor).code.length); } + function test_bytecodeSize_proxy() public { + vm.snapshotValue("proxy bytecode size", address(proxyAsImplementation).code.length); + } + function test_setup_succeeds() public view { - assertEq(address(positionDescriptor.poolManager()), address(manager)); - assertEq(positionDescriptor.wrappedNative(), WETH9); - assertEq(positionDescriptor.nativeCurrencyLabel(), nativeCurrencyLabel); + assertEq(address(proxyAsImplementation.poolManager()), address(manager)); + assertEq(proxyAsImplementation.wrappedNative(), WETH9); + assertEq(proxyAsImplementation.nativeCurrencyLabelBytes(), nativeCurrencyLabelBytes); + } + + function test_nativeCurrencyLabel_succeeds() public view { + assertEq(proxyAsImplementation.nativeCurrencyLabel(), nativeCurrencyLabel); } function test_currencyRatioPriority_mainnet_succeeds() public { vm.chainId(1); - assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); - assertEq(positionDescriptor.currencyRatioPriority(address(0)), CurrencyRatioSortOrder.DENOMINATOR); - assertEq(positionDescriptor.currencyRatioPriority(USDC), CurrencyRatioSortOrder.NUMERATOR_MOST); - assertEq(positionDescriptor.currencyRatioPriority(USDT), CurrencyRatioSortOrder.NUMERATOR_MORE); - assertEq(positionDescriptor.currencyRatioPriority(DAI), CurrencyRatioSortOrder.NUMERATOR); - assertEq(positionDescriptor.currencyRatioPriority(TBTC), CurrencyRatioSortOrder.DENOMINATOR_MORE); - assertEq(positionDescriptor.currencyRatioPriority(WBTC), CurrencyRatioSortOrder.DENOMINATOR_MOST); - assertEq(positionDescriptor.currencyRatioPriority(makeAddr("ALICE")), 0); + assertEq(proxyAsImplementation.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(proxyAsImplementation.currencyRatioPriority(address(0)), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(proxyAsImplementation.currencyRatioPriority(USDC), CurrencyRatioSortOrder.NUMERATOR_MOST); + assertEq(proxyAsImplementation.currencyRatioPriority(USDT), CurrencyRatioSortOrder.NUMERATOR_MORE); + assertEq(proxyAsImplementation.currencyRatioPriority(DAI), CurrencyRatioSortOrder.NUMERATOR); + assertEq(proxyAsImplementation.currencyRatioPriority(TBTC), CurrencyRatioSortOrder.DENOMINATOR_MORE); + assertEq(proxyAsImplementation.currencyRatioPriority(WBTC), CurrencyRatioSortOrder.DENOMINATOR_MOST); + assertEq(proxyAsImplementation.currencyRatioPriority(makeAddr("ALICE")), 0); } function test_currencyRatioPriority_notMainnet_succeeds() public { - assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); - assertEq(positionDescriptor.currencyRatioPriority(address(0)), CurrencyRatioSortOrder.DENOMINATOR); - assertEq(positionDescriptor.currencyRatioPriority(USDC), 0); - assertEq(positionDescriptor.currencyRatioPriority(USDT), 0); - assertEq(positionDescriptor.currencyRatioPriority(DAI), 0); - assertEq(positionDescriptor.currencyRatioPriority(TBTC), 0); - assertEq(positionDescriptor.currencyRatioPriority(WBTC), 0); - assertEq(positionDescriptor.currencyRatioPriority(makeAddr("ALICE")), 0); + assertEq(proxyAsImplementation.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(proxyAsImplementation.currencyRatioPriority(address(0)), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(proxyAsImplementation.currencyRatioPriority(USDC), 0); + assertEq(proxyAsImplementation.currencyRatioPriority(USDT), 0); + assertEq(proxyAsImplementation.currencyRatioPriority(DAI), 0); + assertEq(proxyAsImplementation.currencyRatioPriority(TBTC), 0); + assertEq(proxyAsImplementation.currencyRatioPriority(WBTC), 0); + assertEq(proxyAsImplementation.currencyRatioPriority(makeAddr("ALICE")), 0); } function test_flipRatio_succeeds() public { vm.chainId(1); // bc price = token1/token0 - assertTrue(positionDescriptor.flipRatio(USDC, WETH9)); - assertFalse(positionDescriptor.flipRatio(DAI, USDC)); - assertFalse(positionDescriptor.flipRatio(WBTC, WETH9)); - assertFalse(positionDescriptor.flipRatio(WBTC, USDC)); - assertFalse(positionDescriptor.flipRatio(WBTC, DAI)); + assertTrue(proxyAsImplementation.flipRatio(USDC, WETH9)); + assertFalse(proxyAsImplementation.flipRatio(DAI, USDC)); + assertFalse(proxyAsImplementation.flipRatio(WBTC, WETH9)); + assertFalse(proxyAsImplementation.flipRatio(WBTC, USDC)); + assertFalse(proxyAsImplementation.flipRatio(WBTC, DAI)); } function test_tokenURI_succeeds() public { @@ -112,7 +121,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { // The prefix length is calculated by converting the string to bytes and finding its length uint256 prefixLength = bytes("data:application/json;base64,").length; - string memory uri = positionDescriptor.tokenURI(lpm, tokenId); + string memory uri = proxyAsImplementation.tokenURI(lpm, tokenId); // Convert the uri to bytes bytes memory uriBytes = bytes(uri); @@ -133,7 +142,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { } // quote is currency1, base is currency0 - assertFalse(positionDescriptor.flipRatio(Currency.unwrap(key.currency0), Currency.unwrap(key.currency1))); + assertFalse(proxyAsImplementation.flipRatio(Currency.unwrap(key.currency0), Currency.unwrap(key.currency1))); string memory symbol0 = SafeCurrencyMetadata.currencySymbol(Currency.unwrap(currency0), nativeCurrencyLabel); string memory symbol1 = SafeCurrencyMetadata.currencySymbol(Currency.unwrap(currency1), nativeCurrencyLabel); @@ -231,7 +240,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { // The prefix length is calculated by converting the string to bytes and finding its length uint256 prefixLength = bytes("data:application/json;base64,").length; - string memory uri = positionDescriptor.tokenURI(lpm, tokenId); + string memory uri = proxyAsImplementation.tokenURI(lpm, tokenId); // Convert the uri to bytes bytes memory uriBytes = bytes(uri); @@ -253,7 +262,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { // quote is currency1, base is currency0 assertFalse( - positionDescriptor.flipRatio(Currency.unwrap(nativeKey.currency0), Currency.unwrap(nativeKey.currency1)) + proxyAsImplementation.flipRatio(Currency.unwrap(nativeKey.currency0), Currency.unwrap(nativeKey.currency1)) ); string memory symbol0 = @@ -354,7 +363,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { vm.expectRevert(abi.encodeWithSelector(IPositionDescriptor.InvalidTokenId.selector, tokenId + 1)); - positionDescriptor.tokenURI(lpm, tokenId + 1); + proxyAsImplementation.tokenURI(lpm, tokenId + 1); } // Helper functions for testing purposes diff --git a/test/position-managers/PositionManager.modifyLiquidities.t.sol b/test/position-managers/PositionManager.modifyLiquidities.t.sol index 58392a9f..e82981d3 100644 --- a/test/position-managers/PositionManager.modifyLiquidities.t.sol +++ b/test/position-managers/PositionManager.modifyLiquidities.t.sol @@ -806,7 +806,7 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF // Set the fee on transfer amount 1% higher. (uint256 amount0, uint256 amount1) = - fotKey.currency0 == Currency.wrap(address(fotToken)) ? (100e18, 99e18) : (99e19, 100e18); + fotKey.currency0 == Currency.wrap(address(fotToken)) ? (100e18, 99e18) : (99e18, 100e18); Plan memory planner = Planner.init(); planner.add(Actions.SETTLE, abi.encode(fotKey.currency0, amount0, true)); @@ -821,7 +821,7 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF lpm.modifyLiquidities(actions, _deadline); (uint256 amount0AfterTransfer, uint256 amount1AfterTransfer) = - fotKey.currency0 == Currency.wrap(address(fotToken)) ? (99e18, 100e18) : (100e18, 99e19); + fotKey.currency0 == Currency.wrap(address(fotToken)) ? (99e18, 100e18) : (100e18, 99e18); uint128 newLiquidity = LiquidityAmounts.getLiquidityForAmounts( SQRT_PRICE_1_1, @@ -919,14 +919,14 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF // bool currency0IsFOT = fotKey.currency0 == Currency.wrap(address(fotToken)); // bool positionIsEntirelyInOtherToken = currency0IsFOT // ? tickUpper <= TickMath.getTickAtSqrtPrice(sqrtPriceX96) - // : tickLower > TickMath.getTickAtSqrtPrice(sqrtPriceX96); + // : tickLower >= TickMath.getTickAtSqrtPrice(sqrtPriceX96); // if (bips == 10000 && !positionIsEntirelyInOtherToken) { if ( bips == 10000 && !( fotKey.currency0 == Currency.wrap(address(fotToken)) ? tickUpper <= TickMath.getTickAtSqrtPrice(sqrtPriceX96) - : tickLower > TickMath.getTickAtSqrtPrice(sqrtPriceX96) + : tickLower >= TickMath.getTickAtSqrtPrice(sqrtPriceX96) ) ) { vm.expectRevert(Position.CannotUpdateEmptyPosition.selector); diff --git a/test/shared/Deploy.sol b/test/shared/Deploy.sol index 4b671d8c..cfb9a215 100644 --- a/test/shared/Deploy.sol +++ b/test/shared/Deploy.sol @@ -6,6 +6,7 @@ import {IPositionDescriptor} from "../../src/interfaces/IPositionDescriptor.sol" import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {IV4Quoter} from "../../src/interfaces/IV4Quoter.sol"; import {IStateView} from "../../src/interfaces/IStateView.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; library Deploy { Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); @@ -41,13 +42,25 @@ library Deploy { } } + function transparentUpgradeableProxy(address implementation, address admin, bytes memory data, bytes memory salt) + internal + returns (TransparentUpgradeableProxy proxy) + { + bytes memory args = abi.encode(implementation, admin, data); + bytes memory initcode = + abi.encodePacked(vm.getCode("TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy"), args); + assembly { + proxy := create2(0, add(initcode, 0x20), mload(initcode), salt) + } + } + function positionDescriptor( address poolManager, address wrappedNative, - string memory nativeCurrencyLabel, + bytes32 nativeCurrencyLabelBytes, bytes memory salt ) internal returns (IPositionDescriptor descriptor) { - bytes memory args = abi.encode(poolManager, wrappedNative, nativeCurrencyLabel); + bytes memory args = abi.encode(poolManager, wrappedNative, nativeCurrencyLabelBytes); bytes memory initcode = abi.encodePacked(vm.getCode("PositionDescriptor.sol:PositionDescriptor"), args); assembly { descriptor := create2(0, add(initcode, 0x20), mload(initcode), salt) diff --git a/test/shared/PosmTestSetup.sol b/test/shared/PosmTestSetup.sol index 8798220e..89c4491c 100644 --- a/test/shared/PosmTestSetup.sol +++ b/test/shared/PosmTestSetup.sol @@ -22,6 +22,7 @@ import {MockERC20} from "solmate/src/test/utils/mocks/MockERC20.sol"; import {SortTokens} from "@uniswap/v4-core/test/utils/SortTokens.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {PositionConfig} from "../shared/PositionConfig.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; /// @notice A shared test contract that wraps the v4-core deployers contract and exposes basic liquidity operations on posm. contract PosmTestSetup is Test, Deployers, DeployPermit2, LiquidityOperations { @@ -29,9 +30,12 @@ contract PosmTestSetup is Test, Deployers, DeployPermit2, LiquidityOperations { IAllowanceTransfer permit2; IPositionDescriptor public positionDescriptor; + TransparentUpgradeableProxy proxy; + IPositionDescriptor proxyAsImplementation; HookSavesDelta hook; address hookAddr = address(uint160(Hooks.AFTER_ADD_LIQUIDITY_FLAG | Hooks.AFTER_REMOVE_LIQUIDITY_FLAG)); IWETH9 public _WETH9 = IWETH9(address(new WETH())); + address governance = address(0xABCD); HookModifyLiquidities hookModifyLiquidities; address hookModifyLiquiditiesAddr = address( @@ -69,9 +73,11 @@ contract PosmTestSetup is Test, Deployers, DeployPermit2, LiquidityOperations { permit2 = IAllowanceTransfer(deployPermit2()); positionDescriptor = Deploy.positionDescriptor(address(poolManager), 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, "ETH", hex"00"); + proxy = Deploy.transparentUpgradeableProxy(address(positionDescriptor), governance, "", hex"03"); lpm = Deploy.positionManager( - address(poolManager), address(permit2), 100_000, address(positionDescriptor), address(_WETH9), hex"03" + address(poolManager), address(permit2), 100_000, address(proxy), address(_WETH9), hex"03" ); + proxyAsImplementation = IPositionDescriptor(address(proxy)); } function seedBalance(address to) internal {