diff --git a/foundry.toml b/foundry.toml index 7357efcc..1123c9d8 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,6 +3,7 @@ solc = "0.8.23" #via_ir = true bytecode_hash = "ipfs" optimizer_runs = 1_000_000 +gas_limit = "18446744073709551615" libs = ['lib'] remappings = [ "@openzeppelin/=lib/openzeppelin-contracts/contracts/", diff --git a/script/unoptimized-deployer-meta b/script/unoptimized-deployer-meta index 50822360..bfcb6146 100644 --- a/script/unoptimized-deployer-meta +++ b/script/unoptimized-deployer-meta @@ -1,6 +1,6 @@ { - "constant_product_hash": "0xadeb5412c0cc65b7731f7190a28411dee2e89236f5e304ae49d1f90501134e06", - "factory_hash": "0x4e6ccdb29abe10d6374353935f08b1c008defaddab9aab32c6531161a3ed7826", - "oracle_caller_hash": "0xf31d810e0940d59eb76303615744d1837449efb8575274db894f243eddb2c304", - "stable_hash": "0xca90854439397b4f6bbaac64acdaaa1538802c2bc6c9951ac4a161e7c60a4528" + "constant_product_hash": "0x5c457dc7ab8cc4db0c7088c12fb2c35971517bc18be0788a37a31551f3acfb3c", + "factory_hash": "0xef85bf001813a3ca09b7d4c95f39f3d733812fcf381a373bd5beb89a748ddc5b", + "oracle_caller_hash": "0xb747bec5b4d21e05eff52d57f42c366b975aa81f8626d91205c6a3b31b853dc7", + "stable_hash": "0xe466a25862b9f9c9c3183f4c99f73af1e647bdd2feea892aff546338cccba39e" } \ No newline at end of file diff --git a/src/ReservoirDeployer.sol b/src/ReservoirDeployer.sol index c76030ea..c12616cd 100644 --- a/src/ReservoirDeployer.sol +++ b/src/ReservoirDeployer.sol @@ -17,12 +17,12 @@ contract ReservoirDeployer { uint256 public step = 0; // Bytecode hashes. - bytes32 public constant FACTORY_HASH = bytes32(0x84e232ec0f6ea2ec4c9e4cfa7e5469dab805cb65ffaab4a7ee9c7c602f91345a); + bytes32 public constant FACTORY_HASH = bytes32(0xae59efa1baf1113957e748f189c8eb9271c4a0d919c8af03d26ae4fbddb51e81); bytes32 public constant CONSTANT_PRODUCT_HASH = - bytes32(0xbd78fb88c65e9a8804773c3a0e0350627ae244d6daf312a549fc1e08ef9bf56e); - bytes32 public constant STABLE_HASH = bytes32(0x24174b50e2a4c46e25d5367496b6a2ca38bf33dceb07ef3aba32e2c1266a6bf1); + bytes32(0x356ae496acb4119a46481b3326102cb38281aad285e579d14598f6c5bb76f5e2); + bytes32 public constant STABLE_HASH = bytes32(0xa4bb872f2e22f611d0fd1ff88d960edc58283528282cb20aab4fbe14da557300); bytes32 public constant ORACLE_CALLER_HASH = - bytes32(0xed0f7d96dcba353321d583022005b03c09088f7de3800703dc8324b1da6c77a6); + bytes32(0xf130f5cb7eebcf57bac9f5d273fd8cc18dfb1b3f48d114a5e6a8230440d50e0f); // Deployment addresses. GenericFactory public factory; diff --git a/src/ReservoirPair.sol b/src/ReservoirPair.sol index adc1a989..98ac3c73 100644 --- a/src/ReservoirPair.sol +++ b/src/ReservoirPair.sol @@ -9,6 +9,7 @@ import { StdMath } from "src/libraries/StdMath.sol"; import { FactoryStoreLib } from "src/libraries/FactoryStore.sol"; import { Bytes32Lib } from "src/libraries/Bytes32.sol"; import { LogCompression } from "src/libraries/LogCompression.sol"; +import { Buffer } from "src/libraries/Buffer.sol"; import { IAssetManager, IERC20 } from "src/interfaces/IAssetManager.sol"; import { IAssetManagedPair } from "src/interfaces/IAssetManagedPair.sol"; @@ -25,6 +26,7 @@ abstract contract ReservoirPair is IAssetManagedPair, ReservoirERC20 { using SafeTransferLib for address; using StdMath for uint256; using FixedPointMathLib for uint256; + using Buffer for uint16; uint256 public constant MINIMUM_LIQUIDITY = 1e3; uint256 public constant FEE_ACCURACY = 1_000_000; // 100% @@ -93,7 +95,7 @@ abstract contract ReservoirPair is IAssetManagedPair, ReservoirERC20 { //////////////////////////////////////////////////////////////////////////*/ - Slot0 internal _slot0 = Slot0({ reserve0: 0, reserve1: 0, packedTimestamp: 0, index: type(uint16).max }); + Slot0 internal _slot0 = Slot0({ reserve0: 0, reserve1: 0, packedTimestamp: 0, index: Buffer.SIZE - 1}); function _currentTime() internal view returns (uint32) { return uint32(block.timestamp & 0x7FFFFFFF); @@ -608,7 +610,7 @@ abstract contract ReservoirPair is IAssetManagedPair, ReservoirERC20 { aPrevious.logAccRawPrice + aPrevious.logInstantRawPrice * int88(int256(uint256(aTimeElapsed))); int88 logAccClampedPrice = aPrevious.logAccClampedPrice + aPrevious.logInstantClampedPrice * int88(int256(uint256(aTimeElapsed))); - aSlot0.index += 1; + aSlot0.index = aSlot0.index.next(); _observations[aSlot0.index] = Observation( int24(aLogInstantRawPrice), int24(aLogInstantClampedPrice), diff --git a/src/libraries/Buffer.sol b/src/libraries/Buffer.sol new file mode 100644 index 00000000..079f7460 --- /dev/null +++ b/src/libraries/Buffer.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +pragma solidity ^0.8.0; + +library Buffer { + // The buffer is a circular storage structure with 2048 (2 ** 11) slots. + uint16 public constant SIZE = 2048; + + /** + * @dev Returns the index of the element before the one pointed by `index`. + */ + function prev(uint16 index) internal pure returns (uint16) { + return sub(index, 1); + } + + /** + * @dev Returns the index of the element after the one pointed by `index`. + */ + function next(uint16 index) internal pure returns (uint16) { + return add(index, 1); + } + + /** + * @dev Returns the index of an element `offset` slots after the one pointed by `index`. + */ + function add(uint16 index, uint16 offset) internal pure returns (uint16) { + unchecked { + return (index + offset) % SIZE; + } + } + + /** + * @dev Returns the index of an element `offset` slots before the one pointed by `index`. + */ + function sub(uint16 index, uint16 offset) internal pure returns (uint16) { + unchecked { + return (index + SIZE - offset) % SIZE; + } + } +} diff --git a/test/unit/OracleWriter.t.sol b/test/unit/OracleWriter.t.sol index 19302488..1a3b5f14 100644 --- a/test/unit/OracleWriter.t.sol +++ b/test/unit/OracleWriter.t.sol @@ -9,6 +9,7 @@ import { LogCompression } from "src/libraries/LogCompression.sol"; import { FactoryStoreLib } from "src/libraries/FactoryStore.sol"; import { ConstantProductOracleMath } from "src/libraries/ConstantProductOracleMath.sol"; import { StableOracleMath } from "src/libraries/StableOracleMath.sol"; +import { Buffer } from "src/libraries/Buffer.sol"; import { GenericFactory } from "src/GenericFactory.sol"; import { Constants } from "src/Constants.sol"; @@ -196,7 +197,7 @@ contract OracleWriterTest is BaseTest { // sanity (,,, uint16 lIndex) = _pair.getReserves(); Observation memory lObs = _oracleCaller.observation(_pair, lIndex); - assertEq(lIndex, type(uint16).max); + assertEq(lIndex, Buffer.SIZE - 1); assertEq(LogCompression.fromLowResLog(lObs.logInstantRawPrice), lOriginalPrice, "instant 1"); // act @@ -207,7 +208,7 @@ contract OracleWriterTest is BaseTest { (,,, lIndex) = _pair.getReserves(); lObs = _oracleCaller.observation(_pair, lIndex); - assertEq(lIndex, type(uint16).max); + assertEq(lIndex, Buffer.SIZE - 1); assertNotEq(LogCompression.fromLowResLog(lObs.logInstantRawPrice), lOriginalPrice); assertNotEq(LogCompression.fromLowResLog(lObs.logInstantClampedPrice), lOriginalPrice); diff --git a/test/unit/libraries/Buffer.t.sol b/test/unit/libraries/Buffer.t.sol new file mode 100644 index 00000000..40ce0676 --- /dev/null +++ b/test/unit/libraries/Buffer.t.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +import { Buffer } from "src/libraries/Buffer.sol"; + +contract BufferTest is Test { + using Buffer for uint16; + + function testPrev_AtLimit() external { + // arrange + uint16 lIndex = 0; + + // act & assert + assertEq(lIndex.prev(), Buffer.SIZE - 1); + } + + function testNext_AtLimit() external { + // arrange + uint16 lLimit = Buffer.SIZE - 1; + + // act & assert + assertEq(lLimit.next(), 0); + } + + function testNext_GreaterThanBufferSize(uint16 aStartingIndex) external { + // assume + uint16 lStartingIndex = uint16(bound(aStartingIndex, Buffer.SIZE, type(uint16).max)); + + // act & assert + unchecked { + assertEq(lStartingIndex.next(), (lStartingIndex + 1) % Buffer.SIZE); + } + assertLt(lStartingIndex.next(), Buffer.SIZE); // index returned always within bounds of Buffer.SIZE + } + + function testAdd_IndexGreaterThanBufferSize(uint16 aStartingIndex, uint16 aOffset) external { + // assume + uint16 lStartingIndex = uint16(bound(aStartingIndex, Buffer.SIZE, type(uint16).max)); + + // act + uint16 lResult = lStartingIndex.add(aOffset); + + // assert + assertLt(lResult, Buffer.SIZE); + } + + function testSub_IndexGreaterThanBufferSize(uint16 aStartingIndex, uint16 aOffset) external { + // assume + uint16 lStartingIndex = uint16(bound(aStartingIndex, Buffer.SIZE, type(uint16).max)); + + // act + uint16 lResult = lStartingIndex.sub(aOffset); + + // assert + assertLt(lResult, Buffer.SIZE); + } +}