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

fix write logic bug by simplifying write logic #130

Merged
merged 5 commits into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
82 changes: 38 additions & 44 deletions src/OptionSettlementEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -290,77 +290,71 @@ contract OptionSettlementEngine is ERC1155, IOptionSettlementEngine {
}

/// @inheritdoc IOptionSettlementEngine
/// @dev Supplying claimId as 0 to the overloaded write signifies that a new
/// claim NFT should be minted for the options lot, rather than being added
/// as an existing claim.
function write(uint256 optionId, uint112 amount) external returns (uint256 claimId) {
return write(optionId, amount, 0);
}

/// @inheritdoc IOptionSettlementEngine
function write(uint256 optionId, uint112 amount, uint256 claimId) public returns (uint256) {
(uint160 optionKey, uint96 decodedClaimNum) = decodeTokenId(optionId);

// optionId must be zero in lower 96b for provided option Id
if (decodedClaimNum != 0) {
revert InvalidOption(optionId);
}

// claim provided must match the option provided
if (claimId != 0 && ((claimId >> 96) != (optionId >> 96))) {
revert EncodedOptionIdInClaimIdDoesNotMatchProvidedOptionId(claimId, optionId);
}

function write(uint256 tokenId, uint112 amount) public returns (uint256) {
// You need to write some amount
if (amount == 0) {
revert AmountWrittenCannotBeZero();
}

// Get the optionKey and claimNum from the tokenId
(uint160 optionKey, uint96 claimNum) = decodeTokenId(tokenId);

// Pass through the tokenId as the encodedClaimId
0xAlcibiades marked this conversation as resolved.
Show resolved Hide resolved
uint256 encodedClaimId = tokenId;

// Sanitize a zeroed encodedOptionId from the optionKey
uint256 encodedOptionId = uint256(optionKey) << 96;

// Get the option record and check that it's valid to write against
Option storage optionRecord = _option[optionKey];

// Make sure the option exists, and hasn't expired
uint40 expiry = optionRecord.expiryTimestamp;
if (expiry == 0) {
revert InvalidOption(optionKey);
revert InvalidOption(encodedOptionId);
}
if (expiry <= block.timestamp) {
revert ExpiredOption(optionId, expiry);
revert ExpiredOption(encodedOptionId, expiry);
}

// Calculate, record, and emit event for fee accrual on underlying asset
uint256 rxAmount = optionRecord.underlyingAmount * amount;
address underlyingAsset = optionRecord.underlyingAsset;

uint256 encodedClaimId = claimId;
if (claimId == 0) {
// create new claim
// Increment the next token ID
uint96 claimNum = optionRecord.nextClaimNum++;
encodedClaimId = encodeTokenId(optionKey, claimNum);
// Store info about the claim
// create new claim
if (claimNum == 0) {
// Make encodedClaimId reflect the next available claim and increment the next
// available claim in storage.
encodedClaimId = encodeTokenId(optionKey, optionRecord.nextClaimNum++);
// Store info about the claim.
_claim[encodedClaimId] = OptionLotClaim({amountWritten: amount, claimed: false});
} else {
// check ownership of claim
}
// Add to existing claim
else {
// Check ownership of claim
uint256 balance = balanceOf[msg.sender][encodedClaimId];
if (balance != 1) {
revert CallerDoesNotOwnClaimId(encodedClaimId);
}

// retrieve claim
OptionLotClaim storage existingClaim = _claim[encodedClaimId];

existingClaim.amountWritten += amount;
// Increment balance
_claim[encodedClaimId].amountWritten += amount;
}

// Handle internal claim bucket accounting
uint16 bucketIndex = _addOrUpdateClaimBucket(optionKey, amount);
_addOrUpdateClaimIndex(encodedClaimId, bucketIndex, amount);

// Calculate amount to receive
uint256 rxAmount = optionRecord.underlyingAmount * amount;

// Add underlying asset to stack
address underlyingAsset = optionRecord.underlyingAsset;

// Calculate Fee and emit events
uint256 fee = _calculateRecordAndEmitFee(underlyingAsset, rxAmount);
emit OptionsWritten(optionId, msg.sender, encodedClaimId, amount);
emit OptionsWritten(encodedOptionId, msg.sender, encodedClaimId, amount);

if (claimId == 0) {
if (claimNum == 0) {
// Mint options and claim token to writer
uint256[] memory tokens = new uint256[](2);
tokens[0] = optionId;
tokens[0] = encodedOptionId;
tokens[1] = encodedClaimId;

uint256[] memory amounts = new uint256[](2);
Expand All @@ -370,7 +364,7 @@ contract OptionSettlementEngine is ERC1155, IOptionSettlementEngine {
_batchMint(msg.sender, tokens, amounts, "");
} else {
// Mint more options on existing claim to writer
_mint(msg.sender, optionId, amount, "");
_mint(msg.sender, encodedOptionId, amount, "");
}

// Transfer the requisite underlying asset
Expand Down
23 changes: 2 additions & 21 deletions src/interfaces/IOptionSettlementEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,6 @@ interface IOptionSettlementEngine {
*/
error InvalidClaim(uint256 token);

/**
* @notice Provided claimId does not match provided option id in the upper 160b
* encoding the corresponding option ID for which the claim was written.
* @param claimId The provided claim ID.
* @param optionId The provided option ID.
*/
error EncodedOptionIdInClaimIdDoesNotMatchProvidedOptionId(uint256 claimId, uint256 optionId);

/**
* @notice The optionId specified expired at expiry.
* @param optionId The id of the expired option.
Expand Down Expand Up @@ -395,21 +387,10 @@ interface IOptionSettlementEngine {

/**
* @notice Writes a specified amount of the specified option, returning claim NFT id.
* @param optionId The desired option id to write.
* @param amount The desired number of options to write.
* @return claimId The claim NFT id for the option bundle.
*/
function write(uint256 optionId, uint112 amount) external returns (uint256 claimId);

/**
* @notice This override allows additional options to be written against a particular
* claim id.
* @param optionId The desired option id to write.
* @param tokenId The desired token id to write against, set lower 96 bytes to zero to mint a new claim NFT
* @param amount The desired number of options to write.
* @param claimId The claimId for the options lot to which the caller will add options
* @return claimId The claim NFT id for the option bundle.
*/
function write(uint256 optionId, uint112 amount, uint256 claimId) external returns (uint256);
function write(uint256 tokenId, uint112 amount) external returns (uint256 claimId);

/*//////////////////////////////////////////////////////////////
// Exercise Options
Expand Down
30 changes: 6 additions & 24 deletions test/OptionSettlementEngine.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ contract OptionSettlementTest is Test, NFTreceiver {
assertEq(2, engine.balanceOf(ALICE, testOptionId));

// write some more options, adding to existing claim
uint256 claimId3 = engine.write(testOptionId, 1, claimId);
uint256 claimId3 = engine.write(claimId, 1);
assertEq(claimId, claimId3);
assertEq(1, engine.balanceOf(ALICE, claimId3));
assertEq(3, engine.balanceOf(ALICE, testOptionId));
Expand Down Expand Up @@ -759,7 +759,7 @@ contract OptionSettlementTest is Test, NFTreceiver {
emit OptionsWritten(testOptionId, ALICE, claimId, 1);

vm.prank(ALICE);
engine.write(testOptionId, 1, claimId);
engine.write(claimId, 1);
}

function testEventExercise() public {
Expand Down Expand Up @@ -1107,30 +1107,12 @@ contract OptionSettlementTest is Test, NFTreceiver {
function testRevertWriteWhenInvalidOption() public {
// Option ID not 0 in lower 96 b
uint256 invalidOptionId = testOptionId + 1;
vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId));
engine.write(invalidOptionId, 1);

// Option ID not initialized
invalidOptionId = engine.encodeTokenId(0x1, 0x0);
vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId >> 96));
vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.InvalidOption.selector, invalidOptionId));
engine.write(invalidOptionId, 1);
}

function testRevertWriteWhenEncodedOptionIdInClaimIdDoesNotMatchProvidedOptionId() public {
uint256 option1Claim1 = engine.encodeTokenId(0xDEADBEEF1, 0xCAFECAFE1);
uint256 option2WithoutClaim = engine.encodeTokenId(0xDEADBEEF2, 0x0);

vm.expectRevert(
abi.encodeWithSelector(
IOptionSettlementEngine.EncodedOptionIdInClaimIdDoesNotMatchProvidedOptionId.selector,
option1Claim1,
option2WithoutClaim
)
);

engine.write(option2WithoutClaim, 1, option1Claim1);
}

function testRevertWriteWhenAmountWrittenCannotBeZero() public {
uint112 invalidWriteAmount = 0;

Expand Down Expand Up @@ -1173,7 +1155,7 @@ contract OptionSettlementTest is Test, NFTreceiver {
vm.expectRevert(abi.encodeWithSelector(IOptionSettlementEngine.CallerDoesNotOwnClaimId.selector, claimId));

vm.prank(BOB);
engine.write(testOptionId, 1, claimId);
engine.write(claimId, 1);
}

function testRevertWriteWhenExpiredOption() public {
Expand All @@ -1188,7 +1170,7 @@ contract OptionSettlementTest is Test, NFTreceiver {
abi.encodeWithSelector(IOptionSettlementEngine.ExpiredOption.selector, testOptionId, testExpiryTimestamp)
);

engine.write(testOptionId, 1, claimId);
engine.write(claimId, 1);
vm.stopPrank();
}

Expand Down Expand Up @@ -1633,7 +1615,7 @@ contract OptionSettlementTest is Test, NFTreceiver {
} else {
uint256 claimId = claimIds[_randBetween(seed++, claimIdLength)];
emit log_named_uint("ADD EXISTING CLAIM", claimId);
engine.write(optionId, toWrite, claimId);
engine.write(claimId, toWrite);
}

// add to total written
Expand Down