From 0f955ab1faa91a74e1ac5f68b07cc6f35ec837f7 Mon Sep 17 00:00:00 2001 From: neodaoist Date: Fri, 4 Nov 2022 11:43:46 -0400 Subject: [PATCH 1/4] Cover totalSupply() check in newOptionType() --- src/OptionSettlement.sol | 31 ---------------------- test/OptionSettlement.t.sol | 53 +++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/OptionSettlement.sol b/src/OptionSettlement.sol index 70acfe2..1d35608 100644 --- a/src/OptionSettlement.sol +++ b/src/OptionSettlement.sol @@ -420,37 +420,6 @@ contract OptionSettlementEngine is ERC1155, IOptionSettlementEngine { // ********************************************************************** // INTERNAL HELPERS // ********************************************************************** - /** - * @dev Writes the specified number of options, transferring in the requisite - * underlying assets, and trasnsferring fungible ERC1155 tokens to caller. - * Reverts if insufficient underlying assets are not available from caller. - * @param _optionId The options to write. - * @param amount The amount of options to write. - */ - function _writeOptions(uint160 _optionId, uint112 amount) internal returns (Option storage) { - if (amount == 0) { - revert AmountWrittenCannotBeZero(); - } - - Option storage optionRecord = _option[_optionId]; - - if (optionRecord.expiryTimestamp <= block.timestamp) { - revert ExpiredOption(uint256(_optionId) << 96, optionRecord.expiryTimestamp); - } - - uint256 rxAmount = amount * optionRecord.underlyingAmount; - uint256 fee = ((rxAmount / 10000) * feeBps); - address underlyingAsset = optionRecord.underlyingAsset; - - // Transfer the requisite underlying asset - SafeTransferLib.safeTransferFrom(ERC20(underlyingAsset), msg.sender, address(this), (rxAmount + fee)); - - feeBalance[underlyingAsset] += fee; - - emit FeeAccrued(underlyingAsset, msg.sender, fee); - - return optionRecord; - } /// @dev Performs fair exercise assignment by pseudorandomly selecting a claim /// bucket between the intial creation of the option type and "today". The buckets diff --git a/test/OptionSettlement.t.sol b/test/OptionSettlement.t.sol index 9c5adc7..0d46fac 100644 --- a/test/OptionSettlement.t.sol +++ b/test/OptionSettlement.t.sol @@ -920,7 +920,7 @@ contract OptionSettlementTest is Test, NFTreceiver { }); } - function testRevertNewOptionTypeWhenInvalidAssets() public { + function testRevertNewOptionTypeWhenAssetsAreTheSame() public { vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidAssets.selector, DAI_A, DAI_A)); _newOption({ @@ -933,7 +933,56 @@ contract OptionSettlementTest is Test, NFTreceiver { }); } - // TODO test for Check total supplies and ensure the option will be exercisable + function testRevertNewOptionTypeWhenTotalSuppliesAreTooLowToExercise() public { + uint96 underlyingAmountExceedsTotalSupply = uint96(IERC20(DAI_A).totalSupply() + 1); + + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidAssets.selector, DAI_A, WETH_A)); + + _newOption({ + underlyingAsset: DAI_A, + exerciseTimestamp: testExerciseTimestamp, + expiryTimestamp: testExpiryTimestamp, + exerciseAsset: WETH_A, + underlyingAmount: underlyingAmountExceedsTotalSupply, + exerciseAmount: testExerciseAmount + }); + + uint96 exerciseAmountExceedsTotalSupply = uint96(IERC20(USDC_A).totalSupply() + 1); + + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidAssets.selector, USDC_A, WETH_A)); + + _newOption({ + underlyingAsset: USDC_A, + exerciseTimestamp: testExerciseTimestamp, + expiryTimestamp: testExpiryTimestamp, + exerciseAsset: WETH_A, + underlyingAmount: testUnderlyingAmount, + exerciseAmount: exerciseAmountExceedsTotalSupply + }); + } + + // write() + // L211 + // L277 + // L230 + // L263 + + // exercise() + // L297 + // L303 + // L307 + + // redeem() + // L341 + // L347 + // L353 + + // underlying() + // L392 + // L399–400 + + // _writeOptions() + function testFailAssignExercise() public { // Exercise an option before anyone has written it From 1ecc611caed8446eae0196dfc10f5b05e82016df Mon Sep 17 00:00:00 2001 From: neodaoist Date: Fri, 4 Nov 2022 16:56:56 -0400 Subject: [PATCH 2/4] More unit tests for uncovered reverts --- src/OptionSettlement.sol | 2 +- test/OptionSettlement.t.sol | 166 +++++++++++++++++++++++------------- 2 files changed, 106 insertions(+), 62 deletions(-) diff --git a/src/OptionSettlement.sol b/src/OptionSettlement.sol index 1d35608..08d662b 100644 --- a/src/OptionSettlement.sol +++ b/src/OptionSettlement.sol @@ -227,7 +227,7 @@ contract OptionSettlementEngine is ERC1155, IOptionSettlementEngine { revert InvalidOption(_optionIdU160b); } if (expiry <= block.timestamp) { - revert ExpiredOption(uint256(_optionIdU160b) << 96, expiry); + revert ExpiredOption(uint256(_optionIdU160b) << 96, expiry); // TODO measure gas savings and just use optionId } uint256 rxAmount = amount * optionRecord.underlyingAmount; diff --git a/test/OptionSettlement.t.sol b/test/OptionSettlement.t.sol index 0d46fac..098484a 100644 --- a/test/OptionSettlement.t.sol +++ b/test/OptionSettlement.t.sol @@ -960,7 +960,7 @@ contract OptionSettlementTest is Test, NFTreceiver { exerciseAmount: exerciseAmountExceedsTotalSupply }); } - + // write() // L211 // L277 @@ -981,50 +981,118 @@ contract OptionSettlementTest is Test, NFTreceiver { // L392 // L399–400 - // _writeOptions() + function testRevertWriteWhenInvalidOption() public { + uint256 invalidOptionId = testOptionId + 1; + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId) + ); - function testFailAssignExercise() public { - // Exercise an option before anyone has written it - vm.expectRevert(IOptionSettlementEngine.NoClaims.selector); - engine.exercise(testOptionId, 1); + engine.write(invalidOptionId, 1); } - function testFailWriteInvalidOption() public { - vm.expectRevert(IOptionSettlementEngine.InvalidOption.selector); - engine.write(testOptionId + 1, 1); + function testRevertWriteWhenClaimIdDoesNotEncodeOptionId() public { + uint256 option1Claim1 = engine.getTokenId(0xDEADBEEF1, 0xCAFECAFE1); + uint256 option2WithoutClaim = engine.getTokenId(0xDEADBEEF2, 0x0); + + vm.expectRevert( + abi.encodeWithSelector( + IOptionSettlementEngine.EncodedOptionIdInClaimIdDoesNotMatchProvidedOptionId.selector, + option1Claim1, + option2WithoutClaim + ) + ); + + engine.write(option2WithoutClaim, 1, option1Claim1); } - function testFailWriteExpiredOption() public { - vm.warp(testExpiryTimestamp); - vm.expectRevert(IOptionSettlementEngine.ExpiredOption.selector); + function testRevertWriteWhenAmountWrittenIsZero() public { + uint112 invalidWriteAmount = 0; + + vm.expectRevert(IOptionSettlementEngine.AmountWrittenCannotBeZero.selector); + + engine.write(testOptionId, invalidWriteAmount); } - function testFailExerciseBeforeExcercise() public { - (, IOptionSettlementEngine.Option memory option) = _newOption( - WETH_A, // underlyingAsset - testExerciseTimestamp + 1, // exerciseTimestamp - testExpiryTimestamp + 1, // expiryTimestamp - WETH_A, // exerciseAsset - testUnderlyingAmount, // underlyingAmount - testExerciseAmount // exerciseAmount + // TODO how can a user hit InvalidOption revert on write() L227 ? + + function testRevertWriteExpiredOption() public { + vm.warp(testExpiryTimestamp); + + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.ExpiredOption.selector, testOptionId, testExpiryTimestamp) ); - uint256 badOptionId = engine.newOptionType(option); + engine.write(testOptionId, 1); + } + + function testRevertWriteWhenWriterDoesNotOwnClaim() public { + vm.startPrank(ALICE); + uint256 claimId = engine.write(testOptionId, 1); + vm.stopPrank(); + + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId)); + + vm.prank(BOB); + engine.write(testOptionId, 1, claimId); + } + + // TODO how can a user redeem a claim (>= expiry) and write new options to the same contract ? + + // function testRevertWriteWhenWritingToLotAlreadyClaimed() public { + // vm.startPrank(ALICE); + // uint256 claimId = engine.write(testOptionId, 1); + + // vm.warp(testExerciseTimestamp + 1 seconds); + + // engine.redeem(claimId); + + // vm.expectRevert( + // abi.encodeWithSelector(IOptionSettlementEngine.AlreadyClaimed.selector, claimId) + // ); + + // engine.write(testOptionId, 1, claimId); + // vm.stopPrank(); + // } + + // TODO NoClaims error is not being thrown anywhere anymore + + // function testRevertExerciseFailAssignExercise() public { + // // Exercise an option before anyone has written it + // vm.expectRevert(IOptionSettlementEngine.NoClaims.selector); + // engine.exercise(testOptionId, 1); + // } + + function testRevertExerciseWhenBeforeExerciseTimestamp() public { // Alice writes vm.startPrank(ALICE); - engine.write(badOptionId, 1); - engine.safeTransferFrom(ALICE, BOB, badOptionId, 1, ""); + engine.write(testOptionId, 1); + engine.safeTransferFrom(ALICE, BOB, testOptionId, 1, ""); vm.stopPrank(); // Bob immediately exercises before exerciseTimestamp vm.startPrank(BOB); - vm.expectRevert(IOptionSettlementEngine.ExpiredOption.selector); - engine.exercise(badOptionId, 1); + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.ExerciseTooEarly.selector, testOptionId, testExerciseTimestamp) + ); + engine.exercise(testOptionId, 1); vm.stopPrank(); } - function testFailExerciseAtExpiry() public { + function testRevertExerciseWhenInvalidOptionId() public { + vm.startPrank(ALICE); + engine.write(testOptionId, 1); + + uint256 invalidOptionId = testOptionId + 1; + + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId) + ); + + engine.exercise(invalidOptionId, 1); + } + + function testRevertExerciseWhenAtExpiry() public { // Alice writes vm.startPrank(ALICE); engine.write(testOptionId, 1); @@ -1036,28 +1104,34 @@ contract OptionSettlementTest is Test, NFTreceiver { // Bob exercises vm.startPrank(BOB); - vm.expectRevert(IOptionSettlementEngine.ExpiredOption.selector); + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.ExpiredOption.selector, testOptionId, testExpiryTimestamp) + ); engine.exercise(testOptionId, 1); vm.stopPrank(); } - function testFailExerciseExpiredOption() public { + function testRevertExerciseWhenAfterExpiry() public { // Alice writes vm.startPrank(ALICE); engine.write(testOptionId, 1); engine.safeTransferFrom(ALICE, BOB, testOptionId, 1, ""); vm.stopPrank(); - // Fast-forward to after expiry - vm.warp(testExpiryTimestamp + 1); + // Fast-forward to at expiry + vm.warp(testExpiryTimestamp + 1 seconds); // Bob exercises vm.startPrank(BOB); - vm.expectRevert(IOptionSettlementEngine.ExpiredOption.selector); + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.ExpiredOption.selector, testOptionId, testExpiryTimestamp) + ); engine.exercise(testOptionId, 1); vm.stopPrank(); } + + function testFailRedeemInvalidClaim() public { vm.startPrank(ALICE); vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidClaim.selector, abi.encode(69))); @@ -1105,42 +1179,12 @@ contract OptionSettlementTest is Test, NFTreceiver { engine.redeem(claimId); } - function testWriteZeroOptionsFails() public { - vm.startPrank(ALICE); - vm.expectRevert(IOptionSettlementEngine.AmountWrittenCannotBeZero.selector); - engine.write(testOptionId, 0); - } - function testUriFailsWithTokenIdEncodingNonexistantOptionType() public { uint256 tokenId = 420; vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.TokenNotFound.selector, tokenId)); engine.uri(420); } - function testRevertIfClaimIdDoesNotEncodeOptionId() public { - uint256 option1Claim1 = engine.getTokenId(0xDEADBEEF1, 0xCAFECAFE1); - uint256 option2 = engine.getTokenId(0xDEADBEEF2, 0x0); - - vm.expectRevert( - abi.encodeWithSelector( - IOptionSettlementEngine.EncodedOptionIdInClaimIdDoesNotMatchProvidedOptionId.selector, - option1Claim1, - option2 - ) - ); - engine.write(option2, 1, option1Claim1); - } - - function testRevertIfWriterDoesNotOwnClaim() public { - vm.startPrank(ALICE); - uint256 claimId = engine.write(testOptionId, 1); - vm.stopPrank(); - - vm.startPrank(BOB); - vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId)); - engine.write(testOptionId, 1, claimId); - } - // ********************************************************************** // FUZZ TESTS // ********************************************************************** From 84598444fd7bd9a5e5bad2231b336af5cdad5bee Mon Sep 17 00:00:00 2001 From: neodaoist Date: Mon, 7 Nov 2022 11:59:46 -0500 Subject: [PATCH 3/4] Complete round of revert tests --- test/OptionSettlement.t.sol | 105 +++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/test/OptionSettlement.t.sol b/test/OptionSettlement.t.sol index 098484a..01f72ae 100644 --- a/test/OptionSettlement.t.sol +++ b/test/OptionSettlement.t.sol @@ -1015,6 +1015,7 @@ contract OptionSettlementTest is Test, NFTreceiver { } // TODO how can a user hit InvalidOption revert on write() L227 ? + // function testRevertWriteExpiredOption() public { vm.warp(testExpiryTimestamp); @@ -1037,8 +1038,8 @@ contract OptionSettlementTest is Test, NFTreceiver { engine.write(testOptionId, 1, claimId); } - // TODO how can a user redeem a claim (>= expiry) and write new options to the same contract ? - + // TODO how can a user redeem a claim (>= expiry) and write new options to the same contract, + // when we can't add new options to an expired contract ? // function testRevertWriteWhenWritingToLotAlreadyClaimed() public { // vm.startPrank(ALICE); // uint256 claimId = engine.write(testOptionId, 1); @@ -1056,7 +1057,6 @@ contract OptionSettlementTest is Test, NFTreceiver { // } // TODO NoClaims error is not being thrown anywhere anymore - // function testRevertExerciseFailAssignExercise() public { // // Exercise an option before anyone has written it // vm.expectRevert(IOptionSettlementEngine.NoClaims.selector); @@ -1128,57 +1128,102 @@ contract OptionSettlementTest is Test, NFTreceiver { ); engine.exercise(testOptionId, 1); vm.stopPrank(); - } + } - + function testRevertRedeemWhenInvalidClaim() public { + uint256 badClaimId = engine.getTokenId(0xDEADBEEF, 0); - function testFailRedeemInvalidClaim() public { - vm.startPrank(ALICE); - vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidClaim.selector, abi.encode(69))); - engine.redeem(69); + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.InvalidClaim.selector, badClaimId) + ); + + vm.prank(ALICE); + engine.redeem(badClaimId); } - function testFailRedeemBalanceTooLow() public { - // Alice writes and transfers to bob, then alice tries to redeem + function testRevertRedeemWhenBalanceTooLow() public { + // Alice writes and transfers to Bob, then Alice tries to redeem vm.startPrank(ALICE); uint256 claimId = engine.write(testOptionId, 1); - engine.safeTransferFrom(ALICE, BOB, testOptionId, 1, ""); - vm.expectRevert(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector); + engine.safeTransferFrom(ALICE, BOB, claimId, 1, ""); + + vm.warp(testExpiryTimestamp); + + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId + )); + engine.redeem(claimId); vm.stopPrank(); + // Carol feels left out and tries to redeem what she can't - vm.startPrank(CAROL); - vm.expectRevert(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector); + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId + )); + + vm.prank(CAROL); engine.redeem(claimId); - vm.stopPrank(); - // Bob redeems, which should burn, and then be unable to redeem a second time + + // Bob redeems, which burns the Claim NFT, and then is unable to redeem a second time vm.startPrank(BOB); engine.redeem(claimId); - vm.expectRevert(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector); - engine.redeem(claimId); - } - function testFailRedeemAlreadyClaimed() public { - vm.startPrank(ALICE); - uint256 claimId = engine.write(testOptionId, 1); - // write a second option so balance will be > 0 - engine.write(testOptionId, 1); - vm.warp(testExpiryTimestamp + 1); - engine.redeem(claimId); - vm.expectRevert(IOptionSettlementEngine.AlreadyClaimed.selector); + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId + )); + engine.redeem(claimId); + vm.stopPrank(); } - function testRedeemClaimTooSoon() public { + // TODO how can a user have balance != 0 of a claim which is burned ? + // in other words, the second claimId returned by write() is what would be the token + // of balance > 0, but that is not used here + + // Note I'm not sure AlreadyClaimed() error is reachable, either via redeem() or write() + + // function testRevertRedeemAlreadyClaimed() public { + // vm.startPrank(ALICE); + // uint256 claimId = engine.write(testOptionId, 1); + + // // write a second option so balance will be > 0 + // engine.write(testOptionId, 1); + + // vm.warp(testExpiryTimestamp); + + // engine.redeem(claimId); + + // vm.expectRevert( + // abi.encodeWithSelector(IOptionSettlementEngine.AlreadyClaimed.selector, claimId + // )); + + // engine.redeem(claimId); + // vm.stopPrank(); + // } + + function testRevertRedeemClaimTooSoon() public { vm.startPrank(ALICE); uint256 claimId = engine.write(testOptionId, 1); - vm.warp(testExerciseTimestamp - 1); + + vm.warp(testExerciseTimestamp - 1 seconds); + vm.expectRevert( abi.encodeWithSelector(IOptionSettlementEngine.ClaimTooSoon.selector, claimId, testExpiryTimestamp) ); + engine.redeem(claimId); } + function testRevertUnderlyingWhenNoOptionIsInitialized() public { + uint256 badOptionId = 123; + + vm.expectRevert( + abi.encodeWithSelector(IOptionSettlementEngine.TokenNotFound.selector, badOptionId) + ); + + engine.underlying(badOptionId); + } + function testUriFailsWithTokenIdEncodingNonexistantOptionType() public { uint256 tokenId = 420; vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.TokenNotFound.selector, tokenId)); From 2b339d9fd4157179d1aa83b15d2710bb34e24c3c Mon Sep 17 00:00:00 2001 From: neodaoist Date: Mon, 7 Nov 2022 12:18:42 -0500 Subject: [PATCH 4/4] TODOs, fmt, snapshot --- .gas-snapshot | 85 ++++++++++++++++++++----------------- test/OptionSettlement.t.sol | 51 +++++++--------------- 2 files changed, 61 insertions(+), 75 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 131f6bd..ae4330b 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,48 +1,53 @@ -OptionSettlementTest:testAddOptionsToExistingClaim() (gas: 405684) -OptionSettlementTest:testAssignMultipleBuckets() (gas: 618142) +OptionSettlementTest:testAddOptionsToExistingClaim() (gas: 405640) +OptionSettlementTest:testAssignMultipleBuckets() (gas: 618120) OptionSettlementTest:testEventExercise() (gas: 330225) -OptionSettlementTest:testEventNewOptionType() (gas: 122243) -OptionSettlementTest:testEventRedeem() (gas: 250690) -OptionSettlementTest:testEventSweepFeesWhenFeesAccruedForExercise() (gas: 1171282) -OptionSettlementTest:testEventSweepFeesWhenFeesAccruedForWrite() (gas: 1032675) -OptionSettlementTest:testEventWriteWhenExistingClaim() (gas: 275245) -OptionSettlementTest:testEventWriteWhenNewClaim() (gas: 253845) -OptionSettlementTest:testExerciseBeforeExpiry() (gas: 322900) -OptionSettlementTest:testExerciseIncompleteExercise() (gas: 342642) -OptionSettlementTest:testExerciseMultipleWriteSameChain() (gas: 454327) -OptionSettlementTest:testExerciseWithDifferentDecimals() (gas: 434011) -OptionSettlementTest:testFailAssignExercise() (gas: 13434) -OptionSettlementTest:testFailExerciseAtExpiry() (gas: 280372) -OptionSettlementTest:testFailExerciseBeforeExcercise() (gas: 15270) -OptionSettlementTest:testFailExerciseExpiredOption() (gas: 280494) -OptionSettlementTest:testFailNewOptionTypeExerciseWindowTooShort() (gas: 17721) -OptionSettlementTest:testFailNewOptionTypeOptionsTypeExists() (gas: 14468) -OptionSettlementTest:testFailRedeemAlreadyClaimed() (gas: 376656) -OptionSettlementTest:testFailRedeemBalanceTooLow() (gas: 278156) -OptionSettlementTest:testFailRedeemInvalidClaim() (gas: 13012) -OptionSettlementTest:testFailWriteExpiredOption() (gas: 5733) -OptionSettlementTest:testFailWriteInvalidOption() (gas: 10817) -OptionSettlementTest:testFuzzExercise(uint112,uint112) (runs: 256, μ: 354735, ~: 355902) -OptionSettlementTest:testFuzzGetDecodedIdComponents(uint256,uint256) (runs: 256, μ: 10888, ~: 10888) -OptionSettlementTest:testFuzzGetEncodedIdComponents(uint256,uint256) (runs: 256, μ: 10792, ~: 10792) +OptionSettlementTest:testEventNewOptionType() (gas: 122299) +OptionSettlementTest:testEventRedeem() (gas: 250712) +OptionSettlementTest:testEventSweepFeesWhenFeesAccruedForExercise() (gas: 1171370) +OptionSettlementTest:testEventSweepFeesWhenFeesAccruedForWrite() (gas: 1032763) +OptionSettlementTest:testEventWriteWhenExistingClaim() (gas: 275256) +OptionSettlementTest:testEventWriteWhenNewClaim() (gas: 253823) +OptionSettlementTest:testExerciseBeforeExpiry() (gas: 322966) +OptionSettlementTest:testExerciseIncompleteExercise() (gas: 342708) +OptionSettlementTest:testExerciseMultipleWriteSameChain() (gas: 454372) +OptionSettlementTest:testExerciseWithDifferentDecimals() (gas: 434066) +OptionSettlementTest:testFuzzExercise(uint112,uint112) (runs: 256, μ: 354691, ~: 355858) +OptionSettlementTest:testFuzzGetDecodedIdComponents(uint256,uint256) (runs: 256, μ: 10910, ~: 10910) +OptionSettlementTest:testFuzzGetEncodedIdComponents(uint256,uint256) (runs: 256, μ: 10725, ~: 10725) OptionSettlementTest:testFuzzNewOptionType(uint96,uint96,uint40,uint40) (runs: 256, μ: 124378, ~: 124378) OptionSettlementTest:testFuzzRedeem(uint112,uint112) (runs: 256, μ: 345500, ~: 346881) OptionSettlementTest:testFuzzWrite(uint112) (runs: 256, μ: 271611, ~: 271611) -OptionSettlementTest:testFuzzWriteExerciseRedeem(uint32) (runs: 256, μ: 9864745, ~: 9892668) -OptionSettlementTest:testGetDecodedIdComponents() (gas: 441394) -OptionSettlementTest:testGetEncodedIdComponents() (gas: 441036) -OptionSettlementTest:testGetOptionFromEncodedId() (gas: 125143) -OptionSettlementTest:testIsOptionInitialized() (gas: 125744) -OptionSettlementTest:testNewOptionTypeDisregardsNextClaimIdAndCreationTimestamp() (gas: 118650) -OptionSettlementTest:testNewOptionTypeInvalidAssets() (gas: 18476) -OptionSettlementTest:testRandomAssignment() (gas: 973148) -OptionSettlementTest:testRedeemClaimTooSoon() (gas: 251906) +OptionSettlementTest:testFuzzWriteExerciseRedeem(uint32) (runs: 256, μ: 9878325, ~: 9820743) +OptionSettlementTest:testGetDecodedIdComponents() (gas: 441449) +OptionSettlementTest:testGetEncodedIdComponents() (gas: 441047) +OptionSettlementTest:testGetOptionFromEncodedId() (gas: 125242) +OptionSettlementTest:testIsOptionInitialized() (gas: 125777) +OptionSettlementTest:testNewOptionTypeDisregardsNextClaimIdAndCreationTimestamp() (gas: 118672) +OptionSettlementTest:testRandomAssignment() (gas: 973103) OptionSettlementTest:testRedeemNotExercised() (gas: 254217) -OptionSettlementTest:testRevertIfClaimIdDoesNotEncodeOptionId() (gas: 11753) -OptionSettlementTest:testRevertIfWriterDoesNotOwnClaim() (gas: 256240) -OptionSettlementTest:testTokenURI() (gas: 480943) +OptionSettlementTest:testRevertExerciseWhenAfterExpiry() (gas: 261828) +OptionSettlementTest:testRevertExerciseWhenAtExpiry() (gas: 261660) +OptionSettlementTest:testRevertExerciseWhenBeforeExerciseTimestamp() (gas: 261266) +OptionSettlementTest:testRevertExerciseWhenInvalidOptionId() (gas: 247737) +OptionSettlementTest:testRevertNewOptionTypeWhenAssetsAreTheSame() (gas: 18498) +OptionSettlementTest:testRevertNewOptionTypeWhenExerciseWindowTooShort() (gas: 17662) +OptionSettlementTest:testRevertNewOptionTypeWhenExpiryTooSoon() (gas: 19600) +OptionSettlementTest:testRevertNewOptionTypeWhenOptionsTypeExists() (gas: 121822) +OptionSettlementTest:testRevertNewOptionTypeWhenTotalSuppliesAreTooLowToExercise() (gas: 46381) +OptionSettlementTest:testRevertRedeemClaimTooSoon() (gas: 251906) +OptionSettlementTest:testRevertRedeemWhenBalanceTooLow() (gas: 271878) +OptionSettlementTest:testRevertRedeemWhenInvalidClaim() (gas: 10646) +OptionSettlementTest:testRevertSetFeeToWhenNotCurrentFeeTo() (gas: 11808) +OptionSettlementTest:testRevertSetFeeToWhenZeroAddress() (gas: 11600) +OptionSettlementTest:testRevertUnderlyingWhenNoOptionIsInitialized() (gas: 11779) +OptionSettlementTest:testRevertWriteExpiredOption() (gas: 16558) +OptionSettlementTest:testRevertWriteWhenAmountWrittenIsZero() (gas: 10881) +OptionSettlementTest:testRevertWriteWhenClaimIdDoesNotEncodeOptionId() (gas: 11799) +OptionSettlementTest:testRevertWriteWhenInvalidOption() (gas: 11385) +OptionSettlementTest:testRevertWriteWhenWriterDoesNotOwnClaim() (gas: 256195) +OptionSettlementTest:testSetFeeTo() (gas: 15753) +OptionSettlementTest:testTokenURI() (gas: 480899) OptionSettlementTest:testUnderlyingAfterExercise() (gas: 361507) OptionSettlementTest:testUnderlyingWhenNotExercised() (gas: 256004) -OptionSettlementTest:testUriFailsWithTokenIdEncodingNonexistantOptionType() (gas: 18659) +OptionSettlementTest:testUriFailsWithTokenIdEncodingNonexistantOptionType() (gas: 18637) OptionSettlementTest:testWriteMultipleWriteSameOptionType() (gas: 384240) -OptionSettlementTest:testWriteZeroOptionsFails() (gas: 11316) diff --git a/test/OptionSettlement.t.sol b/test/OptionSettlement.t.sol index 01f72ae..412af7e 100644 --- a/test/OptionSettlement.t.sol +++ b/test/OptionSettlement.t.sol @@ -946,7 +946,7 @@ contract OptionSettlementTest is Test, NFTreceiver { underlyingAmount: underlyingAmountExceedsTotalSupply, exerciseAmount: testExerciseAmount }); - + uint96 exerciseAmountExceedsTotalSupply = uint96(IERC20(USDC_A).totalSupply() + 1); vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidAssets.selector, USDC_A, WETH_A)); @@ -984,9 +984,7 @@ contract OptionSettlementTest is Test, NFTreceiver { function testRevertWriteWhenInvalidOption() public { uint256 invalidOptionId = testOptionId + 1; - vm.expectRevert( - abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId) - ); + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId)); engine.write(invalidOptionId, 1); } @@ -1014,8 +1012,7 @@ contract OptionSettlementTest is Test, NFTreceiver { engine.write(testOptionId, invalidWriteAmount); } - // TODO how can a user hit InvalidOption revert on write() L227 ? - // + // TODO write() L227 function testRevertWriteExpiredOption() public { vm.warp(testExpiryTimestamp); @@ -1038,8 +1035,7 @@ contract OptionSettlementTest is Test, NFTreceiver { engine.write(testOptionId, 1, claimId); } - // TODO how can a user redeem a claim (>= expiry) and write new options to the same contract, - // when we can't add new options to an expired contract ? + // TODO write() L263 // function testRevertWriteWhenWritingToLotAlreadyClaimed() public { // vm.startPrank(ALICE); // uint256 claimId = engine.write(testOptionId, 1); @@ -1056,7 +1052,7 @@ contract OptionSettlementTest is Test, NFTreceiver { // vm.stopPrank(); // } - // TODO NoClaims error is not being thrown anywhere anymore + // TODO exercise() // function testRevertExerciseFailAssignExercise() public { // // Exercise an option before anyone has written it // vm.expectRevert(IOptionSettlementEngine.NoClaims.selector); @@ -1073,7 +1069,9 @@ contract OptionSettlementTest is Test, NFTreceiver { // Bob immediately exercises before exerciseTimestamp vm.startPrank(BOB); vm.expectRevert( - abi.encodeWithSelector(IOptionSettlementEngine.ExerciseTooEarly.selector, testOptionId, testExerciseTimestamp) + abi.encodeWithSelector( + IOptionSettlementEngine.ExerciseTooEarly.selector, testOptionId, testExerciseTimestamp + ) ); engine.exercise(testOptionId, 1); vm.stopPrank(); @@ -1085,9 +1083,7 @@ contract OptionSettlementTest is Test, NFTreceiver { uint256 invalidOptionId = testOptionId + 1; - vm.expectRevert( - abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId) - ); + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId)); engine.exercise(invalidOptionId, 1); } @@ -1128,14 +1124,12 @@ contract OptionSettlementTest is Test, NFTreceiver { ); engine.exercise(testOptionId, 1); vm.stopPrank(); - } + } function testRevertRedeemWhenInvalidClaim() public { uint256 badClaimId = engine.getTokenId(0xDEADBEEF, 0); - vm.expectRevert( - abi.encodeWithSelector(IOptionSettlementEngine.InvalidClaim.selector, badClaimId) - ); + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidClaim.selector, badClaimId)); vm.prank(ALICE); engine.redeem(badClaimId); @@ -1149,17 +1143,13 @@ contract OptionSettlementTest is Test, NFTreceiver { vm.warp(testExpiryTimestamp); - vm.expectRevert( - abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId - )); + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId)); engine.redeem(claimId); vm.stopPrank(); // Carol feels left out and tries to redeem what she can't - vm.expectRevert( - abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId - )); + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId)); vm.prank(CAROL); engine.redeem(claimId); @@ -1168,20 +1158,13 @@ contract OptionSettlementTest is Test, NFTreceiver { vm.startPrank(BOB); engine.redeem(claimId); - vm.expectRevert( - abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId - )); + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId)); engine.redeem(claimId); vm.stopPrank(); } - // TODO how can a user have balance != 0 of a claim which is burned ? - // in other words, the second claimId returned by write() is what would be the token - // of balance > 0, but that is not used here - - // Note I'm not sure AlreadyClaimed() error is reachable, either via redeem() or write() - + // TODO redeem() L353 // function testRevertRedeemAlreadyClaimed() public { // vm.startPrank(ALICE); // uint256 claimId = engine.write(testOptionId, 1); @@ -1217,9 +1200,7 @@ contract OptionSettlementTest is Test, NFTreceiver { function testRevertUnderlyingWhenNoOptionIsInitialized() public { uint256 badOptionId = 123; - vm.expectRevert( - abi.encodeWithSelector(IOptionSettlementEngine.TokenNotFound.selector, badOptionId) - ); + vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.TokenNotFound.selector, badOptionId)); engine.underlying(badOptionId); }