Skip to content

Commit cd16bdf

Browse files
authored
update FeeVault (#80)
* update FeeVault constructor to accept additional parameters for deployment configuration * lint * updates * ++
1 parent d82f13b commit cd16bdf

File tree

4 files changed

+77
-73
lines changed

4 files changed

+77
-73
lines changed

contracts/README.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,21 @@ The FeeVault uses CREATE2 for deterministic addresses across chains.
3333

3434
### Environment Variables
3535

36-
| Variable | Deploy | Operational | Description |
37-
|----------|--------|-------------|-------------|
38-
| `OWNER` | Required | - | Owner address (can configure the vault) |
39-
| `SALT` | Optional | - | CREATE2 salt (default: `0x0`). Use any bytes32 value |
40-
| `DESTINATION_DOMAIN` | Optional | Required | Hyperlane destination chain ID |
41-
| `RECIPIENT_ADDRESS` | Optional | Required | Recipient on destination chain (bytes32, left-padded) |
42-
| `MINIMUM_AMOUNT` | Optional | Optional | Minimum wei to bridge |
43-
| `CALL_FEE` | Optional | Optional | Fee in wei for calling `sendToCelestia()` |
44-
| `BRIDGE_SHARE_BPS` | Optional | Optional | Basis points to bridge (default: 10000 = 100%) |
45-
| `OTHER_RECIPIENT` | Optional | Required* | Address to receive non-bridged portion |
46-
47-
*`OTHER_RECIPIENT` is required only if `BRIDGE_SHARE_BPS` < 10000
36+
All configuration is set via constructor arguments at deploy time:
37+
38+
| Variable | Required | Description |
39+
|----------|----------|-------------|
40+
| `OWNER` | Yes | Owner address (can configure the vault post-deployment) |
41+
| `SALT` | No | CREATE2 salt (default: `0x0`). Use any bytes32 value |
42+
| `DESTINATION_DOMAIN` | Yes* | Hyperlane destination chain ID |
43+
| `RECIPIENT_ADDRESS` | Yes* | Recipient on destination chain (bytes32, left-padded) |
44+
| `MINIMUM_AMOUNT` | No | Minimum wei to bridge (default: 0) |
45+
| `CALL_FEE` | No | Fee in wei for calling `sendToCelestia()` (default: 0) |
46+
| `BRIDGE_SHARE_BPS` | No | Basis points to bridge (default: 10000 = 100%) |
47+
| `OTHER_RECIPIENT` | No** | Address to receive non-bridged portion |
48+
49+
*Required for the vault to be operational (can be set to 0 at deploy and configured later via setters)
50+
**Required if `BRIDGE_SHARE_BPS` < 10000
4851

4952
**Note:** `HYP_NATIVE_MINTER` must be set via `setHypNativeMinter()` after deployment for the vault to be operational.
5053

contracts/script/DeployFeeVault.s.sol

Lines changed: 32 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,81 +10,66 @@ contract DeployFeeVault is Script {
1010
address owner = vm.envAddress("OWNER");
1111
bytes32 salt = vm.envOr("SALT", bytes32(0));
1212

13-
// Optional: Post-deployment configuration
1413
uint32 destinationDomain = uint32(vm.envOr("DESTINATION_DOMAIN", uint256(0)));
1514
bytes32 recipientAddress = vm.envOr("RECIPIENT_ADDRESS", bytes32(0));
1615
uint256 minimumAmount = vm.envOr("MINIMUM_AMOUNT", uint256(0));
1716
uint256 callFee = vm.envOr("CALL_FEE", uint256(0));
18-
uint256 bridgeShareBps = vm.envOr("BRIDGE_SHARE_BPS", uint256(10000));
17+
uint256 bridgeShareBps = vm.envOr("BRIDGE_SHARE_BPS", uint256(0)); // 0 defaults to 10000 in constructor
1918
address otherRecipient = vm.envOr("OTHER_RECIPIENT", address(0));
2019
// ===================================
2120

22-
// Compute address before deployment
23-
address predicted = computeAddress(salt, owner);
24-
console.log("Predicted FeeVault address:", predicted);
25-
2621
vm.startBroadcast();
2722

2823
// Deploy FeeVault with CREATE2
29-
FeeVault feeVault = new FeeVault{salt: salt}(owner);
30-
console.log("FeeVault deployed at:", address(feeVault));
31-
require(address(feeVault) == predicted, "Address mismatch");
32-
33-
// Configure if values provided
34-
if (destinationDomain != 0 && recipientAddress != bytes32(0)) {
35-
feeVault.setRecipient(destinationDomain, recipientAddress);
36-
console.log("Recipient set - domain:", destinationDomain);
37-
}
38-
39-
if (minimumAmount > 0) {
40-
feeVault.setMinimumAmount(minimumAmount);
41-
console.log("Minimum amount set:", minimumAmount);
42-
}
43-
44-
if (callFee > 0) {
45-
feeVault.setCallFee(callFee);
46-
console.log("Call fee set:", callFee);
47-
}
48-
49-
if (bridgeShareBps != 10000) {
50-
feeVault.setBridgeShare(bridgeShareBps);
51-
console.log("Bridge share set:", bridgeShareBps, "bps");
52-
}
53-
54-
if (otherRecipient != address(0)) {
55-
feeVault.setOtherRecipient(otherRecipient);
56-
console.log("Other recipient set:", otherRecipient);
57-
}
24+
FeeVault feeVault = new FeeVault{salt: salt}(
25+
owner, destinationDomain, recipientAddress, minimumAmount, callFee, bridgeShareBps, otherRecipient
26+
);
5827

5928
vm.stopBroadcast();
6029

30+
console.log("FeeVault deployed at:", address(feeVault));
31+
console.log("Owner:", owner);
32+
console.log("Destination domain:", destinationDomain);
33+
console.log("Minimum amount:", minimumAmount);
34+
console.log("Call fee:", callFee);
35+
console.log("Bridge share bps:", feeVault.bridgeShareBps());
6136
console.log("");
6237
console.log("NOTE: Call setHypNativeMinter() after deploying HypNativeMinter");
6338
}
64-
65-
/// @notice Compute the CREATE2 address for FeeVault deployment
66-
function computeAddress(bytes32 salt, address owner) public view returns (address) {
67-
bytes32 bytecodeHash = keccak256(abi.encodePacked(type(FeeVault).creationCode, abi.encode(owner)));
68-
return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, bytecodeHash)))));
69-
}
7039
}
7140

72-
/// @notice Standalone script to compute FeeVault address without deploying
41+
/// @notice Compute FeeVault CREATE2 address off-chain
42+
/// @dev Use this to predict the address before deploying
43+
/// Requires env vars: DEPLOYER (EOA), OWNER, SALT (optional), and all constructor args
7344
contract ComputeFeeVaultAddress is Script {
7445
function run() external view {
75-
address owner = vm.envAddress("OWNER");
76-
bytes32 salt = vm.envOr("SALT", bytes32(0));
7746
address deployer = vm.envAddress("DEPLOYER");
47+
bytes32 salt = vm.envOr("SALT", bytes32(0));
48+
49+
address owner = vm.envAddress("OWNER");
50+
uint32 destinationDomain = uint32(vm.envOr("DESTINATION_DOMAIN", uint256(0)));
51+
bytes32 recipientAddress = vm.envOr("RECIPIENT_ADDRESS", bytes32(0));
52+
uint256 minimumAmount = vm.envOr("MINIMUM_AMOUNT", uint256(0));
53+
uint256 callFee = vm.envOr("CALL_FEE", uint256(0));
54+
uint256 bridgeShareBps = vm.envOr("BRIDGE_SHARE_BPS", uint256(0));
55+
address otherRecipient = vm.envOr("OTHER_RECIPIENT", address(0));
7856

79-
bytes32 bytecodeHash = keccak256(abi.encodePacked(type(FeeVault).creationCode, abi.encode(owner)));
57+
bytes32 initCodeHash = keccak256(
58+
abi.encodePacked(
59+
type(FeeVault).creationCode,
60+
abi.encode(
61+
owner, destinationDomain, recipientAddress, minimumAmount, callFee, bridgeShareBps, otherRecipient
62+
)
63+
)
64+
);
8065

8166
address predicted =
82-
address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)))));
67+
address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, initCodeHash)))));
8368

8469
console.log("========== FeeVault Address Computation ==========");
70+
console.log("Deployer (EOA):", deployer);
8571
console.log("Owner:", owner);
8672
console.log("Salt:", vm.toString(salt));
87-
console.log("Deployer:", deployer);
8873
console.log("Predicted address:", predicted);
8974
console.log("==================================================");
9075
}

contracts/src/FeeVault.sol

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,26 @@ contract FeeVault {
3636
_;
3737
}
3838

39-
constructor(address _owner) {
39+
constructor(
40+
address _owner,
41+
uint32 _destinationDomain,
42+
bytes32 _recipientAddress,
43+
uint256 _minimumAmount,
44+
uint256 _callFee,
45+
uint256 _bridgeShareBps,
46+
address _otherRecipient
47+
) {
48+
require(_owner != address(0), "FeeVault: owner is the zero address");
49+
require(_bridgeShareBps <= 10000, "FeeVault: invalid bps");
50+
4051
owner = _owner;
41-
bridgeShareBps = 10000; // Default to 100% bridge
52+
destinationDomain = _destinationDomain;
53+
recipientAddress = _recipientAddress;
54+
minimumAmount = _minimumAmount;
55+
callFee = _callFee;
56+
bridgeShareBps = _bridgeShareBps == 0 ? 10000 : _bridgeShareBps;
57+
otherRecipient = _otherRecipient;
58+
4259
emit OwnershipTransferred(address(0), _owner);
4360
}
4461

contracts/test/FeeVault.t.sol

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,18 @@ contract FeeVaultTest is Test {
3535
user = address(0x1);
3636
otherRecipient = address(0x99);
3737
mockMinter = new MockHypNativeMinter();
38-
feeVault = new FeeVault(owner);
3938

40-
// Configure contract
39+
feeVault = new FeeVault(
40+
owner,
41+
destination,
42+
recipient,
43+
minAmount,
44+
fee,
45+
10000, // 100% bridge share
46+
otherRecipient
47+
);
48+
4149
feeVault.setHypNativeMinter(address(mockMinter));
42-
feeVault.setRecipient(destination, recipient);
43-
feeVault.setMinimumAmount(minAmount);
44-
feeVault.setCallFee(fee);
45-
feeVault.setOtherRecipient(otherRecipient);
46-
// Default bridge share is 10000 (100%)
4750
}
4851

4952
function test_Receive() public {
@@ -207,11 +210,7 @@ contract FeeVaultTest is Test {
207210

208211
function test_SendToCelestia_MinterNotSet() public {
209212
// Deploy fresh vault without minter
210-
FeeVault freshVault = new FeeVault(owner);
211-
freshVault.setRecipient(destination, recipient);
212-
freshVault.setMinimumAmount(minAmount);
213-
freshVault.setCallFee(fee);
214-
freshVault.setOtherRecipient(otherRecipient);
213+
FeeVault freshVault = new FeeVault(owner, destination, recipient, minAmount, fee, 10000, otherRecipient);
215214

216215
(bool success,) = address(freshVault).call{value: minAmount}("");
217216
require(success);

0 commit comments

Comments
 (0)