diff --git a/src/HookCoveredCall.sol b/src/HookCoveredCall.sol index 8f263e3..71361a0 100644 --- a/src/HookCoveredCall.sol +++ b/src/HookCoveredCall.sol @@ -8,8 +8,6 @@ import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; /// future. Further, each covered call is mapped to a specific ERC-721 contract address -- meaning there is one covered /// call contract per collection. contract HookCoveredCall is BeaconProxy { - // TODO(HOOK-789)[GAS]: Explore implemeting the initialize function by setting storage slots on the - // newly deployed contract to avoid additional method calls. constructor( address beacon, address nftAddress, diff --git a/src/HookCoveredCallImplV1.sol b/src/HookCoveredCallImplV1.sol index 9fac387..2dacf6b 100644 --- a/src/HookCoveredCallImplV1.sol +++ b/src/HookCoveredCallImplV1.sol @@ -39,8 +39,8 @@ contract HookCoveredCallImplV1 is /// @notice The metadata for each covered call option /// @param writer The address of the writer that created the call option /// @param owner The address of the current owner of the underlying, updated as bidding occurs - /// @param tokenAddress The address of the NFT that the option is on - /// @param tokenId The tokenId of the NFT that the option is on + /// @param vaultAddress the address of the vault holding the underlying asset + /// @param assetId the asset id of the underlying within the vault /// @param strike The strike price to exercise the call option /// @param expiration The expiration time of the call option /// @param settled a flag that marks when a settlement action has taken place successfully @@ -49,6 +49,7 @@ contract HookCoveredCallImplV1 is struct CallOption { address writer; address vaultAddress; + uint256 assetId; uint256 strike; uint256 expiration; uint256 bid; @@ -71,9 +72,9 @@ contract HookCoveredCallImplV1 is /// @dev storage of all existing options contracts. mapping(uint256 => CallOption) public optionParams; - /// @dev the address of the ERC-721 contract that can serve as underlying assets for this + /// @dev the address of the token contract permitted to serve as underlying assets for this /// instrument. - address public allowedNftContract; + address public allowedUnderlyingAddress; /// @dev the address of WETH on the chain where this contract is deployed address public weth; @@ -131,7 +132,7 @@ contract HookCoveredCallImplV1 is _protocol = IHookProtocol(protocol); _erc721VaultFactory = IHookERC721VaultFactory(hookVaultFactory); weth = _protocol.getWETHAddress(); - allowedNftContract = nftContract; + allowedUnderlyingAddress = nftContract; /// Initialize basic configuration. /// Even though these are defaults, we cannot set them in the constructor because @@ -148,6 +149,7 @@ contract HookCoveredCallImplV1 is /// @dev See {IHookCoveredCall-mintWithVault}. function mintWithVault( address vaultAddress, + uint256 assetId, uint256 strikePrice, uint256 expirationTime, Signatures.Signature calldata signature @@ -155,57 +157,79 @@ contract HookCoveredCallImplV1 is IHookVault vault = IHookVault(vaultAddress); require( - allowedNftContract == vault.assetAddress(), + allowedUnderlyingAddress == vault.assetAddress(assetId), "mintWithVault -- token must be on the project allowlist" ); - require(vault.getHoldsAsset(), "mintWithVault-- asset must be in vault"); + require( + vault.getHoldsAsset(assetId), + "mintWithVault-- asset must be in vault" + ); // the beneficial owner is the only one able to impose entitlements, so // we need to require that they've done so here. - address writer = vault.getBeneficialOwner(); + address writer = vault.getBeneficialOwner(assetId); Entitlements.Entitlement memory entitlement = Entitlements.Entitlement({ beneficialOwner: writer, operator: address(this), vaultAddress: address(vault), + assetId: assetId, expiry: expirationTime }); vault.imposeEntitlement(entitlement, signature); return - _mintOptionWithVault(writer, address(vault), strikePrice, expirationTime); + _mintOptionWithVault( + writer, + address(vault), + assetId, + strikePrice, + expirationTime + ); } /// @dev See {IHookCoveredCall-mintWithEntitledVault}. function mintWithEntitledVault( address vaultAddress, + uint256 assetId, uint256 strikePrice, uint256 expirationTime ) external whenNotPaused returns (uint256) { IHookVault vault = IHookVault(vaultAddress); require( - allowedNftContract == vault.assetAddress(), + allowedUnderlyingAddress == vault.assetAddress(assetId), "mintWithVault -- token must be on the project allowlist" ); - require(vault.getHoldsAsset(), "mintWithVault-- asset must be in vault"); - (bool active, address operator) = vault.getCurrentEntitlementOperator(); + require( + vault.getHoldsAsset(assetId), + "mintWithVault-- asset must be in vault" + ); + (bool active, address operator) = vault.getCurrentEntitlementOperator( + assetId + ); require( active && operator == address(this), "mintWithVault -- call contact must be the entitled operator" ); require( - expirationTime == vault.entitlementExpiration(), + expirationTime == vault.entitlementExpiration(assetId), "mintWithVault -- entitlement expiration must match call expiration" ); // the beneficial owner owns the asset so // they should recieve the option. - address writer = vault.getBeneficialOwner(); + address writer = vault.getBeneficialOwner(assetId); return - _mintOptionWithVault(writer, vaultAddress, strikePrice, expirationTime); + _mintOptionWithVault( + writer, + vaultAddress, + assetId, + strikePrice, + expirationTime + ); } /// @dev See {IHookCoveredCall-mintWithErc721}. @@ -216,8 +240,9 @@ contract HookCoveredCallImplV1 is uint256 expirationTime ) external whenNotPaused returns (uint256) { address tokenOwner = IERC721(tokenAddress).ownerOf(tokenId); + uint256 assetId = 0; /// assume that the token is using an individual vault. require( - allowedNftContract == tokenAddress, + allowedUnderlyingAddress == tokenAddress, "mintWithErc721 -- token must be on the project allowlist" ); @@ -246,6 +271,7 @@ contract HookCoveredCallImplV1 is beneficialOwner: tokenOwner, operator: address(this), vaultAddress: vault, + assetId: assetId, /// assume that the asset within the vault has assetId 0 expiry: expirationTime }); @@ -260,11 +286,18 @@ contract HookCoveredCallImplV1 is // make sure that the vault actually has the asset. require( - IHookVault(vault).getHoldsAsset(), + IHookVault(vault).getHoldsAsset(assetId), "mintWithErc712 -- asset must be in vault" ); - return _mintOptionWithVault(tokenOwner, vault, strikePrice, expirationTime); + return + _mintOptionWithVault( + tokenOwner, + vault, + assetId, + strikePrice, + expirationTime + ); } /// @notice internal use function to record the option and mint it @@ -272,11 +305,13 @@ contract HookCoveredCallImplV1 is /// has a valid entitlement, and has the asset inside it /// @param writer the writer of the call option, usually the current owner of the underlying asset /// @param vaultAddress the address of the IHookVault which contains the underlying asset + /// @param assetId the id of the underlying asset /// @param strikePrice the strike price for this current option, in ETH /// @param expirationTime the time after which the option will be considered expired function _mintOptionWithVault( address writer, address vaultAddress, + uint256 assetId, uint256 strikePrice, uint256 expirationTime ) private returns (uint256) { @@ -294,6 +329,7 @@ contract HookCoveredCallImplV1 is optionParams[newOptionId] = CallOption({ writer: writer, vaultAddress: vaultAddress, + assetId: assetId, strike: strikePrice, expiration: expirationTime, bid: 0, @@ -313,6 +349,7 @@ contract HookCoveredCallImplV1 is emit CallCreated( writer, vaultAddress, + assetId, newOptionId, strikePrice, expirationTime @@ -373,7 +410,7 @@ contract HookCoveredCallImplV1 is // the new high bidder is the beneficial owner of the asset. // The beneficial owner must be set here instead of with a final bid // because the ability to - IHookVault(call.vaultAddress).setBeneficialOwner(msg.sender); + IHookVault(call.vaultAddress).setBeneficialOwner(call.assetId, msg.sender); // emit event emit Bid(optionId, bidAmt, msg.sender); @@ -429,7 +466,7 @@ contract HookCoveredCallImplV1 is _safeTransferETHWithFallback(ownerOf(optionId), spread); if (returnNft) { - IHookVault(call.vaultAddress).withdrawalAsset(); + IHookVault(call.vaultAddress).withdrawalAsset(call.assetId); } // burn nft @@ -481,17 +518,21 @@ contract HookCoveredCallImplV1 is _safeTransferETHWithFallback(call.highBidder, call.bid); // if we have a bid, we may have set the bidder, so make sure to revert it here. - IHookVault(call.vaultAddress).setBeneficialOwner(call.writer); + IHookVault(call.vaultAddress).setBeneficialOwner( + call.assetId, + call.writer + ); } if (returnNft) { // Because the call is not expired, we should be able to reclaim the asset from the vault if (call.expiration > block.timestamp) { IHookVault(call.vaultAddress).clearEntitlementAndDistribute( + call.assetId, call.writer ); } else { - IHookVault(call.vaultAddress).withdrawalAsset(); + IHookVault(call.vaultAddress).withdrawalAsset(call.assetId); } } @@ -507,9 +548,6 @@ contract HookCoveredCallImplV1 is /// the protocol. Bidders could bid in this settlement auction, and in the middle of the auction the writer /// could call this reclaim method. If they do that, they'll get their nft back _however_ there is no way for /// the current bidder to reclaim their money. - /// - /// TODO: To fix this, we're specifically sending that high bidder's money back; however, we should verify that - /// there are not patterns we need to watch here. } //// ---- Administrative Fns. @@ -593,11 +631,15 @@ contract HookCoveredCallImplV1 is '.base { fill: white; font-family: serif; font-size: 14px; }'; - parts[1] = HookStrings.toAsciiString(vault.assetAddress()); + parts[1] = HookStrings.toAsciiString( + vault.assetAddress(optionParams[tokenId].assetId) + ); parts[2] = ''; - parts[3] = HookStrings.toString(vault.assetTokenId()); + parts[3] = HookStrings.toString( + vault.assetTokenId(optionParams[tokenId].assetId) + ); parts[4] = ""; diff --git a/src/HookERC721VaultImplV1.sol b/src/HookERC721VaultImplV1.sol index 03514b7..0240edf 100644 --- a/src/HookERC721VaultImplV1.sol +++ b/src/HookERC721VaultImplV1.sol @@ -25,6 +25,8 @@ contract HookERC721VaultImplV1 is Initializable, ReentrancyGuard { + uint256 private constant ASSET_ID = 0; + /// ---------------- STORAGE ---------------- /// /// @dev these are the NFT contract address and tokenId the vault is covering @@ -65,7 +67,7 @@ contract HookERC721VaultImplV1 is /// @dev See {IHookERC721Vault-withdrawalAsset}. /// @dev withdrawals can only be performed by the beneficial owner if there are no entitlements - function withdrawalAsset() external { + function withdrawalAsset(uint256) external { // require(msg.sender == beneficialOwner, "the beneficial owner is the only one able to withdrawl"); require( !hasActiveEntitlement(), @@ -74,7 +76,7 @@ contract HookERC721VaultImplV1 is _nftContract.safeTransferFrom(address(this), beneficialOwner, _tokenId); - emit AssetWithdrawn(msg.sender, beneficialOwner); + emit AssetWithdrawn(ASSET_ID, msg.sender, beneficialOwner); } /// @dev See {IHookERC721Vault-imposeEntitlement}. @@ -166,7 +168,7 @@ contract HookERC721VaultImplV1 is "onERC721Received -- non-escrow asset returned when airdrops are disabled" ); } - emit AssetReceived(from, operator, msg.sender, tokenId); + emit AssetReceived(from, operator, msg.sender, tokenId, ASSET_ID); return this.onERC721Received.selector; } @@ -206,15 +208,15 @@ contract HookERC721VaultImplV1 is } /// @dev See {IHookERC721Vault-flashLoan}. - function flashLoan(address receiverAddress, bytes calldata params) - external - override - nonReentrant - { + function flashLoan( + uint256 assetId, + address receiverAddress, + bytes calldata params + ) external override nonReentrant { IERC721FlashLoanReceiver receiver = IERC721FlashLoanReceiver( receiverAddress ); - + require(assetId == ASSET_ID, "flashLoan -- invalid asset id"); require(receiverAddress != address(0), "flashLoan -- zero address"); require( msg.sender == beneficialOwner, @@ -261,7 +263,11 @@ contract HookERC721VaultImplV1 is /// @notice Looks up the address of the currently entitled operator /// @dev returns the null address if there is no active entitlement /// @return operator the address of the current operator - function entitledOperatorContract() external view returns (address operator) { + function entitledOperatorContract(uint256) + external + view + returns (address operator) + { if (!hasActiveEntitlement()) { return address(0); } else { @@ -270,7 +276,11 @@ contract HookERC721VaultImplV1 is } /// @dev See {IHookVault-entitlementExpiration}. - function entitlementExpiration() external view returns (uint256 expiry) { + function entitlementExpiration(uint256) + external + view + returns (uint256 expiry) + { if (!hasActiveEntitlement()) { return 0; } else { @@ -279,26 +289,28 @@ contract HookERC721VaultImplV1 is } /// @dev See {IHookERC721Vault-getBeneficialOwner}. - function getBeneficialOwner() external view returns (address) { + function getBeneficialOwner(uint256) external view returns (address) { return beneficialOwner; } /// @dev See {IHookERC721Vault-getHoldsAsset}. - function getHoldsAsset() external view returns (bool holdsAsset) { + function getHoldsAsset(uint256) external view returns (bool holdsAsset) { return _nftContract.ownerOf(_tokenId) == address(this); } - function assetAddress() external view returns (address) { + function assetAddress(uint256) external view returns (address) { return address(_nftContract); } - function assetTokenId() external view returns (uint256) { + function assetTokenId(uint256) external view returns (uint256) { return _tokenId; } /// @dev See {IHookERC721Vault-setBeneficialOwner}. /// setBeneficialOwner can only be called by the entitlementContract if there is an activeEntitlement. - function setBeneficialOwner(address newBeneficialOwner) external { + function setBeneficialOwner(uint256 assetId, address newBeneficialOwner) + external + { if (hasActiveEntitlement()) { require( msg.sender == _currentEntitlement.operator, @@ -310,12 +322,16 @@ contract HookERC721VaultImplV1 is "setBeneficialOwner -- only the current owner can update the beneficial owner" ); } + require( + assetId == ASSET_ID, + "setBeneficialOwner -- this contract only contains one asset" + ); _setBeneficialOwner(newBeneficialOwner); } /// @dev See {IHookERC721Vault-clearEntitlement}. /// @dev This can only be called if an entitlement currently exists, otherwise it would be a no-op - function clearEntitlement() public { + function clearEntitlement(uint256) public { require( hasActiveEntitlement(), "clearEntitlement -- an active entitlement must exist" @@ -332,7 +348,7 @@ contract HookERC721VaultImplV1 is /// intended reciever, which should match the beneficialOwner. The function will throw if /// the reciever and owner do not match. /// @param reciever the intended reciever of the asset - function clearEntitlementAndDistribute(address reciever) + function clearEntitlementAndDistribute(uint256, address reciever) external nonReentrant { @@ -340,9 +356,9 @@ contract HookERC721VaultImplV1 is beneficialOwner == reciever, "clearEntitlementAndDistribute -- Only the beneficial owner can recieve the asset" ); - clearEntitlement(); + clearEntitlement(ASSET_ID); IERC721(_nftContract).safeTransferFrom(address(this), reciever, _tokenId); - emit AssetWithdrawn(msg.sender, beneficialOwner); + emit AssetWithdrawn(ASSET_ID, msg.sender, beneficialOwner); } /// @dev Get the EIP-712 hash of an Entitlement. @@ -397,9 +413,14 @@ contract HookERC721VaultImplV1 is entitlement.vaultAddress == address(this), "_verifyAndRegisterEntitlement -- the entitled contract must match the vault contract" ); + require( + entitlement.assetId == ASSET_ID, + "_verifyAndRegisterEntitlement -- the entitled contract must match the vault contract" + ); _currentEntitlement = entitlement; _hasEntitlement = true; emit EntitlementImposed( + ASSET_ID, entitlement.operator, entitlement.expiry, beneficialOwner @@ -411,17 +432,18 @@ contract HookERC721VaultImplV1 is address(0), address(0), address(0), + ASSET_ID, 0 ); _hasEntitlement = false; - emit EntitlementCleared(beneficialOwner); + emit EntitlementCleared(ASSET_ID, beneficialOwner); } function hasActiveEntitlement() public view returns (bool) { return block.timestamp < _currentEntitlement.expiry && _hasEntitlement; } - function getCurrentEntitlementOperator() + function getCurrentEntitlementOperator(uint256) external view returns (bool isActive, address operator) @@ -436,6 +458,6 @@ contract HookERC721VaultImplV1 is "_setBeneficialOwner -- new owner is the zero address" ); beneficialOwner = newBeneficialOwner; - emit BeneficialOwnerSet(newBeneficialOwner, msg.sender); + emit BeneficialOwnerSet(ASSET_ID, newBeneficialOwner, msg.sender); } } diff --git a/src/interfaces/IHookCoveredCall.sol b/src/interfaces/IHookCoveredCall.sol index f3cc8a9..e45316a 100644 --- a/src/interfaces/IHookCoveredCall.sol +++ b/src/interfaces/IHookCoveredCall.sol @@ -31,16 +31,23 @@ import "../lib/Signatures.sol"; /// of the instrument NFT, the strike price is transferred to the writer. The high bid is transferred to the holder of /// the option. interface IHookCoveredCall is IERC721 { + /// @notice emitted when a new call option is successfully minted with a specific underlying vault event CallCreated( address writer, address vaultAddress, + uint256 assetId, uint256 optionId, uint256 strikePrice, uint256 expiration ); + /// @notice emitted when a call option is destroyed event CallDestroyed(uint256 optionId); + /// @notice emitted when a call option settlement auction gets and accepts a new bid + /// @param bidder the account placing the bid that is now the high bidder + /// @param bidAmount the amount of the bid (in wei) + /// @param optionId the option for the underlying that was bid on event Bid(uint256 optionId, uint256 bidAmount, address bidder); /// @notice Mints a new call option for a particular "underlying" ERC-721 NFT with a given strike price and expiration @@ -57,26 +64,30 @@ interface IHookCoveredCall is IERC721 { ) external returns (uint256); /// @notice Mints a new call option for the assets deposited in a particular vault given strike price and expiration. - /// @param _vaultAddress the contract address of the vault currently holding the call option - /// @param _strikePrice the strike price for the call option being written - /// @param _expirationTime time the timestamp after which the option will be expired + /// @param vaultAddress the contract address of the vault currently holding the call option + /// @param assetId the id of the asset within the vault + /// @param strikePrice the strike price for the call option being written + /// @param expirationTime time the timestamp after which the option will be expired /// @param signature the signature used to place the entitlement onto the vault function mintWithVault( - address _vaultAddress, - uint256 _strikePrice, - uint256 _expirationTime, + address vaultAddress, + uint256 assetId, + uint256 strikePrice, + uint256 expirationTime, Signatures.Signature calldata signature ) external returns (uint256); /// @notice Mints a new call option for the assets deposited in a particular vault given strike price and expiration. /// That vault must already have a registered entitlement for this contract with the correct expiration registered. - /// @param _vaultAddress the contract address of the vault currently holding the call option - /// @param _strikePrice the strike price for the call option being written - /// @param _expirationTime time the timestamp after which the option will be expired + /// @param vaultAddress the contract address of the vault currently holding the call option + /// @param assetId the id of the asset within the vault + /// @param strikePrice the strike price for the call option being written + /// @param expirationTime time the timestamp after which the option will be expired function mintWithEntitledVault( - address _vaultAddress, - uint256 _strikePrice, - uint256 _expirationTime + address vaultAddress, + uint256 assetId, + uint256 strikePrice, + uint256 expirationTime ) external returns (uint256); /// @notice Bid in the settlement auction for an option. The paid amount is the bid, diff --git a/src/interfaces/IHookERC721Vault.sol b/src/interfaces/IHookERC721Vault.sol index d99e3f6..4e672e7 100644 --- a/src/interfaces/IHookERC721Vault.sol +++ b/src/interfaces/IHookERC721Vault.sol @@ -21,7 +21,7 @@ interface IHookERC721Vault is IHookVault, IERC721Receiver { event AssetFlashLoaned(address owner, address flashLoanImpl); /// @notice the tokenID of the underlying ERC721 token; - function assetTokenId() external view returns (uint256); + function assetTokenId(uint256 assetId) external view returns (uint256); /// @notice flashLoans the vaulted asset to another contract for use and return to the vault. Only the owner /// may perform the flashloan @@ -30,5 +30,9 @@ interface IHookERC721Vault is IHookVault, IERC721Receiver { /// @param receiverAddress the contract which implements the {IERC721FlashLoanReceiver} interface to utilize the /// asset while it is loaned out /// @param params calldata params to forward to the reciever - function flashLoan(address receiverAddress, bytes calldata params) external; + function flashLoan( + uint256 assetId, + address receiverAddress, + bytes calldata params + ) external; } diff --git a/src/interfaces/IHookVault.sol b/src/interfaces/IHookVault.sol index 0751920..aff30e1 100644 --- a/src/interfaces/IHookVault.sol +++ b/src/interfaces/IHookVault.sol @@ -28,7 +28,7 @@ interface IHookVault { /// @notice emitted when an entitlement is placed on an asset event EntitlementImposed( uint256 assetId, - address entitledAccout, + address entitledAccount, uint256 expiry, address beneficialOwner ); @@ -54,6 +54,8 @@ interface IHookVault { uint256 assetId ); + event AssetWithdrawn(uint256 assetId, address to, address beneficialOwner); + /// @notice Withdrawal an unencumbered asset from this vault /// @param assetId the asset to remove from the vault function withdrawalAsset(uint256 assetId) external; diff --git a/src/lib/Entitlements.sol b/src/lib/Entitlements.sol index 684648c..4ebdc0d 100644 --- a/src/lib/Entitlements.sol +++ b/src/lib/Entitlements.sol @@ -3,8 +3,6 @@ pragma solidity ^0.8.10; import "./Signatures.sol"; library Entitlements { - // TODO: Should we add a nonce to this struct? This would allow us to make the - // tokenIds cancelable. uint256 private constant _ENTITLEMENT_TYPEHASH = uint256( keccak256( diff --git a/src/test/HookCoveredCallIntegrationTest.sol b/src/test/HookCoveredCallIntegrationTest.sol index 1033c45..8d418e5 100644 --- a/src/test/HookCoveredCallIntegrationTest.sol +++ b/src/test/HookCoveredCallIntegrationTest.sol @@ -38,6 +38,7 @@ contract HookCoveredCallIntegrationTest is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration diff --git a/src/test/HookCoveredCallTests.sol b/src/test/HookCoveredCallTests.sol index f741d39..aea0b71 100644 --- a/src/test/HookCoveredCallTests.sol +++ b/src/test/HookCoveredCallTests.sol @@ -34,6 +34,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -70,10 +71,11 @@ contract HookCoveredCallMintTests is HookProtocolTest { writer ); vm.expectEmit(true, true, true, true); - emit CallCreated(address(writer), address(vault), 1, 1000, expiration); + emit CallCreated(address(writer), address(vault), 0, 1, 1000, expiration); uint256 optionId = calls.mintWithVault( address(vault), + 0, 1000, expiration, sig @@ -84,7 +86,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { "owner should own the option" ); - (bool isActive, address operator) = vault.getCurrentEntitlementOperator(); + (bool isActive, address operator) = vault.getCurrentEntitlementOperator(0); assertTrue(isActive, "there should be an active entitlement"); assertTrue( operator == address(calls), @@ -116,6 +118,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { ); uint256 optionId = calls.mintWithVault( address(vault), + 0, 1000, expiration, sig @@ -141,6 +144,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { vm.expectRevert("mintWithVault-- asset must be in vault"); uint256 optionId = calls.mintWithVault( address(vault), + 0, 1000, expiration, sig @@ -166,6 +170,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { vm.expectRevert("mintWithVault -- token must be on the project allowlist"); uint256 optionId = calls.mintWithVault( address(vault), + 0, 1000, expiration, sig @@ -189,6 +194,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -213,6 +219,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 2, // This would be the second option id. 1000, expiration @@ -249,6 +256,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -292,7 +300,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { vm.expectRevert( "validateEntitlementSignature --- not signed by beneficialOwner" ); - calls.mintWithVault(address(vault), 1000, expiration, signature); + calls.mintWithVault(address(vault), 0, 1000, expiration, signature); } function testCannotMintOptionInvalidExpiration() public { @@ -411,6 +419,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -450,6 +459,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -481,6 +491,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -513,6 +524,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -556,6 +568,7 @@ contract HookCoveredCallMintTests is HookProtocolTest { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -910,7 +923,10 @@ contract HookCoveredCallSettleTests is HookProtocolTest { address(token), underlyingTokenId ); - vm.expectCall(vaultAddress, abi.encodeWithSignature("withdrawalAsset()")); + vm.expectCall( + vaultAddress, + abi.encodeWithSignature("withdrawalAsset(uint256)", 0) + ); vm.prank(writer); calls.settleOption(optionTokenId, true); @@ -1221,7 +1237,10 @@ contract HookCoveredCallReclaimTests is HookProtocolTest { address(token), underlyingTokenId ); - vm.expectCall(vaultAddress, abi.encodeWithSignature("withdrawalAsset()")); + vm.expectCall( + vaultAddress, + abi.encodeWithSignature("withdrawalAsset(uint256)", 0) + ); calls.reclaimAsset(optionTokenId, true); } @@ -1311,7 +1330,10 @@ contract HookCoveredCallReclaimTests is HookProtocolTest { address(token), underlyingTokenId ); - vm.expectCall(vaultAddress, abi.encodeWithSignature("withdrawalAsset()")); + vm.expectCall( + vaultAddress, + abi.encodeWithSignature("withdrawalAsset(uint256)", 0) + ); calls.reclaimAsset(optionTokenId, true); } @@ -1360,7 +1382,10 @@ contract HookCoveredCallReclaimTests is HookProtocolTest { address(token), underlyingTokenId ); - vm.expectCall(vaultAddress, abi.encodeWithSignature("withdrawalAsset()")); + vm.expectCall( + vaultAddress, + abi.encodeWithSignature("withdrawalAsset(uint256)", 0) + ); calls.reclaimAsset(optionTokenId, true); } @@ -1412,7 +1437,10 @@ contract HookCoveredCallReclaimTests is HookProtocolTest { address(token), underlyingTokenId ); - vm.expectCall(vaultAddress, abi.encodeWithSignature("withdrawalAsset()")); + vm.expectCall( + vaultAddress, + abi.encodeWithSignature("withdrawalAsset(uint256)", 0) + ); calls.reclaimAsset(optionTokenId, true); } } diff --git a/src/test/HookVaultTests.sol b/src/test/HookVaultTests.sol index 7879d07..3cace63 100644 --- a/src/test/HookVaultTests.sol +++ b/src/test/HookVaultTests.sol @@ -54,6 +54,7 @@ contract HookVaultTests is HookProtocolTest { beneficialOwner: ownerAdd, operator: operator, vaultAddress: vaultAddress, + assetId: 0, expiry: _expiry }); @@ -100,11 +101,11 @@ contract HookVaultTests is HookProtocolTest { HookERC721VaultImplV1 vaultImpl = HookERC721VaultImplV1(vaultAddress); assertTrue( - vaultImpl.getHoldsAsset(), + vaultImpl.getHoldsAsset(0), "the token should be owned by the vault" ); assertTrue( - vaultImpl.getBeneficialOwner() == writer, + vaultImpl.getBeneficialOwner(0) == writer, "writer should be the beneficial owner" ); assertTrue( @@ -142,7 +143,7 @@ contract HookVaultTests is HookProtocolTest { IERC721FlashLoanReceiver flashLoan = new FlashLoanSuccess(); vm.prank(writer); - vaultImpl.flashLoan(address(flashLoan), " "); + vaultImpl.flashLoan(0, address(flashLoan), " "); assertTrue( token.ownerOf(tokenId) == vaultAddress, "good flashloan should work" @@ -186,7 +187,7 @@ contract HookVaultTests is HookProtocolTest { vm.expectRevert( "flashLoan -- flashLoan feature disabled for this contract" ); - vaultImpl.flashLoan(address(flashLoan), " "); + vaultImpl.flashLoan(0, address(flashLoan), " "); assertTrue( token.ownerOf(tokenId) == vaultAddress, "good flashloan should work" @@ -222,7 +223,7 @@ contract HookVaultTests is HookProtocolTest { IERC721FlashLoanReceiver flashLoan = new FlashLoanApproveForAll(); vm.prank(writer); - vaultImpl.flashLoan(address(flashLoan), " "); + vaultImpl.flashLoan(0, address(flashLoan), " "); assertTrue( token.ownerOf(tokenId) == vaultAddress, "good flashloan should work" @@ -259,7 +260,7 @@ contract HookVaultTests is HookProtocolTest { vm.prank(writer); vm.expectRevert("flashLoan -- the flash loan contract must return true"); - vaultImpl.flashLoan(address(flashLoan), " "); + vaultImpl.flashLoan(0, address(flashLoan), " "); assertTrue( token.ownerOf(tokenId) == vaultAddress, "good flashloan should work" @@ -296,7 +297,7 @@ contract HookVaultTests is HookProtocolTest { vm.prank(writer); vm.expectRevert("ERC721: transfer caller is not owner nor approved"); - vaultImpl.flashLoan(address(flashLoan), " "); + vaultImpl.flashLoan(0, address(flashLoan), " "); assertTrue( token.ownerOf(tokenId) == vaultAddress, "good flashloan should work" @@ -333,7 +334,7 @@ contract HookVaultTests is HookProtocolTest { vm.prank(writer); vm.expectRevert("ERC721: operator query for nonexistent token"); - vaultImpl.flashLoan(address(flashLoan), " "); + vaultImpl.flashLoan(0, address(flashLoan), " "); // operation reverted, so we can still mess with the asset assertTrue( token.ownerOf(tokenId) == vaultAddress, @@ -370,7 +371,7 @@ contract HookVaultTests is HookProtocolTest { IERC721FlashLoanReceiver flashLoan = new FlashLoanVerifyCalldata(); vm.prank(writer); - vaultImpl.flashLoan(address(flashLoan), "hello world"); + vaultImpl.flashLoan(0, address(flashLoan), "hello world"); // operation reverted, so we can still mess with the asset assertTrue( token.ownerOf(tokenId) == vaultAddress, @@ -408,7 +409,7 @@ contract HookVaultTests is HookProtocolTest { vm.prank(writer); vm.expectRevert("should check helloworld"); - vaultImpl.flashLoan(address(flashLoan), "hello world wrong!"); + vaultImpl.flashLoan(0, address(flashLoan), "hello world wrong!"); // operation reverted, so we can still mess with the asset assertTrue( token.ownerOf(tokenId) == vaultAddress, @@ -443,11 +444,11 @@ contract HookVaultTests is HookProtocolTest { vaultImpl.imposeEntitlement(entitlement, sig); assertTrue( - vaultImpl.getHoldsAsset(), + vaultImpl.getHoldsAsset(0), "the token should be owned by the vault" ); assertTrue( - vaultImpl.getBeneficialOwner() == writer, + vaultImpl.getBeneficialOwner(0) == writer, "writer should be the beneficial owner" ); assertTrue( @@ -461,7 +462,7 @@ contract HookVaultTests is HookProtocolTest { "withdrawalAsset -- the asset canot be withdrawn with an active entitlement" ); vm.prank(writer); - vaultImpl.withdrawalAsset(); + vaultImpl.withdrawalAsset(0); } function testEntitlementGoesAwayAfterExpiration() public { @@ -502,9 +503,9 @@ contract HookVaultTests is HookProtocolTest { ); vm.prank(writer); - vaultImpl.withdrawalAsset(); + vaultImpl.withdrawalAsset(0); assertTrue( - !vaultImpl.getHoldsAsset(), + !vaultImpl.getHoldsAsset(0), "the token should not be owned by the vault" ); @@ -540,7 +541,7 @@ contract HookVaultTests is HookProtocolTest { HookERC721VaultImplV1 vaultImpl = HookERC721VaultImplV1(vaultAddress); vm.prank(mockContract); - vaultImpl.clearEntitlement(); + vaultImpl.clearEntitlement(0); assertTrue( !vaultImpl.hasActiveEntitlement(), @@ -549,9 +550,9 @@ contract HookVaultTests is HookProtocolTest { // check that the owner can actually withdrawl vm.prank(writer); - vaultImpl.withdrawalAsset(); + vaultImpl.withdrawalAsset(0); assertTrue( - !vaultImpl.getHoldsAsset(), + !vaultImpl.getHoldsAsset(0), "the token should not be owned by the vault" ); @@ -648,7 +649,7 @@ contract HookVaultTests is HookProtocolTest { ); vm.prank(mockContract); - vaultImpl.clearEntitlement(); + vaultImpl.clearEntitlement(0); assertTrue( !vaultImpl.hasActiveEntitlement(), @@ -677,7 +678,7 @@ contract HookVaultTests is HookProtocolTest { function testOnlyOneEntitlementAllowed() public { (address vaultAddress, uint256 tokenId) = createVaultandAsset(); - address mockContract = address(69); + address mockContract = address(3333); uint256 expiration = block.timestamp + 1 days; ( @@ -761,13 +762,13 @@ contract HookVaultTests is HookProtocolTest { vm.expectRevert( "clearEntitlement -- only the entitled address can clear the entitlement" ); - vaultImpl.clearEntitlement(); + vaultImpl.clearEntitlement(0); vm.prank(address(55566677788899911)); vm.expectRevert( "clearEntitlement -- only the entitled address can clear the entitlement" ); - vaultImpl.clearEntitlement(); + vaultImpl.clearEntitlement(0); } function testClearAndDistributeReturnsNFT() public { @@ -796,7 +797,7 @@ contract HookVaultTests is HookProtocolTest { HookERC721VaultImplV1 vaultImpl = HookERC721VaultImplV1(vaultAddress); vm.prank(mockContract); - vaultImpl.clearEntitlementAndDistribute(writer); + vaultImpl.clearEntitlementAndDistribute(0, writer); assertTrue( !vaultImpl.hasActiveEntitlement(), @@ -870,6 +871,6 @@ contract HookVaultTests is HookProtocolTest { "clearEntitlementAndDistribute -- Only the beneficial owner can recieve the asset" ); vm.prank(mockContract); - vaultImpl.clearEntitlementAndDistribute(address(0x033333344545)); + vaultImpl.clearEntitlementAndDistribute(0, address(0x033333344545)); } } diff --git a/src/test/utils/base.sol b/src/test/utils/base.sol index 69adc8d..02eac44 100644 --- a/src/test/utils/base.sol +++ b/src/test/utils/base.sol @@ -45,6 +45,7 @@ contract HookProtocolTest is Test, EIP712, PermissionConstants { event CallCreated( address writer, address vaultAddress, + uint256 assetId, uint256 optionId, uint256 strikePrice, uint256 expiration @@ -117,6 +118,7 @@ contract HookProtocolTest is Test, EIP712, PermissionConstants { emit CallCreated( address(writer), address(token), + 0, 1, // This would be the first option id. 1000, expiration @@ -168,6 +170,7 @@ contract HookProtocolTest is Test, EIP712, PermissionConstants { beneficialOwner: address(writer), operator: address(calls), vaultAddress: va, + assetId: 0, expiry: expiry }) );