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

Remove the distribute asset parameter on settlement #43

Merged
merged 1 commit into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
98 changes: 62 additions & 36 deletions integration/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,49 @@ describe("Vault", function () {
await vaultInstance.connect(beneficialOwner).withdrawalAsset(0);
});

it("allows asset to be withdrawn by beneficialOwner", async function () {
const nowEpoch = Date.now() / 1000;
await testNFT
.connect(beneficialOwner)
["safeTransferFrom(address,address,uint256)"](
beneficialOwner.address,
vaultInstance.address,
1
);

expect(await vaultInstance.getBeneficialOwner(0)).eq(
beneficialOwner.address
);

expect(await vaultInstance.connect(beneficialOwner).withdrawalAsset(0))
.not.to.throw;

expect(await vaultInstance.getHoldsAsset(0)).to.be.false;
});

it("allows asset to be blocks withdrawals by others", async function () {
const nowEpoch = Date.now() / 1000;
await testNFT
.connect(beneficialOwner)
["safeTransferFrom(address,address,uint256)"](
beneficialOwner.address,
vaultInstance.address,
1
);

expect(await vaultInstance.getBeneficialOwner(0)).eq(
beneficialOwner.address
);

await expect(
vaultInstance.connect(runner).withdrawalAsset(0)
).to.be.revertedWith(
"withdrawalAsset -- only the beneficial owner can withdrawal an asset"
);

expect(await vaultInstance.getHoldsAsset(0)).to.be.true;
});

it("allows operator to clear entitlement and distribute", async function () {
const nowEpoch = Date.now() / 1000;
await testNFT
Expand Down Expand Up @@ -1087,7 +1130,9 @@ describe("Vault", function () {
runner.address,
"0x0000000000000000000000000000000000000000"
)
).to.be.revertedWith("assetIdIsZero -- this vault only supports asset id 0");
).to.be.revertedWith(
"assetIdIsZero -- this vault only supports asset id 0"
);
});

it("allows basic flashloans", async function () {
Expand Down Expand Up @@ -2005,12 +2050,11 @@ describe("Call Instrument Tests", function () {
const callFactoryFactory = await ethers.getContractFactory(
"HookCoveredCallFactory"
);
const tokenURILib = await ethers.getContractFactory(
"TokenURI"
)
const tokenURILib = await ethers.getContractFactory("TokenURI");
const tokenURI = await tokenURILib.deploy();
const callImplFactory = await ethers.getContractFactory(
"HookCoveredCallImplV1", {libraries: {TokenURI: tokenURI.address}}
"HookCoveredCallImplV1",
{ libraries: { TokenURI: tokenURI.address } }
);
const callBeaconFactory = await ethers.getContractFactory(
"HookUpgradeableBeacon"
Expand Down Expand Up @@ -2828,16 +2872,14 @@ describe("Call Instrument Tests", function () {
// Move forward to after auction period ends
await ethers.provider.send("evm_increaseTime", [4 * SECS_IN_A_DAY]);

const settleCall = calls.connect(writer).settleOption(tokenId, false);
const settleCall = calls.connect(writer).settleOption(tokenId);
await expect(settleCall).to.be.revertedWith(
"settle -- bid must be won by someone"
);
});

it("should not settle auction before expiration", async function () {
const settleCall = calls
.connect(writer)
.settleOption(optionTokenId, false);
const settleCall = calls.connect(writer).settleOption(optionTokenId);
await expect(settleCall).to.be.revertedWith(
"settle -- option must be expired"
);
Expand All @@ -2847,10 +2889,8 @@ describe("Call Instrument Tests", function () {
// Move forward to after auction period ends
await ethers.provider.send("evm_increaseTime", [1 * SECS_IN_A_DAY]);

await calls.connect(writer).settleOption(optionTokenId, false);
const settleCallAgain = calls
.connect(writer)
.settleOption(optionTokenId, false);
await calls.connect(writer).settleOption(optionTokenId);
const settleCallAgain = calls.connect(writer).settleOption(optionTokenId);
await expect(settleCallAgain).to.be.revertedWith(
"settle -- the call cannot already be settled"
);
Expand All @@ -2860,9 +2900,7 @@ describe("Call Instrument Tests", function () {
// Move forward to after auction period ends
await ethers.provider.send("evm_increaseTime", [1 * SECS_IN_A_DAY]);

const settleCall = calls
.connect(writer)
.settleOption(optionTokenId, false);
const settleCall = calls.connect(writer).settleOption(optionTokenId);
await expect(settleCall).to.emit(calls, "CallSettled");

const vaultAddress = await calls.getVaultAddress(optionTokenId);
Expand All @@ -2871,21 +2909,7 @@ describe("Call Instrument Tests", function () {
vaultAddress
);

expect(await vault.getBeneficialOwner(0)).to.eq(
writer.address
);
});

it("should settle auction and return nft", async function () {
// Move forward to after auction period ends
await ethers.provider.send("evm_increaseTime", [1 * SECS_IN_A_DAY]);

const settleCall = calls
.connect(writer)
.settleOption(optionTokenId, true);
await expect(settleCall).to.emit(calls, "CallSettled");

expect(await token.ownerOf(0)).to.eq(writer.address);
expect(await vault.getBeneficialOwner(0)).to.eq(writer.address);
});

it("should settle auction when option writer is high bidder", async function () {
Expand All @@ -2894,7 +2918,7 @@ describe("Call Instrument Tests", function () {

const settleCall = calls
.connect(secondBidder)
.settleOption(secondOptionTokenId, false);
.settleOption(secondOptionTokenId);
await expect(settleCall).to.emit(calls, "CallSettled");

const vaultAddress = await calls.getVaultAddress(secondOptionTokenId);
Expand All @@ -2903,9 +2927,7 @@ describe("Call Instrument Tests", function () {
vaultAddress
);

expect(await vault.getBeneficialOwner(0)).to.eq(
secondBidder.address
);
expect(await vault.getBeneficialOwner(0)).to.eq(secondBidder.address);
});
});

Expand Down Expand Up @@ -2956,7 +2978,7 @@ describe("Call Instrument Tests", function () {
await ethers.provider.send("evm_increaseTime", [1 * SECS_IN_A_DAY]);

// Settle option
await calls.connect(writer).settleOption(optionTokenId, false);
await calls.connect(writer).settleOption(optionTokenId);

const reclaimAsset = calls
.connect(writer)
Expand Down Expand Up @@ -3194,6 +3216,10 @@ describe("Call Instrument Tests", function () {
it("should get expiration", async function () {
expect(await calls.getExpiration(optionTokenId)).to.eq(expiration);
});

it("should have a tokenURI", async function () {
expect(await calls.tokenURI(optionTokenId)).to.not.be.null;
});
});

/*
Expand Down
11 changes: 2 additions & 9 deletions src/HookCoveredCallImplV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,7 @@ contract HookCoveredCallImplV1 is
// ----- END OF OPTION FUNCTIONS ---------//

/// @dev See {IHookCoveredCall-settleOption}.
function settleOption(uint256 optionId, bool returnNft)
external
nonReentrant
{
function settleOption(uint256 optionId) external nonReentrant {
CallOption storage call = optionParams[optionId];
require(
call.highBidder != address(0),
Expand Down Expand Up @@ -551,13 +548,9 @@ contract HookCoveredCallImplV1 is
_safeTransferETHWithFallback(call.writer, call.strike);
}

// return send option holder their earnings
// send option holder their earnings
_safeTransferETHWithFallback(optionOwner, spread);

if (returnNft) {
IHookVault(call.vaultAddress).withdrawalAsset(call.assetId);
}

emit CallSettled(optionId);
}

Expand Down
4 changes: 4 additions & 0 deletions src/HookERC721MultiVaultImplV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ contract HookERC721MultiVaultImplV1 is
!hasActiveEntitlement(assetId),
"withdrawalAsset -- the asset cannot be withdrawn with an active entitlement"
);
require(
assets[assetId].beneficialOwner == msg.sender,
"withdrawalAsset -- only the beneficial owner can withdrawal an asset"
);

_nftContract.safeTransferFrom(
address(this),
Expand Down
3 changes: 1 addition & 2 deletions src/interfaces/IHookCoveredCall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,7 @@ interface IHookCoveredCall is IERC721Metadata {
/// are subtracted from the distribution amounts.
///
/// @param optionId of the option to settle.
/// @param returnNft true if token should be withdrawn from vault, false to leave token in the vault.
function settleOption(uint256 optionId, bool returnNft) external;
function settleOption(uint256 optionId) external;

/// @notice Allows anyone to burn the instrument NFT for an expired option.
/// @param optionId of the option to burn.
Expand Down
6 changes: 1 addition & 5 deletions src/test/HookCoveredCallBiddingRevertTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,12 @@ contract HookCoveredCallBiddingRevertTests is HookProtocolTest {
// settle the auction
// assertTrue(token.ownerOf(underlyingTokenId) == address(calls), "call contract should own the token");
vm.warp(expiration + 3 seconds);
calls.settleOption(optionId, true);
calls.settleOption(optionId);

// verify the balances are correct
uint256 writerEndBalance = writer.balance;
uint256 buyerEndBalance = buyer.balance;

assertTrue(
token.ownerOf(underlyingTokenId) == bidder2,
"the high bidder should own the nft"
);
assertTrue(
writerEndBalance - writerStartBalance == 1000,
"the writer gets the strike price"
Expand Down
6 changes: 1 addition & 5 deletions src/test/HookCoveredCallIntegrationTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -163,16 +163,12 @@ contract HookCoveredCallIntegrationTest is HookProtocolTest {
// settle the auction
// assertTrue(token.ownerOf(underlyingTokenId) == address(calls), "call contract should own the token");
vm.warp(expiration + 3 seconds);
calls.settleOption(optionId, true);
calls.settleOption(optionId);

// verify the balances are correct
uint256 writerEndBalance = writer.balance;
uint256 buyerEndBalance = buyer.balance;

assertTrue(
token.ownerOf(underlyingTokenId) == bidder2,
"the high bidder should own the nft"
);
assertTrue(
writerEndBalance - writerStartBalance == 1000,
"the writer gets the strike price"
Expand Down
32 changes: 12 additions & 20 deletions src/test/HookCoveredCallTests.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
uint256 writerStartBalance = writer.balance;

vm.prank(writer);
calls.settleOption(optionTokenId, false);
calls.settleOption(optionTokenId);

assertTrue(
buyerStartBalance + (0.2 ether - 1000 wei) == buyer.balance,
Expand All @@ -942,21 +942,17 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
);
}

function testSettleOptionReturnNft() public {
function testSettleOption2() public {
uint256 buyerStartBalance = buyer.balance;
uint256 writerStartBalance = writer.balance;

IHookERC721Vault vault = vaultFactory.getVault(
address(token),
underlyingTokenId
);
vm.expectCall(
address(vault),
abi.encodeWithSignature("withdrawalAsset(uint32)", 0)
);

vm.prank(writer);
calls.settleOption(optionTokenId, true);
calls.settleOption(optionTokenId);

assertTrue(
buyerStartBalance + (0.2 ether - 1000 wei) == buyer.balance,
Expand All @@ -966,10 +962,6 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
writerStartBalance + 1000 wei == writer.balance,
"buyer should have received the option"
);
assertTrue(
token.ownerOf(underlyingTokenId) == address(secondBidder),
"secondBidder (winner) should get the underlying asset"
);
}

function testCannotSettleOptionNoWinningBid() public {
Expand All @@ -992,7 +984,7 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
// Option expires in 3 days from current block; bidding starts in 2 days.
vm.warp(block.timestamp + 3.1 days);
vm.expectRevert("settle -- bid must be won by someone");
calls.settleOption(optionId, true);
calls.settleOption(optionId);
}

function testCannotSettleOptionBeforeExpiration() public {
Expand All @@ -1017,15 +1009,15 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
calls.bid{value: 0.1 ether}(optionId);

vm.expectRevert("settle -- option must be expired");
calls.settleOption(optionId, true);
calls.settleOption(optionId);
}

function testCannotSettleSettledOption() public {
vm.prank(writer);
calls.settleOption(optionTokenId, false);
calls.settleOption(optionTokenId);

vm.expectRevert("settle -- the call cannot already be settled");
calls.settleOption(optionTokenId, true);
calls.settleOption(optionTokenId);
}

function testSettleOptionWhenWriterHighBidder() public {
Expand Down Expand Up @@ -1058,7 +1050,7 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
calls.bid{value: 1 wei}(optionId);
vm.warp(block.timestamp + 1 days);

calls.settleOption(optionId, false);
calls.settleOption(optionId);

assertTrue(
buyerStartBalance + 1 wei == buyer.balance,
Expand Down Expand Up @@ -1109,7 +1101,7 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
vm.warp(block.timestamp + 1 days);

vm.prank(writer);
calls.settleOption(optionId, false);
calls.settleOption(optionId);

assertTrue(
buyerStartBalance + 1000 wei == buyer.balance,
Expand Down Expand Up @@ -1161,7 +1153,7 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
vm.warp(block.timestamp + 1 days);

vm.prank(writer);
calls.settleOption(optionId, false);
calls.settleOption(optionId);

assertTrue(
buyerStartBalance + 2 wei == buyer.balance,
Expand Down Expand Up @@ -1216,7 +1208,7 @@ contract HookCoveredCallSettleTests is HookProtocolTest {
vm.warp(block.timestamp + 1 days);

vm.prank(writer);
calls.settleOption(optionId, false);
calls.settleOption(optionId);

assertTrue(
buyerStartBalance + 3 wei == buyer.balance,
Expand Down Expand Up @@ -1286,7 +1278,7 @@ contract HookCoveredCallReclaimTests is HookProtocolTest {
setUpOptionBids();

vm.startPrank(writer);
calls.settleOption(optionTokenId, false);
calls.settleOption(optionTokenId);

vm.expectRevert("reclaimAsset -- the option has already been settled");
calls.reclaimAsset(optionTokenId, true);
Expand Down