Skip to content

Commit 005cf6f

Browse files
committed
feat: add fee recipient and percentage to Hyperfund and Hyperstaker creation
1 parent c25bc0c commit 005cf6f

4 files changed

Lines changed: 73 additions & 45 deletions

File tree

src/HyperfundFactory.sol

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ contract HyperfundFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable
2727
address admin,
2828
address manager,
2929
address pauser,
30-
address upgrader
30+
address upgrader,
31+
address feeRecipient,
32+
uint256 feePercentage
3133
);
3234

3335
event HyperstakerCreated(
@@ -36,7 +38,9 @@ contract HyperfundFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable
3638
address admin,
3739
address manager,
3840
address pauser,
39-
address upgrader
41+
address upgrader,
42+
address feeRecipient,
43+
uint256 feePercentage
4044
);
4145

4246
constructor() {
@@ -59,7 +63,7 @@ contract HyperfundFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable
5963
/// @param pauser address of the pauser of the Hyperfund
6064
/// @param upgrader address of the upgrader of the Hyperfund
6165
/// @return address of the new Hyperfund
62-
function createHyperfund(uint256 hypercertTypeId, address admin, address manager, address pauser, address upgrader)
66+
function createHyperfund(uint256 hypercertTypeId, address admin, address manager, address pauser, address upgrader, address feeRecipient, uint256 feePercentage)
6367
external
6468
returns (address)
6569
{
@@ -69,16 +73,14 @@ contract HyperfundFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable
6973

7074
Hyperfund implementation = new Hyperfund();
7175
bytes memory initData = abi.encodeWithSelector(
72-
Hyperfund.initialize.selector, address(hypercertMinter), hypercertTypeId, admin, manager, pauser, upgrader
76+
Hyperfund.initialize.selector, address(hypercertMinter), hypercertTypeId, admin, manager, pauser, upgrader, feeRecipient, feePercentage
7377
);
7478

75-
ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initData);
76-
IHypercertToken(hypercertMinter).setApprovalForAll(address(proxy), true);
77-
address newHyperfund = address(proxy);
78-
require(newHyperfund != address(0), DeploymentFailed());
79-
79+
address newHyperfund = _deployContract(address(implementation), initData);
80+
IHypercertToken(hypercertMinter).setApprovalForAll(newHyperfund, true);
81+
8082
hyperfunds[hypercertTypeId] = true;
81-
emit HyperfundCreated(newHyperfund, hypercertTypeId, admin, manager, pauser, upgrader);
83+
emit HyperfundCreated(newHyperfund, hypercertTypeId, admin, manager, pauser, upgrader, feeRecipient, feePercentage);
8284
return newHyperfund;
8385
}
8486

@@ -94,23 +96,23 @@ contract HyperfundFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable
9496
address admin,
9597
address manager,
9698
address pauser,
97-
address upgrader
99+
address upgrader,
100+
address feeRecipient,
101+
uint256 feePercentage
98102
) external returns (address) {
99103
require(manager != address(0), InvalidAddress());
100104
require(hyperstakers[hypercertTypeId] == false, AlreadyDeployed());
101105
require(msg.sender == IHypercertToken(hypercertMinter).ownerOf(hypercertTypeId + 1), NotOwnerOfHypercert());
102106

103107
Hyperstaker implementation = new Hyperstaker();
104108
bytes memory initData = abi.encodeWithSelector(
105-
Hyperstaker.initialize.selector, address(hypercertMinter), hypercertTypeId, admin, manager, pauser, upgrader
109+
Hyperstaker.initialize.selector, address(hypercertMinter), hypercertTypeId, admin, manager, pauser, upgrader, feeRecipient, feePercentage
106110
);
107-
ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initData);
108-
109-
address newHyperstaker = address(proxy);
110-
require(newHyperstaker != address(0), DeploymentFailed());
111111

112+
address newHyperstaker = _deployContract(address(implementation), initData);
113+
112114
hyperstakers[hypercertTypeId] = true;
113-
emit HyperstakerCreated(newHyperstaker, hypercertTypeId, admin, manager, pauser, upgrader);
115+
emit HyperstakerCreated(newHyperstaker, hypercertTypeId, admin, manager, pauser, upgrader, feeRecipient, feePercentage);
114116
return newHyperstaker;
115117
}
116118

@@ -122,31 +124,39 @@ contract HyperfundFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable
122124
/// @param upgrader address of the upgrader of the Hyperfund and Hyperstaker
123125
/// @return hyperfund address of the new Hyperfund
124126
/// @return hyperstaker address of the new Hyperstaker
125-
function createProject(uint256 hypercertTypeId, address admin, address manager, address pauser, address upgrader)
127+
function createProject(uint256 hypercertTypeId, address admin, address manager, address pauser, address upgrader, address feeRecipient, uint256 feePercentage)
126128
external
127129
returns (address hyperfund, address hyperstaker)
128130
{
129131
require(manager != address(0), InvalidAddress());
130132
require(hyperfunds[hypercertTypeId] == false, AlreadyDeployed());
131133
require(msg.sender == IHypercertToken(hypercertMinter).ownerOf(hypercertTypeId + 1), NotOwnerOfHypercert());
132134

135+
Hyperfund hyperfundImplementation = new Hyperfund();
133136
bytes memory initData = abi.encodeWithSelector(
134-
Hyperfund.initialize.selector, address(hypercertMinter), hypercertTypeId, admin, manager, pauser, upgrader
137+
Hyperfund.initialize.selector, address(hypercertMinter), hypercertTypeId, admin, manager, pauser, upgrader, feeRecipient, feePercentage
135138
);
136139

137-
Hyperfund hyperfundImplementation = new Hyperfund();
138-
ERC1967Proxy hyperfundProxy = new ERC1967Proxy(address(hyperfundImplementation), initData);
139-
hyperfund = address(hyperfundProxy);
140-
require(hyperfund != address(0), DeploymentFailed());
140+
hyperfund = _deployContract(address(hyperfundImplementation), initData);
141+
IHypercertToken(hypercertMinter).setApprovalForAll(hyperfund, true);
141142
hyperfunds[hypercertTypeId] = true;
142-
emit HyperfundCreated(hyperfund, hypercertTypeId, admin, manager, pauser, upgrader);
143+
emit HyperfundCreated(hyperfund, hypercertTypeId, admin, manager, pauser, upgrader, feeRecipient, feePercentage);
143144

144145
Hyperstaker hyperstakerImplementation = new Hyperstaker();
145-
ERC1967Proxy proxy = new ERC1967Proxy(address(hyperstakerImplementation), initData);
146-
hyperstaker = address(proxy);
147-
require(hyperstaker != address(0), DeploymentFailed());
146+
hyperstaker = _deployContract(address(hyperstakerImplementation), initData);
148147
hyperstakers[hypercertTypeId] = true;
149-
emit HyperstakerCreated(hyperstaker, hypercertTypeId, admin, manager, pauser, upgrader);
148+
emit HyperstakerCreated(hyperstaker, hypercertTypeId, admin, manager, pauser, upgrader, feeRecipient, feePercentage);
149+
}
150+
151+
/// @notice Internal helper to create a new contract instance
152+
/// @param implementation The implementation contract address
153+
/// @param initData The initialization data for the contract
154+
/// @return The address of the newly created contract
155+
function _deployContract(address implementation, bytes memory initData) internal returns (address) {
156+
ERC1967Proxy proxy = new ERC1967Proxy(implementation, initData);
157+
address newContract = address(proxy);
158+
require(newContract != address(0), DeploymentFailed());
159+
return newContract;
150160
}
151161

152162
/**

src/Hyperstaker.sol

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ contract Hyperstaker is AccessControlUpgradeable, PausableUpgradeable, UUPSUpgra
2424
IHypercertToken public hypercertMinter;
2525
uint256 public hypercertTypeId;
2626
uint256 public totalUnits;
27+
address public feeRecipient;
28+
/// @notice fee percentage, 10000 = 100%
29+
uint256 public feePercentage;
2730

2831
mapping(uint256 hypercertId => Stake stake) public stakes;
2932
Round[] public rounds;
@@ -74,13 +77,17 @@ contract Hyperstaker is AccessControlUpgradeable, PausableUpgradeable, UUPSUpgra
7477
/// @param _manager The address that will have the MANAGER_ROLE
7578
/// @param _pauser The address that will have the PAUSER_ROLE
7679
/// @param _upgrader The address that will have the UPGRADER_ROLE
80+
/// @param _feeRecipient The address that will receive the fees on rewards
81+
/// @param _feePercentage The percentage of the reward that will be sent to the fee recipient
7782
function initialize(
7883
address _hypercertMinter,
7984
uint256 _hypercertTypeId,
8085
address _admin,
8186
address _manager,
8287
address _pauser,
83-
address _upgrader
88+
address _upgrader,
89+
address _feeRecipient,
90+
uint256 _feePercentage
8491
) public initializer {
8592
__AccessControl_init();
8693
__Pausable_init();
@@ -97,6 +104,8 @@ contract Hyperstaker is AccessControlUpgradeable, PausableUpgradeable, UUPSUpgra
97104
Round memory round;
98105
round.startTime = block.timestamp;
99106
rounds.push(round);
107+
feeRecipient = _feeRecipient;
108+
feePercentage = _feePercentage;
100109
}
101110

102111
function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {}

test/HyperfundFactory.t.sol

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ contract HyperfundFactoryTest is Test {
2121
address public hypercertMinter;
2222
uint256 hypercertId;
2323
address manager;
24+
address feeRecipient;
25+
uint256 feePercentage = 100;
2426
uint256 public totalUnits = 100000000;
2527

2628
event Upgraded(address indexed implementation);
@@ -40,6 +42,7 @@ contract HyperfundFactoryTest is Test {
4042
hyperfundFactory = HyperfundFactory(address(proxy));
4143

4244
manager = address(this);
45+
feeRecipient = vm.addr(1);
4346
// hypercertId = HT(hypercertMinter).mintClaim(
4447
// address(this), totalUnits, "uri", HT.TransferRestrictions.AllowAll
4548
// );
@@ -101,9 +104,9 @@ contract HyperfundFactoryTest is Test {
101104
vm.expectEmit(false, true, false, true);
102105
// We can't know the hyperfund address beforehand, but we can emit a dummy event
103106
// with the other parameters we expect
104-
emit HyperfundFactory.HyperfundCreated(address(0), hypercertId, manager, manager, manager, manager);
107+
emit HyperfundFactory.HyperfundCreated(address(0), hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
105108

106-
address hyperfundAddress = hyperfundFactory.createHyperfund(hypercertId, manager, manager, manager, manager);
109+
address hyperfundAddress = hyperfundFactory.createHyperfund(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
107110

108111
bool createdHyperfund = hyperfundFactory.hyperfunds(hypercertId);
109112
assertTrue(createdHyperfund != false, "Hyperfund should be created and mapped correctly");
@@ -115,37 +118,37 @@ contract HyperfundFactoryTest is Test {
115118
vm.expectEmit(false, true, false, true);
116119
// We can't know the hyperfund address beforehand, but we can emit a dummy event
117120
// with the other parameters we expect
118-
emit HyperfundFactory.HyperstakerCreated(address(0), hypercertId, manager, manager, manager, manager);
121+
emit HyperfundFactory.HyperstakerCreated(address(0), hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
119122

120-
address hyperstakerAddress = hyperfundFactory.createHyperstaker(hypercertId, manager, manager, manager, manager);
123+
address hyperstakerAddress = hyperfundFactory.createHyperstaker(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
121124

122125
bool createdHyperstaker = hyperfundFactory.hyperstakers(hypercertId);
123126
assertTrue(createdHyperstaker != false, "Hyperstaker should be created and mapped correctly");
124127
assertEq(Hyperstaker(hyperstakerAddress).hypercertTypeId(), hypercertId);
125128
}
126129

127130
function test_RevertWhen_RedeployingHyperfundWithSameHypercertId() public {
128-
hyperfundFactory.createHyperfund(hypercertId, manager, manager, manager, manager);
131+
hyperfundFactory.createHyperfund(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
129132

130133
vm.expectRevert(HyperfundFactory.AlreadyDeployed.selector);
131-
hyperfundFactory.createHyperfund(hypercertId, manager, manager, manager, manager);
134+
hyperfundFactory.createHyperfund(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
132135
}
133136

134137
function test_RevertWhen_RedeployingHyperStakerWithSameHypercertId() public {
135-
hyperfundFactory.createHyperstaker(hypercertId, manager, manager, manager, manager);
138+
hyperfundFactory.createHyperstaker(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
136139

137140
vm.expectRevert(HyperfundFactory.AlreadyDeployed.selector);
138-
hyperfundFactory.createHyperstaker(hypercertId, manager, manager, manager, manager);
141+
hyperfundFactory.createHyperstaker(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
139142
}
140143

141144
function test_RevertWhen_CreateHyperfundZeroManager() public {
142145
vm.expectRevert(HyperfundFactory.InvalidAddress.selector);
143-
hyperfundFactory.createHyperfund(hypercertId, address(0), address(0), address(0), address(0));
146+
hyperfundFactory.createHyperfund(hypercertId, address(0), address(0), address(0), address(0), feeRecipient, feePercentage);
144147
}
145148

146149
function test_RevertWhen_CreateHyperstakerZeroManager() public {
147150
vm.expectRevert(HyperfundFactory.InvalidAddress.selector);
148-
hyperfundFactory.createHyperstaker(hypercertId, address(0), address(0), address(0), address(0));
151+
hyperfundFactory.createHyperstaker(hypercertId, address(0), address(0), address(0), address(0), feeRecipient, feePercentage);
149152
}
150153

151154
function test_RevertWhen_FailedHyperfundDeployment() public {
@@ -170,7 +173,7 @@ contract HyperfundFactoryTest is Test {
170173
HyperfundFactory factory = HyperfundFactory(address(proxy));
171174

172175
vm.expectRevert();
173-
factory.createHyperfund(hypercertId, manager, manager, manager, manager);
176+
factory.createHyperfund(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
174177
}
175178

176179
function test_RevertWhen_FailedHyperstakerDeployment() public {
@@ -195,24 +198,24 @@ contract HyperfundFactoryTest is Test {
195198
HyperfundFactory factory = HyperfundFactory(address(proxy));
196199

197200
vm.expectRevert();
198-
factory.createHyperstaker(hypercertId, manager, manager, manager, manager);
201+
factory.createHyperstaker(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
199202
}
200203

201204
function test_RevertWhen_HyperfundCreatorNotOwnerOfHypercert() public {
202205
vm.prank(makeAddr("user2"));
203206
vm.expectRevert(HyperfundFactory.NotOwnerOfHypercert.selector);
204-
hyperfundFactory.createHyperfund(hypercertId, manager, manager, manager, manager);
207+
hyperfundFactory.createHyperfund(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
205208
}
206209

207210
function test_RevertWhen_HyperstakerCreatorNotOwnerOfHypercert() public {
208211
vm.prank(makeAddr("user2"));
209212
vm.expectRevert(HyperfundFactory.NotOwnerOfHypercert.selector);
210-
hyperfundFactory.createHyperstaker(hypercertId, manager, manager, manager, manager);
213+
hyperfundFactory.createHyperstaker(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
211214
}
212215

213216
function test_CreateProject() public {
214217
(address hyperfundAddress, address hyperstakerAddress) =
215-
hyperfundFactory.createProject(hypercertId, manager, manager, manager, manager);
218+
hyperfundFactory.createProject(hypercertId, manager, manager, manager, manager, feeRecipient, feePercentage);
216219
assertTrue(hyperfundFactory.hyperfunds(hypercertId));
217220
assertTrue(hyperfundFactory.hyperstakers(hypercertId));
218221
assertEq(Hyperfund(hyperfundAddress).hypercertTypeId(), hypercertId);

test/Hyperstaker.t.sol

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ contract HyperstakerTest is Test {
2020
address public manager = vm.addr(1);
2121
address public staker = vm.addr(2);
2222
address public staker2 = vm.addr(3);
23+
address public feeRecipient = vm.addr(4);
24+
uint256 public feePercentage = 100;
2325
uint256 public totalUnits = 100000000;
2426
uint256 public stakeAmount = 10000;
2527
uint256 public rewardAmount = 10 ether;
@@ -49,7 +51,9 @@ contract HyperstakerTest is Test {
4951
manager,
5052
manager,
5153
manager,
52-
manager
54+
manager,
55+
feeRecipient,
56+
feePercentage
5357
);
5458

5559
proxy = new ERC1967Proxy(address(implementation), initData);
@@ -68,6 +72,8 @@ contract HyperstakerTest is Test {
6872
assertEq(address(hyperstaker.hypercertMinter()), address(hypercertMinter));
6973
assertEq(hyperstaker.totalUnits(), totalUnits);
7074
assertEq(hyperstaker.getRoundInfo(0).startTime, roundStartTime);
75+
assertEq(hyperstaker.feeRecipient(), feeRecipient);
76+
assertEq(hyperstaker.feePercentage(), feePercentage);
7177
}
7278

7379
function test_SetReward_ERC20() public {

0 commit comments

Comments
 (0)