-
Notifications
You must be signed in to change notification settings - Fork 48
/
SablierMerkleFactory.sol
295 lines (255 loc) · 11.3 KB
/
SablierMerkleFactory.sol
1
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.22;
import { uUNIT } from "@prb/math/src/UD2x18.sol";
import { Adminable } from "../core/abstracts/Adminable.sol";
import { ISablierLockupLinear } from "../core/interfaces/ISablierLockupLinear.sol";
import { ISablierLockupTranched } from "../core/interfaces/ISablierLockupTranched.sol";
import { ISablierMerkleBase } from "./interfaces/ISablierMerkleBase.sol";
import { ISablierMerkleFactory } from "./interfaces/ISablierMerkleFactory.sol";
import { ISablierMerkleInstant } from "./interfaces/ISablierMerkleInstant.sol";
import { ISablierMerkleLL } from "./interfaces/ISablierMerkleLL.sol";
import { ISablierMerkleLT } from "./interfaces/ISablierMerkleLT.sol";
import { Errors } from "./libraries/Errors.sol";
import { SablierMerkleInstant } from "./SablierMerkleInstant.sol";
import { SablierMerkleLL } from "./SablierMerkleLL.sol";
import { SablierMerkleLT } from "./SablierMerkleLT.sol";
import { MerkleBase, MerkleFactory, MerkleLL, MerkleLT } from "./types/DataTypes.sol";
/// @title SablierMerkleFactory
/// @notice See the documentation in {ISablierMerkleFactory}.
contract SablierMerkleFactory is
ISablierMerkleFactory, // 2 inherited components
Adminable // 1 inherited component
{
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/
/// @inheritdoc ISablierMerkleFactory
uint256 public override defaultSablierFee;
/// @dev A mapping of custom Sablier fees by user.
mapping(address campaignCreator => MerkleFactory.SablierFeeByUser customFee) private _sablierFeeByUsers;
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
/// @dev Emits a {TransferAdmin} event.
/// @param initialAdmin The address of the initial contract admin.
constructor(address initialAdmin) Adminable(initialAdmin) { }
/*//////////////////////////////////////////////////////////////////////////
USER-FACING CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @inheritdoc ISablierMerkleFactory
function isPercentagesSum100(MerkleLT.TrancheWithPercentage[] calldata tranches)
external
pure
override
returns (bool result)
{
uint64 totalPercentage;
for (uint256 i = 0; i < tranches.length; ++i) {
totalPercentage += tranches[i].unlockPercentage.unwrap();
}
return totalPercentage == uUNIT;
}
/// @inheritdoc ISablierMerkleFactory
function sablierFeeByUser(address campaignCreator)
external
view
override
returns (MerkleFactory.SablierFeeByUser memory)
{
return _sablierFeeByUsers[campaignCreator];
}
/*//////////////////////////////////////////////////////////////////////////
ADMIN-FACING NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @inheritdoc ISablierMerkleFactory
function resetSablierFeeByUser(address campaignCreator) external override onlyAdmin {
delete _sablierFeeByUsers[campaignCreator];
// Log the reset.
emit ResetSablierFee({ admin: msg.sender, campaignCreator: campaignCreator });
}
/// @inheritdoc ISablierMerkleFactory
function setDefaultSablierFee(uint256 defaultFee) external override onlyAdmin {
// Effect: update the default Sablier fee.
defaultSablierFee = defaultFee;
emit SetDefaultSablierFee(msg.sender, defaultFee);
}
/// @inheritdoc ISablierMerkleFactory
function setSablierFeeByUser(address campaignCreator, uint256 fee) external override onlyAdmin {
MerkleFactory.SablierFeeByUser storage feeByUser = _sablierFeeByUsers[campaignCreator];
// Check: if user does not belong to the custom fee list.
if (!feeByUser.enabled) feeByUser.enabled = true;
// Effect: update the Sablier fee for the given campaign creator.
feeByUser.fee = fee;
// Log the update.
emit SetSablierFeeForUser({ admin: msg.sender, campaignCreator: campaignCreator, sablierFee: fee });
}
/// @inheritdoc ISablierMerkleFactory
function withdrawFees(address payable to, ISablierMerkleBase merkleBase) external override onlyAdmin {
// Check: the withdrawal address is not zero.
if (to == address(0)) {
revert Errors.SablierMerkleFactory_WithdrawToZeroAddress();
}
// Effect: call `withdrawFees` on the MerkleBase contract.
uint256 fees = merkleBase.withdrawFees(to);
// Log the withdrawal.
emit WithdrawSablierFees({ admin: msg.sender, merkleBase: merkleBase, to: to, sablierFees: fees });
}
/*//////////////////////////////////////////////////////////////////////////
USER-FACING NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @inheritdoc ISablierMerkleFactory
function createMerkleInstant(
MerkleBase.ConstructorParams memory baseParams,
uint256 aggregateAmount,
uint256 recipientCount
)
external
override
returns (ISablierMerkleInstant merkleInstant)
{
// Hash the parameters to generate a salt.
bytes32 salt = keccak256(
abi.encodePacked(
msg.sender,
baseParams.asset,
baseParams.expiration,
baseParams.initialAdmin,
abi.encode(baseParams.ipfsCID),
baseParams.merkleRoot,
bytes32(abi.encodePacked(baseParams.name))
)
);
// Compute the Sablier fee for the user.
uint256 sablierFee = _computeSablierFeeForUser(msg.sender);
// Deploy the MerkleInstant contract with CREATE2.
merkleInstant = new SablierMerkleInstant{ salt: salt }(baseParams, sablierFee);
// Log the creation of the MerkleInstant contract, including some metadata that is not stored on-chain.
emit CreateMerkleInstant(merkleInstant, baseParams, aggregateAmount, recipientCount);
}
/// @inheritdoc ISablierMerkleFactory
function createMerkleLL(
MerkleBase.ConstructorParams memory baseParams,
ISablierLockupLinear lockupLinear,
bool cancelable,
bool transferable,
MerkleLL.Schedule memory schedule,
uint256 aggregateAmount,
uint256 recipientCount
)
external
override
returns (ISablierMerkleLL merkleLL)
{
// Hash the parameters to generate a salt.
bytes32 salt = keccak256(
abi.encodePacked(
msg.sender,
baseParams.asset,
baseParams.expiration,
baseParams.initialAdmin,
abi.encode(baseParams.ipfsCID),
baseParams.merkleRoot,
bytes32(abi.encodePacked(baseParams.name)),
lockupLinear,
cancelable,
transferable,
abi.encode(schedule)
)
);
// Compute the Sablier fee for the user.
uint256 sablierFee = _computeSablierFeeForUser(msg.sender);
// Deploy the MerkleLL contract with CREATE2.
merkleLL =
new SablierMerkleLL{ salt: salt }(baseParams, lockupLinear, cancelable, transferable, schedule, sablierFee);
// Log the creation of the MerkleLL contract, including some metadata that is not stored on-chain.
emit CreateMerkleLL(
merkleLL, baseParams, lockupLinear, cancelable, transferable, schedule, aggregateAmount, recipientCount
);
}
/// @inheritdoc ISablierMerkleFactory
function createMerkleLT(
MerkleBase.ConstructorParams memory baseParams,
ISablierLockupTranched lockupTranched,
bool cancelable,
bool transferable,
uint40 streamStartTime,
MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages,
uint256 aggregateAmount,
uint256 recipientCount
)
external
override
returns (ISablierMerkleLT merkleLT)
{
// Calculate the sum of percentages and durations across all tranches.
uint256 count = tranchesWithPercentages.length;
uint256 totalDuration;
for (uint256 i = 0; i < count; ++i) {
unchecked {
// Safe to use `unchecked` because its only used in the event.
totalDuration += tranchesWithPercentages[i].duration;
}
}
// Deploy the MerkleLT contract.
merkleLT = _deployMerkleLT(
baseParams, lockupTranched, cancelable, transferable, streamStartTime, tranchesWithPercentages
);
// Log the creation of the MerkleLT contract, including some metadata that is not stored on-chain.
emit CreateMerkleLT(
merkleLT,
baseParams,
lockupTranched,
cancelable,
transferable,
streamStartTime,
tranchesWithPercentages,
totalDuration,
aggregateAmount,
recipientCount
);
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL NON-CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @dev Computes the Sablier fee for the user, use the default fee if not enabled.
function _computeSablierFeeForUser(address user) internal view returns (uint256) {
return _sablierFeeByUsers[user].enabled ? _sablierFeeByUsers[user].fee : defaultSablierFee;
}
/// @notice Deploys a new MerkleLT contract with CREATE2.
/// @dev We need a separate function to prevent the stack too deep error.
function _deployMerkleLT(
MerkleBase.ConstructorParams memory baseParams,
ISablierLockupTranched lockupTranched,
bool cancelable,
bool transferable,
uint40 streamStartTime,
MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages
)
internal
returns (ISablierMerkleLT merkleLT)
{
// Hash the parameters to generate a salt.
bytes32 salt = keccak256(
abi.encodePacked(
msg.sender,
baseParams.asset,
baseParams.expiration,
baseParams.initialAdmin,
abi.encode(baseParams.ipfsCID),
baseParams.merkleRoot,
bytes32(abi.encodePacked(baseParams.name)),
lockupTranched,
cancelable,
transferable,
streamStartTime,
abi.encode(tranchesWithPercentages)
)
);
// Compute the Sablier fee for the user.
uint256 sablierFee = _computeSablierFeeForUser(msg.sender);
// Deploy the MerkleLT contract with CREATE2.
merkleLT = new SablierMerkleLT{ salt: salt }(
baseParams, lockupTranched, cancelable, transferable, streamStartTime, tranchesWithPercentages, sablierFee
);
}
}