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

[Feature] [Gas] create a multi-asset ERC-721 vault #22

Merged
merged 4 commits into from
May 19, 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
6 changes: 4 additions & 2 deletions src/HookCoveredCallFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ contract HookCoveredCallFactory is
getCallInstrument[assetAddress] == address(0),
"makeCallInstrument -- a call instrument already exists"
);
// make sure new instruments created by admins.
// make sure new instruments created by admins or the role
// has been burned
require(
_protocol.hasRole(ALLOWLISTER_ROLE, msg.sender),
_protocol.hasRole(ALLOWLISTER_ROLE, msg.sender) ||
_protocol.hasRole(ALLOWLISTER_ROLE, address(0)),
"makeCallInstrument -- Only admins can make instruments"
);

Expand Down
42 changes: 15 additions & 27 deletions src/HookCoveredCallImplV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ contract HookCoveredCallImplV1 is
address protocol,
address nftContract,
address hookVaultFactory
) public initializer {
) external initializer {
_protocol = IHookProtocol(protocol);
_erc721VaultFactory = IHookERC721VaultFactory(hookVaultFactory);
weth = _protocol.getWETHAddress();
Expand Down Expand Up @@ -179,13 +179,7 @@ contract HookCoveredCallImplV1 is
vault.imposeEntitlement(entitlement, signature);

return
_mintOptionWithVault(
writer,
address(vault),
assetId,
strikePrice,
expirationTime
);
_mintOptionWithVault(writer, vault, assetId, strikePrice, expirationTime);
}

/// @dev See {IHookCoveredCall-mintWithEntitledVault}.
Expand Down Expand Up @@ -223,13 +217,7 @@ contract HookCoveredCallImplV1 is
address writer = vault.getBeneficialOwner(assetId);

return
_mintOptionWithVault(
writer,
vaultAddress,
assetId,
strikePrice,
expirationTime
);
_mintOptionWithVault(writer, vault, assetId, strikePrice, expirationTime);
}

/// @dev See {IHookCoveredCall-mintWithErc721}.
Expand Down Expand Up @@ -259,18 +247,18 @@ contract HookCoveredCallImplV1 is
);

// FIND OR CREATE HOOK VAULT, SET AN ENTITLEMENT
address vault = _erc721VaultFactory.getVault(tokenAddress, tokenId);
if (vault == address(0)) {
vault = _erc721VaultFactory.makeVault(tokenAddress, tokenId);
}
IHookERC721Vault vault = _erc721VaultFactory.findOrCreateVault(
tokenAddress,
tokenId
);

/// IMPORTANT: the entitlement entitles the user to this contract address. That means that even if this
// implementation code were upgraded, the contract at this address (i.e. with the new implementation) would
// retain the entitlement.
Entitlements.Entitlement memory entitlement = Entitlements.Entitlement({
beneficialOwner: tokenOwner,
operator: address(this),
vaultAddress: vault,
vaultAddress: address(vault),
assetId: assetId, /// assume that the asset within the vault has assetId 0
expiry: expirationTime
});
Expand All @@ -279,21 +267,21 @@ contract HookCoveredCallImplV1 is
// here will be accepted by the vault because we are also simultaneously tendering the asset.
IERC721(tokenAddress).safeTransferFrom(
tokenOwner,
vault,
address(vault),
tokenId,
abi.encode(entitlement)
);

// make sure that the vault actually has the asset.
require(
IHookVault(vault).getHoldsAsset(assetId),
vault.getHoldsAsset(assetId),
"mintWithErc712 -- asset must be in vault"
);

return
_mintOptionWithVault(
tokenOwner,
vault,
IHookVault(vault),
assetId,
strikePrice,
expirationTime
Expand All @@ -304,13 +292,13 @@ contract HookCoveredCallImplV1 is
/// @dev the vault is completely unchecked here, so the caller must ensure the vault is created,
/// 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 vault 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,
IHookVault vault,
uint256 assetId,
uint256 strikePrice,
uint256 expirationTime
Expand All @@ -328,7 +316,7 @@ contract HookCoveredCallImplV1 is
// save the option metadata
optionParams[newOptionId] = CallOption({
writer: writer,
vaultAddress: vaultAddress,
vaultAddress: address(vault),
assetId: assetId,
strike: strikePrice,
expiration: expirationTime,
Expand All @@ -348,7 +336,7 @@ contract HookCoveredCallImplV1 is

emit CallCreated(
writer,
vaultAddress,
address(vault),
assetId,
newOptionId,
strikePrice,
Expand Down
24 changes: 24 additions & 0 deletions src/HookERC721MultiVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";

/// @title ERC-721 MultiVault Proxy Contract
/// @author Jake Nyquist -- [email protected]
/// @notice Each instance of this contract is a unique multi-vault which references the
/// shared implementation pointed to by the Beacon
contract HookERC721MultiVault is BeaconProxy {
constructor(
address beacon,
address nftAddress,
address hookProtocolAddress
)
BeaconProxy(
beacon,
abi.encodeWithSignature(
"initialize(address,address)",
nftAddress,
hookProtocolAddress
)
)
{}
}
15 changes: 15 additions & 0 deletions src/HookERC721MultiVaultBeacon.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity ^0.8.10;

import "./HookUpgradeableBeacon.sol";

/// @title HookERC721MultiVaultBeacon -- beacon holding pointer to current ERC721MultiVault implementation
/// @author Jake Nyquist -- [email protected]
/// @notice The beacon broadcasts the address which contains the existing implementation of the ERC721MultiVault
/// @dev Permissions for who can upgrade are contained within the protocol contract.
contract HookERC721MultiVaultBeacon is HookUpgradeableBeacon {
constructor(
address implementation,
address hookProtocol,
bytes32 upgraderRole
) HookUpgradeableBeacon(implementation, hookProtocol, upgraderRole) {}
}
Loading