Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Early redemption of fully assigned Claims #218

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions .solhint.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error",">=0.8.0"],
"no-inline-assembly": ["off",">=0.8.0"],
"avoid-low-level-calls": ["off",">=0.8.0"],
"not-rely-on-time": ["off",">=0.8.0"],
"func-visibility": ["warn",{"ignoreConstructors":true}]
}
}
"extends": "solhint:recommended",
"rules": {
"compiler-version": [
"error",
">=0.8.0"
],
"no-inline-assembly": [
"off",
">=0.8.0"
],
"avoid-low-level-calls": [
"off",
">=0.8.0"
],
"not-rely-on-time": [
"off",
">=0.8.0"
],
"no-global-import": "off",
"func-visibility": [
"warn",
{
"ignoreConstructors": true
}
]
}
}
2 changes: 2 additions & 0 deletions src/TokenURIGenerator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,8 @@ contract TokenURIGenerator is ITokenURIGenerator {
buffer[i] = ALPHABET[value & 0xf];
value >>= 4;
}

// solhint-disable custom-errors
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
Expand Down
9 changes: 5 additions & 4 deletions src/ValoremOptionsClearinghouse.sol
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ contract ValoremOptionsClearinghouse is ERC1155, IValoremOptionsClearinghouse {
}

/// @inheritdoc IValoremOptionsClearinghouse
function claim(uint256 claimId) external view returns (Claim memory claimInfo) {
0xAlcibiades marked this conversation as resolved.
Show resolved Hide resolved
function claim(uint256 claimId) public view returns (Claim memory claimInfo) {
(uint160 optionKey, uint96 claimKey) = _decodeTokenId(claimId);

if (!_isClaimInitialized(optionKey, claimKey)) {
Expand Down Expand Up @@ -516,12 +516,13 @@ contract ValoremOptionsClearinghouse is ERC1155, IValoremOptionsClearinghouse {
revert CallerDoesNotOwnClaimId(claimId);
}

// Setup pointers to the option and info.
// Setup pointers to the option and claim info.
OptionTypeState storage optionTypeState = optionTypeStates[optionKey];
Option memory optionRecord = optionTypeState.option;
Claim memory claimInfo = claim(claimId); // TODO can we combine this with Claim accounting below?

// Can't redeem until expiry.
if (optionRecord.expiryTimestamp > block.timestamp) {
// Can't redeem before expiry, unless Claim is fully assigned.
if (optionRecord.expiryTimestamp > block.timestamp && claimInfo.amountWritten > claimInfo.amountExercised) {
revert ClaimTooSoon(claimId, optionRecord.expiryTimestamp);
}

Expand Down
61 changes: 60 additions & 1 deletion test/ValoremOptionsClearinghouse-v1.1.unit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -247,5 +247,64 @@ contract ValoremOptionsClearinghousev11UnitTest is BaseClearinghouseTest {
// redeem() early
//////////////////////////////////////////////////////////////*/

// TODO
function test_redeem_whenBeforeExpiryAndClaimIsFullyAssigned() public {
vm.startPrank(ALICE);
uint256 claimId = engine.write(testOptionId, 10);
engine.safeTransferFrom(ALICE, BOB, testOptionId, 10, "");
vm.stopPrank();

vm.warp(testExerciseTimestamp);
vm.prank(BOB);
engine.exercise(testOptionId, 10);

// pre-redeem check
assertEq(
ERC20(testExerciseAsset).balanceOf(ALICE), STARTING_BALANCE, "Alice exercise asset balance, pre-redeem"
);

// vm.warp(testExpiryTimestamp);
vm.prank(ALICE);
engine.redeem(claimId);

// post-redeem check
uint256 expectedPostRedeemBalance = STARTING_BALANCE + (testExerciseAmount * 10);
assertEq(
ERC20(testExerciseAsset).balanceOf(ALICE),
expectedPostRedeemBalance,
"Alice exercise asset balance, post-redeem"
);
}

function testRevert_redeem_whenBeforeExpiryAndClaimIsUnassigned() public {
vm.startPrank(ALICE);
uint256 claimId = engine.write(testOptionId, 2);

vm.warp(testExpiryTimestamp - 1 seconds);

vm.expectRevert(
abi.encodeWithSelector(IValoremOptionsClearinghouse.ClaimTooSoon.selector, claimId, testExpiryTimestamp)
);

engine.redeem(claimId);
vm.stopPrank();
}

function testRevert_redeem_whenBeforeExpiryAndClaimIsPartiallyAssigned() public {
vm.startPrank(ALICE);
uint256 claimId = engine.write(testOptionId, 2);
engine.safeTransferFrom(ALICE, BOB, testOptionId, 2, "");
vm.stopPrank();

vm.warp(testExpiryTimestamp - 1 seconds);

vm.prank(BOB);
engine.exercise(testOptionId, 1);

vm.expectRevert(
abi.encodeWithSelector(IValoremOptionsClearinghouse.ClaimTooSoon.selector, claimId, testExpiryTimestamp)
);

vm.prank(ALICE);
engine.redeem(claimId);
}
}
20 changes: 3 additions & 17 deletions test/ValoremOptionsClearinghouse.unit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ contract ValoremOptionsClearinghouseUnitTest is BaseClearinghouseTest {
// function redeem(uint256 claimId) external
//////////////////////////////////////////////////////////////*/

function test_redeem_whenUnexercised() public {
function test_redeem_whenUnassigned() public {
uint112 amountWritten = 7;
uint256 expectedUnderlyingAmount = testUnderlyingAmount * amountWritten;
uint256 expectedUnderlyingFee = _calculateFee(expectedUnderlyingAmount);
Expand Down Expand Up @@ -792,7 +792,7 @@ contract ValoremOptionsClearinghouseUnitTest is BaseClearinghouseTest {
assertEq(DAILIKE.balanceOf(ALICE), STARTING_BALANCE, "Alice exercise post redeem"); // no change
}

function test_redeem_whenPartiallyExercised() public {
function test_redeem_whenPartiallyAssigned() public {
uint112 amountWritten = 7;
uint112 amountExercised = 3;
uint256 expectedUnderlyingFee = _calculateFee(testUnderlyingAmount);
Expand Down Expand Up @@ -874,7 +874,7 @@ contract ValoremOptionsClearinghouseUnitTest is BaseClearinghouseTest {
);
}

function test_redeem_whenFullyExercised() public {
function test_redeem_whenFullyAssigned() public {
uint112 amountWritten = 7;
uint112 amountExercised = 7;
uint256 expectedUnderlyingFee = _calculateFee(testUnderlyingAmount);
Expand Down Expand Up @@ -992,20 +992,6 @@ contract ValoremOptionsClearinghouseUnitTest is BaseClearinghouseTest {
vm.stopPrank();
}

function testRevert_redeem_whenClaimTooSoon() public {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should not go away, but rather be altered to catch the remaining cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It moved to the other 1.1 test file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking is it will be cleaner for the auditors to encapsulate all 1.1 functionality in its own test class. Open to otherwise tho.

vm.startPrank(ALICE);
uint256 claimId = engine.write(testOptionId, 1);

vm.warp(testExerciseTimestamp - 1 seconds);

vm.expectRevert(
abi.encodeWithSelector(IValoremOptionsClearinghouse.ClaimTooSoon.selector, claimId, testExpiryTimestamp)
);

engine.redeem(claimId);
vm.stopPrank();
}

/*//////////////////////////////////////////////////////////////
// function exercise(uint256 optionId, uint112 amount) external
//////////////////////////////////////////////////////////////*/
Expand Down