Skip to content

Commit 4ae3a14

Browse files
committed
add erc20 vault authenticity handling and factory
1 parent 733f4e8 commit 4ae3a14

5 files changed

Lines changed: 263 additions & 45 deletions

File tree

src/HookAmericanOptionImplV1.sol

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
4141
import "@openzeppelin/contracts/utils/Create2.sol";
4242

4343
import "./lib/Entitlements.sol";
44+
import "./lib/VaultAuthenticator.sol";
4445
import "./lib/BeaconSalts.sol";
4546

4647
import "./interfaces/IHookOptionExercisableVaultValidator.sol";
@@ -70,7 +71,6 @@ contract HookAmericanOptionImplV1 is
7071
/// @param expiration The expiration time of the put option
7172
/// @param assetId the asset id of the cash within the vault. This cash is the strike price
7273
/// @param vaultAddress the address of the vault holding the cash securing the put
73-
/// @param exercisableAssetAddress the address of the asset that can be used to exercise the put
7474
/// @param exercisableAssetIdStart the first token id that can be used to exercise the put (inclusive)
7575
/// @param exercisableAssetIdEnd the last token id that can be used to exercise the put (inclusive)
7676
/// @param settled a flag that marks when a settlement action has taken place successfully. Once this flag is set, ETH should not
@@ -80,7 +80,7 @@ contract HookAmericanOptionImplV1 is
8080
uint32 expiration;
8181
address considerationAssetVaultAddress;
8282
uint32 considerationAssetVaultId;
83-
bytes32 exercisableAssetVaultParameter;
83+
bytes exercisableAssetVaultParameter;
8484
bool settled;
8585
}
8686

@@ -90,15 +90,17 @@ contract HookAmericanOptionImplV1 is
9090
/// the associated option instrument NFT.
9191
Counters.Counter private _optionIds;
9292

93+
/// @dev the address of the factory in the Hook protocol that can be used to generate ERC721 vaults
94+
address private _erc721VaultFactory;
95+
address private _erc20VaultFactory;
96+
9397
/// @dev the address of the deployed hook protocol contract, which has permissions and access controls
9498
IHookProtocol private _protocol;
9599

96100
/// @dev storage of all existing options contracts.
97101
mapping(uint256 => Option) public optionParams;
98102

99103
address public collateralAssetAddress;
100-
101-
address public exercisableAssetAddress;
102104

103105
/// @dev storage of current active put option secured by a specific asset
104106
/// mapping(vaultAddress => mapping(assetId => Options))
@@ -134,15 +136,19 @@ contract HookAmericanOptionImplV1 is
134136
function initialize(
135137
address protocol,
136138
address _collateralAssetAddress,
137-
address _exercisableAssetAddress,
138139
address validator,
139-
address preApprovedMarketplace
140+
address preApprovedMarketplace,
141+
address erc20VaultFactory,
142+
address erc721VaultFactory
140143
) public initializer {
141144
_protocol = IHookProtocol(protocol);
142145
_preApprovedMarketplace = preApprovedMarketplace;
146+
143147
vaultValidator = IHookOptionExercisableVaultValidator(validator);
144148
collateralAssetAddress = _collateralAssetAddress;
145-
exercisableAssetAddress = _exercisableAssetAddress;
149+
150+
_erc20VaultFactory = erc20VaultFactory;
151+
_erc721VaultFactory = erc721VaultFactory;
146152

147153
/// Initialize basic configuration.
148154
/// Even though these are defaults, we cannot set them in the constructor because
@@ -157,7 +163,7 @@ contract HookAmericanOptionImplV1 is
157163
function mintWithVault(
158164
address vaultAddress,
159165
uint32 assetId,
160-
bytes32 vaultValidatorParams,
166+
bytes calldata vaultValidatorParams,
161167
uint32 expirationTime,
162168
Signatures.Signature calldata signature
163169
) external nonReentrant whenNotPaused returns (uint256) {
@@ -170,7 +176,8 @@ contract HookAmericanOptionImplV1 is
170176
require(
171177
_allowedVaultImplementation(
172178
vaultAddress,
173-
collateralAssetAddress
179+
collateralAssetAddress,
180+
assetId
174181
),
175182
"mWV-can only mint with protocol vaults"
176183
);
@@ -194,13 +201,13 @@ contract HookAmericanOptionImplV1 is
194201
);
195202

196203
return
197-
_mintOptionWithVault(writer, vault, assetId, vaultValidatorParams, expirationTime);
204+
_mintOptionWithVault(writer, vault, assetId, expirationTime, vaultValidatorParams);
198205
}
199206

200207
function mintWithEntitledVault(
201208
address vaultAddress,
202209
uint32 assetId,
203-
bytes32 vaultValidatorParams,
210+
bytes calldata vaultValidatorParams,
204211
uint32 expirationTime
205212
) external nonReentrant whenNotPaused returns (uint256) {
206213
IHookVault vault = IHookVault(vaultAddress);
@@ -225,7 +232,8 @@ contract HookAmericanOptionImplV1 is
225232
require(
226233
_allowedVaultImplementation(
227234
vaultAddress,
228-
collateralAssetAddress
235+
collateralAssetAddress,
236+
assetId
229237
),
230238
"mWEV-only protocol vaults allowed"
231239
);
@@ -240,7 +248,7 @@ contract HookAmericanOptionImplV1 is
240248
);
241249

242250
return
243-
_mintOptionWithVault(writer, vault, assetId, vaultValidatorParams, expirationTime);
251+
_mintOptionWithVault(writer, vault, assetId, expirationTime, vaultValidatorParams);
244252
}
245253

246254
/// @notice internal use function to record the option and mint it
@@ -254,8 +262,8 @@ contract HookAmericanOptionImplV1 is
254262
address writer,
255263
IHookVault vault,
256264
uint32 assetId,
257-
bytes32 param,
258-
uint32 expirationTime
265+
uint32 expirationTime,
266+
bytes calldata param
259267
) private returns (uint256) {
260268
// NOTE: The settlement auction always occurs one day before expiration
261269
require(
@@ -314,24 +322,10 @@ contract HookAmericanOptionImplV1 is
314322
/// @param underlyingAddress address of underlying asset
315323
function _allowedVaultImplementation(
316324
address vaultAddress,
317-
address underlyingAddress
325+
address underlyingAddress,
326+
uint32 assetId
318327
) internal view returns (bool) {
319-
// First check if the multiVault is the one to save a bit of gas
320-
// in the case the user is optimizing for gas savings (by using MultiVault)
321-
// if (
322-
// //todo: create an erc20 vault
323-
// // vaultAddress ==
324-
// // Create2.computeAddress(
325-
// // BeaconSalts.erc20VaultSalt(underlyingAddress),
326-
// // BeaconSalts.ByteCodeHash,
327-
// // address(_erc20VaultFactory)
328-
// // )
329-
// ) {
330-
// return true;
331-
// }
332-
333-
// return false;
334-
return true;
328+
return VaultAuthenticator.isHookERC20Vault(_erc20VaultFactory, underlyingAddress, vaultAddress) || VaultAuthenticator.isHookERC721Vault(_erc721VaultFactory, underlyingAddress, vaultAddress, assetId);
335329
}
336330

337331

@@ -348,9 +342,7 @@ return true;
348342
address optionOwner = ownerOf(optionId);
349343
require (msg.sender == optionOwner, "e-only the option owner can exercise");
350344

351-
/// TODO: use validator to ensure that the excercise vault is good
352-
/// TODO: validate that excercise vault is a protocol vault
353-
/// TODO: validate that the underlying asset in the vault is valid.
345+
require(vaultValidator.validate(exerciseAssetVaultAddress, assetId, put.exercisableAssetVaultParameter), "exerciseAsset invalid");
354346

355347
// Send the option writer the underlying asset
356348
IHookVault(exerciseAssetVaultAddress).setBeneficialOwner(assetId, put.writer);
@@ -363,7 +355,7 @@ return true;
363355
_burn(optionId);
364356

365357
// set settled to prevent an additional attempt to exercise the option
366-
optionParams[optionId].settled = true;
358+
optionParams[optionId].settled = true;
367359

368360
// TODO: settled event
369361
// emit PutSettled(optionId);

src/HookERC20VaultFactory.sol

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// SPDX-License-Identifier: MIT
2+
//
3+
// █████████████▌ ▐█████████████
4+
// █████████████▌ ▐█████████████
5+
// █████████████▌ ▐█████████████
6+
// █████████████▌ ▐█████████████
7+
// █████████████▌ ▐█████████████
8+
// █████████████▌ ▐█████████████
9+
// █████████████▌ ▐█████████████
10+
// █████████████▌ ▐█████████████
11+
// ██████████████ ██████████████
12+
// ██████████████ ▄▄████████████████▄▄ ▐█████████████▌
13+
// ██████████████ ▄█████████████████████████████▄ ██████████████
14+
// ██████████▀ ▄█████████████████████████████████ ██████████████▌
15+
// ██████▀ ▄██████████████████████████████████▀ ▄███████████████
16+
// ███▀ ██████████████████████████████████▀ ▄████████████████
17+
// ▀▀ ████████████████████████████████▀▀ ▄█████████████████▌
18+
// █████████████████████▀▀▀▀▀▀▀ ▄▄███████████████████▀
19+
// ██████████████████▀ ▄▄▄█████████████████████████████▀
20+
// ████████████████▀ ▄█████████████████████████████████▀ ██▄
21+
// ▐███████████████▀ ▄██████████████████████████████████▀ █████▄
22+
// ██████████████▀ ▄█████████████████████████████████▀ ▄████████
23+
// ██████████████▀ ███████████████████████████████▀ ▄████████████
24+
// ▐█████████████▌ ▀▀▀▀████████████████████▀▀▀▀ █████████████▌
25+
// ██████████████ ██████████████
26+
// █████████████▌ ██████████████
27+
// █████████████▌ ██████████████
28+
// █████████████▌ ██████████████
29+
// █████████████▌ ██████████████
30+
// █████████████▌ ██████████████
31+
// █████████████▌ ██████████████
32+
// █████████████▌ ██████████████
33+
// █████████████▌ ██████████████
34+
35+
pragma solidity ^0.8.10;
36+
37+
import "@openzeppelin/contracts/utils/Create2.sol";
38+
39+
import "./HookBeaconProxy.sol";
40+
41+
import "./interfaces/IHookERC20VaultFactory.sol";
42+
import "./interfaces/IHookERC20Vault.sol";
43+
import "./interfaces/IHookProtocol.sol";
44+
import "./interfaces/IInitializeableBeacon.sol";
45+
46+
import "./mixin/PermissionConstants.sol";
47+
48+
import "./lib/BeaconSalts.sol";
49+
50+
/// @title Hook Vault Factory
51+
/// @author Jake Nyquist-j@hook.xyz
52+
/// @dev See {IHookERC20VaultFactory}.
53+
/// @dev The factory itself is non-upgradeable; however, each vault is upgradeable (i.e. all vaults)
54+
/// created by this factory can be upgraded at one time via the beacon pattern.
55+
contract HookER20VaultFactory is
56+
IHookERC20VaultFactory,
57+
PermissionConstants
58+
{
59+
60+
/// @notice Registry of all of the active multi-vaults within the protocol
61+
mapping(address => IHookERC20Vault) public override getVault;
62+
63+
address private immutable _hookProtocol;
64+
address private immutable _beacon;
65+
66+
constructor(
67+
address hookProtocolAddress,
68+
address beaconAddress
69+
) {
70+
require(
71+
Address.isContract(hookProtocolAddress),
72+
"hook protocol must be a contract"
73+
);
74+
require(
75+
Address.isContract(beaconAddress),
76+
"beacon address must be a contract"
77+
);
78+
_hookProtocol = hookProtocolAddress;
79+
_beacon = beaconAddress;
80+
81+
}
82+
83+
/// @notice See {IHookERC29VaultFactory-makeVault}.
84+
function makeVault(address tokenAddress)
85+
public
86+
returns (IHookERC20Vault)
87+
{
88+
require(
89+
IHookProtocol(_hookProtocol).hasRole(ALLOWLISTER_ROLE, msg.sender) ||
90+
IHookProtocol(_hookProtocol).hasRole(ALLOWLISTER_ROLE, address(0)),
91+
"makeVault-Only accounts with the ALLOWLISTER role can make new vaults"
92+
);
93+
94+
require(
95+
getVault[tokenAddress] == IHookERC20Vault(address(0)),
96+
"makeVault-vault cannot already exist"
97+
);
98+
99+
IInitializeableBeacon bp = IInitializeableBeacon(
100+
Create2.deploy(
101+
0,
102+
BeaconSalts.erc20VaultSalt(tokenAddress),
103+
type(HookBeaconProxy).creationCode
104+
)
105+
);
106+
107+
bp.initializeBeacon(
108+
_beacon,
109+
/// This is the ABI encoded initializer on the IHookERC20Vault.sol
110+
abi.encodeWithSignature(
111+
"initialize(address,address)",
112+
tokenAddress,
113+
_hookProtocol
114+
)
115+
);
116+
117+
IHookERC20Vault vault = IHookERC20Vault(address(bp));
118+
getVault[tokenAddress] = vault;
119+
emit ERC20VaultCreated(tokenAddress, address(bp));
120+
121+
return vault;
122+
}
123+
124+
/// @notice See {IHookERC20VaultFactory-findOrCreateVault}.
125+
function findOrCreateVault(address tokenAddress)
126+
external
127+
returns (IHookERC20Vault)
128+
{
129+
if (getVault[tokenAddress] != IHookERC20Vault(address(0))) {
130+
return getVault[tokenAddress];
131+
}
132+
133+
return makeVault(tokenAddress);
134+
}
135+
}

0 commit comments

Comments
 (0)