From e260cbfd296df50853517a16477c45c553191967 Mon Sep 17 00:00:00 2001 From: Alcibiades <89996683+0xAlcibiades@users.noreply.github.com> Date: Fri, 25 Mar 2022 19:38:54 -0400 Subject: [PATCH] Gas optimizations in storage structs --- .gas-snapshot | 12 ++++++ src/OptionSettlement.sol | 22 +++++------ src/test/OptionSettlement.t.sol | 70 ++++++++++++++++----------------- 3 files changed, 58 insertions(+), 46 deletions(-) create mode 100644 .gas-snapshot diff --git a/.gas-snapshot b/.gas-snapshot new file mode 100644 index 0000000..bd4971c --- /dev/null +++ b/.gas-snapshot @@ -0,0 +1,12 @@ +OptionSettlementTest:testExercise() (gas: 182892) +OptionSettlementTest:testFailDuplicateChain() (gas: 13642) +OptionSettlementTest:testFailExercise(uint112,uint112) (runs: 256, μ: 88228, ~: 111289) +OptionSettlementTest:testFailUri() (gas: 7693) +OptionSettlementTest:testFuzzExercise(uint112,uint112) (runs: 256, μ: 210415, ~: 217421) +OptionSettlementTest:testFuzzNewChain(uint160,uint96,uint96,uint40,uint40) (runs: 256, μ: 159428, ~: 159428) +OptionSettlementTest:testFuzzRedeem(uint112) (runs: 256, μ: 146653, ~: 146655) +OptionSettlementTest:testFuzzWrite(uint112) (runs: 256, μ: 174449, ~: 174449) +OptionSettlementTest:testNewChain() (gas: 148239) +OptionSettlementTest:testRedeem() (gas: 134716) +OptionSettlementTest:testUri() (gas: 10245) +OptionSettlementTest:testWrite() (gas: 170342) diff --git a/src/OptionSettlement.sol b/src/OptionSettlement.sol index 8ff63a6..2bc6cf1 100644 --- a/src/OptionSettlement.sol +++ b/src/OptionSettlement.sol @@ -31,17 +31,17 @@ struct Option { // The underlying asset to be received address underlyingAsset; // The timestamp after which this option may be exercised - uint64 exerciseTimestamp; + uint40 exerciseTimestamp; + // The timestamp before which this option must be exercised + uint40 expiryTimestamp; // The address of the asset needed for exercise address exerciseAsset; - // The timestamp before which this option must be exercised - uint64 expiryTimestamp; - // Random seed created at the time of option chain creation - uint256 settlementSeed; // The amount of the underlying asset contained within an option contract of this type - uint256 underlyingAmount; + uint96 underlyingAmount; + // Random seed created at the time of option chain creation + uint160 settlementSeed; // The amount of the exercise asset required to exercise this option - uint256 exerciseAmount; + uint96 exerciseAmount; } struct Claim { @@ -49,9 +49,9 @@ struct Claim { uint256 option; // These are 1:1 contracts with the underlying Option struct // The number of contracts written in this claim - uint256 amountWritten; + uint112 amountWritten; // The amount of contracts assigned for exercise to this claim - uint256 amountExercised; + uint112 amountExercised; // The two amounts above along with the option info, can be used to calculate the underlying assets bool claimed; } @@ -161,7 +161,7 @@ contract OptionSettlementEngine is ERC1155 { chainMap[chainKey] = true; } - function write(uint256 optionId, uint256 amount) external { + function write(uint256 optionId, uint112 amount) external { require(tokenType[optionId] == Type.Option, "Token is not an option"); require( option[optionId].settlementSeed != 0, @@ -199,7 +199,7 @@ contract OptionSettlementEngine is ERC1155 { tokens[1] = claimId; uint256[] memory amounts = new uint256[](2); - amounts[0] = amount; + amounts[0] = uint256(amount); amounts[1] = 1; bytes memory data = new bytes(0); diff --git a/src/test/OptionSettlement.t.sol b/src/test/OptionSettlement.t.sol index be60013..e56e06c 100644 --- a/src/test/OptionSettlement.t.sol +++ b/src/test/OptionSettlement.t.sol @@ -76,11 +76,11 @@ contract OptionSettlementTest is DSTest, NFTreceiver { Option memory info = Option({ underlyingAsset: address(weth), exerciseAsset: address(dai), - settlementSeed: 1, - underlyingAmount: 1 ether, - exerciseAmount: 3000 ether, - exerciseTimestamp: uint64(block.timestamp), - expiryTimestamp: (uint64(block.timestamp) + 604800) + settlementSeed: uint160(1), + underlyingAmount: uint96(1 ether), + exerciseAmount: uint96(3000 ether), + exerciseTimestamp: uint40(block.timestamp), + expiryTimestamp: (uint40(block.timestamp) + 604800) }); engine.newChain(info); @@ -106,31 +106,31 @@ contract OptionSettlementTest is DSTest, NFTreceiver { Option memory info = Option({ underlyingAsset: address(weth), exerciseAsset: address(dai), - settlementSeed: 0, - underlyingAmount: 1 ether, - exerciseAmount: 3100 ether, - exerciseTimestamp: uint64(block.timestamp), - expiryTimestamp: (uint64(block.timestamp) + 604800) + settlementSeed: uint160(1), + underlyingAmount: uint96(1 ether), + exerciseAmount: uint96(3100 ether), + exerciseTimestamp: uint40(block.timestamp), + expiryTimestamp: (uint40(block.timestamp) + 604800) }); uint256 tokenId = engine.newChain(info); ( , - uint64 testExerciseTimestamp, + uint40 testExerciseTimestamp, + uint40 testExpiryTimestamp, , - uint64 testExpiryTimestamp, - uint256 testSettlementSeed, - uint256 testUnderlyingAmount, - uint256 testExerciseAmount + uint96 testUnderlyingAmount, + uint160 testSettlementSeed, + uint96 testExerciseAmount ) = engine.option(nextTokenId); assertTrue(engine.chainMap(keccak256(abi.encode(info)))); assertEq(engine.nextTokenId(), nextTokenId + 1); assertEq(tokenId, engine.nextTokenId() - 1); - assertEq(testExerciseTimestamp, uint64(block.timestamp)); - assertEq(testExpiryTimestamp, (uint64(block.timestamp) + 604800)); + assertEq(testExerciseTimestamp, uint40(block.timestamp)); + assertEq(testExpiryTimestamp, (uint40(block.timestamp) + 604800)); assertEq(testUnderlyingAmount, 1 ether); assertEq(testExerciseAmount, 3100 ether); assertEq(testSettlementSeed, 42); @@ -140,19 +140,19 @@ contract OptionSettlementTest is DSTest, NFTreceiver { } function testFuzzNewChain( - uint256 settlementSeed, - uint256 underlyingAmount, - uint256 exerciseAmount, - uint64 exerciseTimestamp, - uint64 expiryTimestamp + uint160 settlementSeed, + uint96 underlyingAmount, + uint96 exerciseAmount, + uint40 exerciseTimestamp, + uint40 expiryTimestamp ) public { uint256 nextTokenId = engine.nextTokenId(); VM.assume(expiryTimestamp >= block.timestamp + 86400); VM.assume(exerciseTimestamp >= block.timestamp); VM.assume(exerciseTimestamp <= expiryTimestamp - 86400); - VM.assume(expiryTimestamp <= type(uint64).max); - VM.assume(exerciseTimestamp <= type(uint64).max); + VM.assume(expiryTimestamp <= type(uint40).max); + VM.assume(exerciseTimestamp <= type(uint40).max); VM.assume(underlyingAmount <= wethTotalSupply); VM.assume(exerciseAmount <= daiTotalSupply); VM.assume(type(uint256).max - underlyingAmount >= wethTotalSupply); @@ -172,12 +172,12 @@ contract OptionSettlementTest is DSTest, NFTreceiver { ( , - uint64 testExerciseTimestamp, + uint40 testExerciseTimestamp, + uint40 testExpiryTimestamp, , - uint64 testExpiryTimestamp, - uint256 testSettlementSeed, - uint256 testUnderlyingAmount, - uint256 testExerciseAmount + uint96 testUnderlyingAmount, + uint160 testSettlementSeed, + uint96 testExerciseAmount ) = engine.option(nextTokenId); assertTrue(engine.chainMap(keccak256(abi.encode(info)))); @@ -202,8 +202,8 @@ contract OptionSettlementTest is DSTest, NFTreceiver { settlementSeed: 1, underlyingAmount: 1 ether, exerciseAmount: 3000 ether, - exerciseTimestamp: uint64(block.timestamp), - expiryTimestamp: (uint64(block.timestamp) + 604800) + exerciseTimestamp: uint40(block.timestamp), + expiryTimestamp: (uint40(block.timestamp) + 604800) }); engine.newChain(info); } @@ -259,7 +259,7 @@ contract OptionSettlementTest is DSTest, NFTreceiver { assertTrue(true); } - function testFuzzWrite(uint256 amountWrite) public { + function testFuzzWrite(uint112 amountWrite) public { uint256 nextTokenId = engine.nextTokenId(); uint256 wethBalanceEngine = IERC20(weth).balanceOf(address(engine)); uint256 wethFeeTo = IERC20(weth).balanceOf(address(engine.feeTo())); @@ -345,7 +345,7 @@ contract OptionSettlementTest is DSTest, NFTreceiver { assertEq(engine.balanceOf(address(this), 1), 1); } - function testFuzzExercise(uint256 amountWrite, uint256 amountExercise) + function testFuzzExercise(uint112 amountWrite, uint112 amountExercise) public { // TODO(add checks after updating exercise()) @@ -405,7 +405,7 @@ contract OptionSettlementTest is DSTest, NFTreceiver { assertEq(engine.balanceOf(address(this), 1), 1); } - function testFailExercise(uint256 amountWrite, uint256 amountExercise) + function testFailExercise(uint112 amountWrite, uint112 amountExercise) public { VM.assume(amountExercise > amountWrite); @@ -441,7 +441,7 @@ contract OptionSettlementTest is DSTest, NFTreceiver { if (engine.tokenType(1) == Type.None) assertTrue(true); } - function testFuzzRedeem(uint256 amountWrite) public { + function testFuzzRedeem(uint112 amountWrite) public { // TODO(add checks after updating exercise()) uint256 wethBalanceEngine = IERC20(weth).balanceOf(address(engine)); uint256 daiBalanceEngine = IERC20(dai).balanceOf(address(engine));