diff --git a/.forge-snapshots/BinHookTest#testBurnSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testBurnSucceedsWithHook.snap index 318a3319..5f00a595 100644 --- a/.forge-snapshots/BinHookTest#testBurnSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testBurnSucceedsWithHook.snap @@ -1 +1 @@ -180732 \ No newline at end of file +180720 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap index d0e2ab6c..007ac57c 100644 --- a/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap @@ -1 +1 @@ -184900 \ No newline at end of file +184922 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap index bcf21868..a7d487b8 100644 --- a/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap @@ -1 +1 @@ -137531 \ No newline at end of file +137556 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap index 0a4a95a6..b250e5b8 100644 --- a/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap @@ -1 +1 @@ -331216 \ No newline at end of file +331204 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testBurnNativeCurrency.snap b/.forge-snapshots/BinPoolManagerTest#testBurnNativeCurrency.snap index 036641c7..e4bcfdbc 100644 --- a/.forge-snapshots/BinPoolManagerTest#testBurnNativeCurrency.snap +++ b/.forge-snapshots/BinPoolManagerTest#testBurnNativeCurrency.snap @@ -1 +1 @@ -137002 \ No newline at end of file +136985 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testExtLoadPoolActiveId.snap b/.forge-snapshots/BinPoolManagerTest#testExtLoadPoolActiveId.snap index f9d12350..34f7d67c 100644 --- a/.forge-snapshots/BinPoolManagerTest#testExtLoadPoolActiveId.snap +++ b/.forge-snapshots/BinPoolManagerTest#testExtLoadPoolActiveId.snap @@ -1 +1 @@ -1824 \ No newline at end of file +1818 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testFuzzUpdateDynamicLPFee.snap b/.forge-snapshots/BinPoolManagerTest#testFuzzUpdateDynamicLPFee.snap index ae3136b0..b1516275 100644 --- a/.forge-snapshots/BinPoolManagerTest#testFuzzUpdateDynamicLPFee.snap +++ b/.forge-snapshots/BinPoolManagerTest#testFuzzUpdateDynamicLPFee.snap @@ -1 +1 @@ -32155 \ No newline at end of file +32143 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testFuzz_SetMaxBinStep.snap b/.forge-snapshots/BinPoolManagerTest#testFuzz_SetMaxBinStep.snap index 8e96cfd1..cfd59fed 100644 --- a/.forge-snapshots/BinPoolManagerTest#testFuzz_SetMaxBinStep.snap +++ b/.forge-snapshots/BinPoolManagerTest#testFuzz_SetMaxBinStep.snap @@ -1 +1 @@ -30405 \ No newline at end of file +30373 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasBurnHalfBin.snap b/.forge-snapshots/BinPoolManagerTest#testGasBurnHalfBin.snap index 84600bd3..2f74df6b 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasBurnHalfBin.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasBurnHalfBin.snap @@ -1 +1 @@ -146702 \ No newline at end of file +146680 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasBurnNineBins.snap b/.forge-snapshots/BinPoolManagerTest#testGasBurnNineBins.snap index 7f5b28ee..7fce43b8 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasBurnNineBins.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasBurnNineBins.snap @@ -1 +1 @@ -294281 \ No newline at end of file +294263 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap b/.forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap index bd102945..92fc94e7 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap @@ -1 +1 @@ -130194 \ No newline at end of file +130177 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap b/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap index 7830e9bd..8e9948ad 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap @@ -1 +1 @@ -119509 \ No newline at end of file +119453 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap index 59e7c167..cdaa3640 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap @@ -1 +1 @@ -976097 \ No newline at end of file +976031 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap index 03b74e65..a782a684 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap @@ -1 +1 @@ -338287 \ No newline at end of file +338221 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap index 94b56815..029c055a 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap @@ -1 +1 @@ -341796 \ No newline at end of file +341730 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap index 7181a3ec..618452c5 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap @@ -1 +1 @@ -144739 \ No newline at end of file +144673 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap index 7d283666..257754c9 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap @@ -1 +1 @@ -179097 \ No newline at end of file +179053 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap index aeb778af..6b16095b 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap @@ -1 +1 @@ -185080 \ No newline at end of file +185036 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap index 0576dd70..c5422946 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap @@ -1 +1 @@ -137535 \ No newline at end of file +137491 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap b/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap index a34474a5..76e1bf3d 100644 --- a/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap +++ b/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap @@ -1 +1 @@ -308275 \ No newline at end of file +308209 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testSetProtocolFee.snap b/.forge-snapshots/BinPoolManagerTest#testSetProtocolFee.snap index 2397c98f..820df218 100644 --- a/.forge-snapshots/BinPoolManagerTest#testSetProtocolFee.snap +++ b/.forge-snapshots/BinPoolManagerTest#testSetProtocolFee.snap @@ -1 +1 @@ -34487 \ No newline at end of file +34475 \ No newline at end of file diff --git a/.forge-snapshots/BitMathTest#leastSignificantBitMaxUint128.snap b/.forge-snapshots/BitMathTest#leastSignificantBitMaxUint128.snap index 9d3f4170..ee2b8364 100644 --- a/.forge-snapshots/BitMathTest#leastSignificantBitMaxUint128.snap +++ b/.forge-snapshots/BitMathTest#leastSignificantBitMaxUint128.snap @@ -1 +1 @@ -455 \ No newline at end of file +456 \ No newline at end of file diff --git a/.forge-snapshots/BitMathTest#leastSignificantBitMaxUint256.snap b/.forge-snapshots/BitMathTest#leastSignificantBitMaxUint256.snap index 2da43253..7d4983b9 100644 --- a/.forge-snapshots/BitMathTest#leastSignificantBitMaxUint256.snap +++ b/.forge-snapshots/BitMathTest#leastSignificantBitMaxUint256.snap @@ -1 +1 @@ -457 \ No newline at end of file +458 \ No newline at end of file diff --git a/.forge-snapshots/BitMathTest#leastSignificantBitSmallNumber.snap b/.forge-snapshots/BitMathTest#leastSignificantBitSmallNumber.snap index 13ef0a79..a21cae36 100644 --- a/.forge-snapshots/BitMathTest#leastSignificantBitSmallNumber.snap +++ b/.forge-snapshots/BitMathTest#leastSignificantBitSmallNumber.snap @@ -1 +1 @@ -453 \ No newline at end of file +454 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap index 9c70c736..4c9cc9d2 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap @@ -1 +1 @@ -360023 \ No newline at end of file +359979 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap index d20d8886..fd7435fc 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap @@ -1 +1 @@ -175191 \ No newline at end of file +175147 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap index 202906bc..f6844bb7 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap @@ -1 +1 @@ -245111 \ No newline at end of file +245092 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap b/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap index 092839d8..a85354e4 100644 --- a/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap +++ b/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap @@ -1 +1 @@ -168607 \ No newline at end of file +168497 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap b/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap index 9dc3012f..432b06b6 100644 --- a/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap +++ b/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap @@ -1 +1 @@ -112417 \ No newline at end of file +112332 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap b/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap index aff23368..4d2f5952 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap @@ -1 +1 @@ -137753 \ No newline at end of file +137731 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap b/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap index 8c915d81..322d4a43 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap @@ -1 +1 @@ -170905 \ No newline at end of file +170883 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap b/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap index f79b46ec..00afc8fe 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap @@ -1 +1 @@ -157667 \ No newline at end of file +157645 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testFuzzUpdateDynamicLPFee.snap b/.forge-snapshots/CLPoolManagerTest#testFuzzUpdateDynamicLPFee.snap index 87265ba2..b9188b90 100644 --- a/.forge-snapshots/CLPoolManagerTest#testFuzzUpdateDynamicLPFee.snap +++ b/.forge-snapshots/CLPoolManagerTest#testFuzzUpdateDynamicLPFee.snap @@ -1 +1 @@ -31820 \ No newline at end of file +31842 \ No newline at end of file diff --git a/.forge-snapshots/ExtsloadTest#extsloadInBatch.snap b/.forge-snapshots/ExtsloadTest#extsloadInBatch.snap index ffd3745f..916890ff 100644 --- a/.forge-snapshots/ExtsloadTest#extsloadInBatch.snap +++ b/.forge-snapshots/ExtsloadTest#extsloadInBatch.snap @@ -1 +1 @@ -10654 \ No newline at end of file +10676 \ No newline at end of file diff --git a/.forge-snapshots/ExtsloadTest#testExtsloadNSlot.snap b/.forge-snapshots/ExtsloadTest#testExtsloadNSlot.snap new file mode 100644 index 00000000..ee1f0104 --- /dev/null +++ b/.forge-snapshots/ExtsloadTest#testExtsloadNSlot.snap @@ -0,0 +1 @@ +14460 \ No newline at end of file diff --git a/.forge-snapshots/VaultTest#Vault.snap b/.forge-snapshots/VaultTest#Vault.snap index 293b04bb..56ecd7e5 100644 --- a/.forge-snapshots/VaultTest#Vault.snap +++ b/.forge-snapshots/VaultTest#Vault.snap @@ -1 +1 @@ -7124 \ No newline at end of file +7461 \ No newline at end of file diff --git a/.forge-snapshots/VaultTest#collectFee.snap b/.forge-snapshots/VaultTest#collectFee.snap index f1743106..8164d7ba 100644 --- a/.forge-snapshots/VaultTest#collectFee.snap +++ b/.forge-snapshots/VaultTest#collectFee.snap @@ -1 +1 @@ -25138 \ No newline at end of file +25135 \ No newline at end of file diff --git a/.forge-snapshots/VaultTest#lockSettledWhenAddLiquidity.snap b/.forge-snapshots/VaultTest#lockSettledWhenAddLiquidity.snap index a546fb0b..cb356e86 100644 --- a/.forge-snapshots/VaultTest#lockSettledWhenAddLiquidity.snap +++ b/.forge-snapshots/VaultTest#lockSettledWhenAddLiquidity.snap @@ -1 +1 @@ -81463 \ No newline at end of file +81428 \ No newline at end of file diff --git a/.forge-snapshots/VaultTest#lockSettledWhenFlashloan.snap b/.forge-snapshots/VaultTest#lockSettledWhenFlashloan.snap index 397eead4..8eeaea01 100644 --- a/.forge-snapshots/VaultTest#lockSettledWhenFlashloan.snap +++ b/.forge-snapshots/VaultTest#lockSettledWhenFlashloan.snap @@ -1 +1 @@ -121269 \ No newline at end of file +121297 \ No newline at end of file diff --git a/.forge-snapshots/VaultTest#lockSettledWhenMultiHopSwap.snap b/.forge-snapshots/VaultTest#lockSettledWhenMultiHopSwap.snap index 2e26a0a0..07db6236 100644 --- a/.forge-snapshots/VaultTest#lockSettledWhenMultiHopSwap.snap +++ b/.forge-snapshots/VaultTest#lockSettledWhenMultiHopSwap.snap @@ -1 +1 @@ -45005 \ No newline at end of file +44992 \ No newline at end of file diff --git a/.forge-snapshots/VaultTest#lockSettledWhenSwap.snap b/.forge-snapshots/VaultTest#lockSettledWhenSwap.snap index b8e90862..67bef2cf 100644 --- a/.forge-snapshots/VaultTest#lockSettledWhenSwap.snap +++ b/.forge-snapshots/VaultTest#lockSettledWhenSwap.snap @@ -1 +1 @@ -45004 \ No newline at end of file +44988 \ No newline at end of file diff --git a/.forge-snapshots/VaultTest#registerPoolManager.snap b/.forge-snapshots/VaultTest#registerPoolManager.snap index d3457db4..223e86f2 100644 --- a/.forge-snapshots/VaultTest#registerPoolManager.snap +++ b/.forge-snapshots/VaultTest#registerPoolManager.snap @@ -1 +1 @@ -47916 \ No newline at end of file +47913 \ No newline at end of file diff --git a/.forge-snapshots/VaultTest#testLock_NoOp.snap b/.forge-snapshots/VaultTest#testLock_NoOp.snap index 4aef5c16..09f6d7a8 100644 --- a/.forge-snapshots/VaultTest#testLock_NoOp.snap +++ b/.forge-snapshots/VaultTest#testLock_NoOp.snap @@ -1 +1 @@ -33072 \ No newline at end of file +33050 \ No newline at end of file diff --git a/src/Extsload.sol b/src/Extsload.sol index e448df60..e06e2225 100644 --- a/src/Extsload.sol +++ b/src/Extsload.sol @@ -18,6 +18,25 @@ abstract contract Extsload is IExtsload { } } + /// @inheritdoc IExtsload + function extsload(bytes32 startSlot, uint256 nSlots) external view returns (bytes memory) { + assembly ("memory-safe") { + // The abi offset of dynamic array in the returndata is 32. + mstore(0, 0x20) + // A left bit-shift of 5 is equivalent to multiplying by 32 but costs less gas. + mstore(0x20, shl(5, nSlots)) + let end := add(0x40, shl(5, nSlots)) + for { let memptr := 0x40 } 1 {} { + mstore(memptr, sload(startSlot)) + memptr := add(memptr, 0x20) + startSlot := add(startSlot, 1) + if iszero(lt(memptr, end)) { break } + } + // The end offset is also the length of the returndata. + return(0, end) + } + } + /// @inheritdoc IExtsload function extsload(bytes32[] calldata slots) external view returns (bytes32[] memory) { // since the function is external and enters a new call context and exits right diff --git a/src/Exttload.sol b/src/Exttload.sol new file mode 100644 index 00000000..5127d51d --- /dev/null +++ b/src/Exttload.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) 2024 PancakeSwap +pragma solidity ^0.8.24; + +import {IExttload} from "./interfaces/IExttload.sol"; + +/// @notice Enables public transient storage access for efficient state retrieval by external contracts. +/// https://eips.ethereum.org/EIPS/eip-2330#rationale +abstract contract Exttload is IExttload { + /// @inheritdoc IExttload + function exttload(bytes32 slot) external view returns (bytes32) { + assembly ("memory-safe") { + mstore(0, tload(slot)) + return(0, 0x20) + } + } + + /// @inheritdoc IExttload + function exttload(bytes32[] calldata slots) external view returns (bytes32[] memory) { + // since the function is external and enters a new call context and exits right + // after execution, Solidity's memory management convention can be disregarded + // and a direct slice of memory can be returned + assembly ("memory-safe") { + // Copy the abi offset of dynamic array and the length of the array to memory. + calldatacopy(0, 0x04, 0x40) + // A left bit-shift of 5 is equivalent to multiplying by 32 but costs less gas. + let end := add(0x40, shl(5, slots.length)) + let calldataptr := slots.offset + // Return values will start at 64 while calldata offset is 68. + for { let memptr := 0x40 } 1 {} { + mstore(memptr, tload(calldataload(calldataptr))) + memptr := add(memptr, 0x20) + calldataptr := add(calldataptr, 0x20) + if iszero(lt(memptr, end)) { break } + } + // The end offset is also the length of the returndata. + return(0, end) + } + } +} diff --git a/src/Vault.sol b/src/Vault.sol index 97bb4c29..ff971e3d 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -14,8 +14,9 @@ import {ILockCallback} from "./interfaces/ILockCallback.sol"; import {SafeCast} from "./libraries/SafeCast.sol"; import {VaultReserves} from "./libraries/VaultReserves.sol"; import {VaultToken} from "./VaultToken.sol"; +import {Exttload} from "./Exttload.sol"; -contract Vault is IVault, VaultToken, Ownable { +contract Vault is IVault, VaultToken, Ownable, Exttload { using SafeCast for *; using PoolIdLibrary for PoolKey; using CurrencyLibrary for Currency; diff --git a/src/interfaces/IExtsload.sol b/src/interfaces/IExtsload.sol index 1f39e418..adcfa9b5 100644 --- a/src/interfaces/IExtsload.sol +++ b/src/interfaces/IExtsload.sol @@ -7,6 +7,12 @@ interface IExtsload { /// @return value The value of the slot as bytes32 function extsload(bytes32 slot) external view returns (bytes32 value); + /// @notice Called by external contracts to access granular pool state + /// @param slot Key of slot to start sloading from + /// @param nSlots Number of slots to load into return value + /// @return value The value of the sload-ed slots concatenated as dynamic bytes + function extsload(bytes32 slot, uint256 nSlots) external view returns (bytes memory value); + /// @notice Called by external contracts to access sparse pool state /// @param slots List of slots to SLOAD from. /// @return values List of loaded values. diff --git a/src/interfaces/IExttload.sol b/src/interfaces/IExttload.sol new file mode 100644 index 00000000..4cb9c787 --- /dev/null +++ b/src/interfaces/IExttload.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +interface IExttload { + /// @notice Called by external contracts to access transient storage of the contract + /// @param slot Key of slot to tload + /// @return value The value of the slot as bytes32 + function exttload(bytes32 slot) external view returns (bytes32 value); + + /// @notice Called by external contracts to access sparse transient pool state + /// @param slots List of slots to tload + /// @return values List of loaded values + function exttload(bytes32[] calldata slots) external view returns (bytes32[] memory values); +} diff --git a/src/interfaces/IVault.sol b/src/interfaces/IVault.sol index 54cd98b1..bb086db0 100644 --- a/src/interfaces/IVault.sol +++ b/src/interfaces/IVault.sol @@ -7,8 +7,9 @@ import {PoolKey} from "../types/PoolKey.sol"; import {BalanceDelta} from "../types/BalanceDelta.sol"; import {IPoolManager} from "./IPoolManager.sol"; import {IVaultToken} from "./IVaultToken.sol"; +import {IExttload} from "./IExttload.sol"; -interface IVault is IVaultToken { +interface IVault is IVaultToken, IExttload { event PoolManagerRegistered(address indexed poolManager); /// @notice Thrown when a function is not called by a pool manager diff --git a/test/Extsload.t.sol b/test/Extsload.t.sol index 71093a78..463c408d 100644 --- a/test/Extsload.t.sol +++ b/test/Extsload.t.sol @@ -45,6 +45,23 @@ contract ExtsloadTest is Test, GasSnapshot { assertEq(abi.encode(slot3), abi.encode(address(0xabcd))); } + function testExtsloadNSlot() public { + snapStart("ExtsloadTest#testExtsloadNSlot"); + bytes memory data = poolManager.extsload(bytes32(uint256(0x00)), 4); + snapEnd(); + + address owner; + address protocolFeeController; + assembly { + owner := mload(add(data, 32)) + // slot 0x01 and 0x02 are mapping so skip for now + protocolFeeController := mload(add(data, 128)) + } + + assertEq(owner, address(this)); + assertEq(protocolFeeController, address(0xabcd)); + } + function testExtsloadInBatch() public { bytes32[] memory slots = new bytes32[](2); slots[0] = 0x00; diff --git a/test/Exttload.t.sol b/test/Exttload.t.sol new file mode 100644 index 00000000..0bab457e --- /dev/null +++ b/test/Exttload.t.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; +import "forge-std/Test.sol"; +import {Vault} from "../src/Vault.sol"; +import {IVault} from "../src/interfaces/IVault.sol"; +import {Exttload} from "../src/Exttload.sol"; +import {ILockCallback} from "../src/interfaces/ILockCallback.sol"; + +contract LockerLoadable is Exttload, ILockCallback { + bytes32 constant LOCKER_SLOT = bytes32(uint256(keccak256("SETTLEMENT_LOCKER")) - 1); + bytes32 constant UNSETTLED_DELTAS_COUNT = bytes32(uint256(keccak256("SETTLEMENT_UNSETTLEMENTD_DELTAS_COUNT")) - 1); + + bool extLoadSingle; + IVault vault; + + constructor(IVault _vault) { + vault = _vault; + } + + function extLoadLockerSingle() external { + extLoadSingle = true; + vault.lock("0x00"); + } + + function extLoadLockerBatch() external { + vault.lock("0x00"); + } + + function lockAcquired(bytes calldata data) external returns (bytes memory) { + if (extLoadSingle) { + address locker1 = address(uint160(uint256(vault.exttload(LOCKER_SLOT)))); + address locker2 = vault.getLocker(); + if (locker1 != locker2) revert(); + } else { + bytes32[] memory slots = new bytes32[](2); + slots[0] = UNSETTLED_DELTAS_COUNT; + slots[1] = LOCKER_SLOT; + bytes32[] memory values = vault.exttload(slots); + + // verify unsettled delta. Potentially make the test more robust with swaps so unsettedDeltaCount is non-zero + uint256 unsettledDelta1 = uint256(values[0]); + uint256 unsettledDelta2 = vault.getUnsettledDeltasCount(); + if (unsettledDelta1 != unsettledDelta2) revert(); + + // verify locker + address locker1 = address(uint160(uint256(values[1]))); + address locker2 = vault.getLocker(); + if (locker1 != locker2) revert(); + } + + return ""; + } +} + +contract ExttloadTest is Test, GasSnapshot { + LockerLoadable lockerLoadable; + IVault vault; + + function setUp() public { + IVault vault = new Vault(); + lockerLoadable = new LockerLoadable(vault); + } + + function testExttload_Single() public { + lockerLoadable.extLoadLockerSingle(); + } + + function testExttload_Batch() public { + lockerLoadable.extLoadLockerBatch(); + } +}