@ballkidz/defifa 0.0.28 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -2
- package/CHANGELOG.md +2 -2
- package/CRYPTO_ECON.md +4 -4
- package/README.md +3 -3
- package/RISKS.md +1 -1
- package/SKILLS.md +1 -1
- package/STYLE_GUIDE.md +2 -50
- package/package.json +2 -2
- package/src/DefifaDeployer.sol +78 -61
- package/src/DefifaGovernor.sol +74 -63
- package/src/DefifaHook.sol +127 -183
- package/src/DefifaProjectOwner.sol +4 -2
- package/src/DefifaTokenUriResolver.sol +28 -16
- package/src/interfaces/IDefifaDeployer.sol +0 -16
- package/src/interfaces/IDefifaHook.sol +0 -4
- package/src/libraries/DefifaHookLib.sol +261 -151
|
@@ -17,78 +17,162 @@ import {DefifaTierCashOutWeight} from "../structs/DefifaTierCashOutWeight.sol";
|
|
|
17
17
|
library DefifaHookLib {
|
|
18
18
|
using SafeERC20 for IERC20;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
//*********************************************************************//
|
|
21
|
+
// --------------------------- custom errors ------------------------- //
|
|
22
|
+
//*********************************************************************//
|
|
23
|
+
|
|
24
|
+
error DefifaHook_BadTierOrder(uint256 previousTierId, uint256 tierId);
|
|
25
|
+
error DefifaHook_InvalidTierId(uint256 tierId, uint256 actualTierId, uint256 maxTierId, uint256 category);
|
|
26
|
+
error DefifaHook_InvalidCashoutWeights(uint256 totalWeight, uint256 expectedWeight);
|
|
27
|
+
|
|
28
|
+
//*********************************************************************//
|
|
29
|
+
// ----------------------------- events ------------------------------ //
|
|
30
|
+
//*********************************************************************//
|
|
23
31
|
|
|
24
32
|
event ClaimedTokens(
|
|
25
33
|
address indexed beneficiary, uint256 defifaTokenAmount, uint256 baseProtocolTokenAmount, address caller
|
|
26
34
|
);
|
|
27
35
|
|
|
36
|
+
//*********************************************************************//
|
|
37
|
+
// ----------------------- internal constants ------------------------ //
|
|
38
|
+
//*********************************************************************//
|
|
39
|
+
|
|
28
40
|
/// @notice The total cashOut weight that can be divided among tiers.
|
|
29
41
|
uint256 internal constant TOTAL_CASHOUT_WEIGHT = 1_000_000_000_000_000_000;
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
//*********************************************************************//
|
|
44
|
+
// -------------------------- public views --------------------------- //
|
|
45
|
+
//*********************************************************************//
|
|
46
|
+
|
|
47
|
+
/// @notice Returns the adjusted pending reserve count for a tier, accounting for refund-phase burns.
|
|
48
|
+
/// @param tierId The tier ID.
|
|
33
49
|
/// @param hookStore The 721 tiers hook store.
|
|
34
50
|
/// @param hook The hook address.
|
|
35
|
-
/// @
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
/// @param refundBurns The number of refund-phase burns for the tier.
|
|
52
|
+
/// @return The adjusted pending reserve count.
|
|
53
|
+
function adjustedPendingReservesFor(
|
|
54
|
+
uint256 tierId,
|
|
38
55
|
IJB721TiersHookStore hookStore,
|
|
39
|
-
address hook
|
|
56
|
+
address hook,
|
|
57
|
+
uint256 refundBurns
|
|
40
58
|
)
|
|
41
59
|
public
|
|
42
60
|
view
|
|
43
|
-
returns (uint256
|
|
61
|
+
returns (uint256)
|
|
44
62
|
{
|
|
45
|
-
//
|
|
46
|
-
|
|
63
|
+
// If no refund burns, return the store's value directly.
|
|
64
|
+
if (refundBurns == 0) return hookStore.numberOfPendingReservesFor({hook: hook, tierId: tierId});
|
|
47
65
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Keep a reference to the number of tier weights.
|
|
52
|
-
uint256 numberOfTierWeights = tierWeights.length;
|
|
66
|
+
// Get the tier to access reserveFrequency and supply data.
|
|
67
|
+
JB721Tier memory tier = hookStore.tierOf({hook: hook, id: tierId, includeResolvedUri: false});
|
|
53
68
|
|
|
54
|
-
//
|
|
55
|
-
|
|
69
|
+
// No reserves if no reserve frequency.
|
|
70
|
+
if (tier.reserveFrequency == 0) return 0;
|
|
56
71
|
|
|
57
|
-
//
|
|
58
|
-
uint256
|
|
72
|
+
// Calculate the number of reserves already minted.
|
|
73
|
+
uint256 reservesMinted = hookStore.numberOfReservesMintedFor({hook: hook, tierId: tierId});
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (tierWeights[i].id <= lastTierId && i != 0) revert DefifaHook_BadTierOrder();
|
|
63
|
-
lastTierId = tierWeights[i].id;
|
|
75
|
+
// Calculate non-reserve mints: initialSupply - remainingSupply - reservesMinted.
|
|
76
|
+
uint256 nonReserveMints = tier.initialSupply - tier.remainingSupply - reservesMinted;
|
|
64
77
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
tier = hookStore.tierOf({hook: hook, id: tierWeights[i].id, includeResolvedUri: false});
|
|
78
|
+
// Subtract refund burns from non-reserve mints (burns can't exceed non-reserve mints).
|
|
79
|
+
uint256 adjustedMints = nonReserveMints > refundBurns ? nonReserveMints - refundBurns : 0;
|
|
68
80
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
// Recalculate available reserves: ceil(adjustedMints / reserveFrequency).
|
|
82
|
+
uint256 availableReserves = adjustedMints / tier.reserveFrequency;
|
|
83
|
+
if (adjustedMints % tier.reserveFrequency > 0) ++availableReserves;
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
85
|
+
// Return pending = available - already minted (floored at 0).
|
|
86
|
+
return availableReserves > reservesMinted ? availableReserves - reservesMinted : 0;
|
|
87
|
+
}
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
/// @notice Computes the attestation units for tiers during payment processing.
|
|
90
|
+
/// @dev Returns parallel arrays: tier IDs, cumulative attestation units per tier, and whether to switch delegate.
|
|
91
|
+
/// @param tierIdsToMint The tier IDs to mint (must be in ascending order).
|
|
92
|
+
/// @param hookStore The 721 tiers hook store.
|
|
93
|
+
/// @param hook The hook address.
|
|
94
|
+
/// @return tierIds The unique tier IDs.
|
|
95
|
+
/// @return attestationAmounts The cumulative attestation units for each unique tier.
|
|
96
|
+
/// @return count The number of unique tiers.
|
|
97
|
+
function computeAttestationUnits(
|
|
98
|
+
uint16[] memory tierIdsToMint,
|
|
99
|
+
IJB721TiersHookStore hookStore,
|
|
100
|
+
address hook
|
|
101
|
+
)
|
|
102
|
+
public
|
|
103
|
+
view
|
|
104
|
+
returns (uint256[] memory tierIds, uint256[] memory attestationAmounts, uint256 count)
|
|
105
|
+
{
|
|
106
|
+
uint256 numberOfTiers = tierIdsToMint.length;
|
|
107
|
+
tierIds = new uint256[](numberOfTiers);
|
|
108
|
+
attestationAmounts = new uint256[](numberOfTiers);
|
|
78
109
|
|
|
79
|
-
|
|
80
|
-
weights[tier.id - 1] = tierWeights[i].cashOutWeight;
|
|
110
|
+
if (numberOfTiers == 0) return (tierIds, attestationAmounts, 0);
|
|
81
111
|
|
|
82
|
-
|
|
83
|
-
|
|
112
|
+
uint256 currentTierId;
|
|
113
|
+
uint256 attestationUnits;
|
|
114
|
+
uint256 accumulated;
|
|
84
115
|
|
|
116
|
+
for (uint256 i; i < numberOfTiers;) {
|
|
117
|
+
if (currentTierId != tierIdsToMint[i]) {
|
|
118
|
+
// Flush accumulated units for previous tier.
|
|
119
|
+
if (currentTierId != 0) {
|
|
120
|
+
tierIds[count] = currentTierId;
|
|
121
|
+
attestationAmounts[count] = accumulated;
|
|
122
|
+
count++;
|
|
123
|
+
}
|
|
124
|
+
if (tierIdsToMint[i] < currentTierId) {
|
|
125
|
+
revert DefifaHook_BadTierOrder({previousTierId: currentTierId, tierId: tierIdsToMint[i]});
|
|
126
|
+
}
|
|
127
|
+
currentTierId = tierIdsToMint[i];
|
|
128
|
+
attestationUnits =
|
|
129
|
+
hookStore.tierOf({hook: hook, id: currentTierId, includeResolvedUri: false}).votingUnits;
|
|
130
|
+
accumulated = attestationUnits;
|
|
131
|
+
} else {
|
|
132
|
+
accumulated += attestationUnits;
|
|
133
|
+
}
|
|
85
134
|
unchecked {
|
|
86
135
|
++i;
|
|
87
136
|
}
|
|
88
137
|
}
|
|
138
|
+
// Flush the last tier.
|
|
139
|
+
if (currentTierId != 0) {
|
|
140
|
+
tierIds[count] = currentTierId;
|
|
141
|
+
attestationAmounts[count] = accumulated;
|
|
142
|
+
count++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
89
145
|
|
|
90
|
-
|
|
91
|
-
|
|
146
|
+
/// @notice Compute the cash out count for the beforeCashOutRecorded hook.
|
|
147
|
+
/// @param gamePhase The current game phase.
|
|
148
|
+
/// @param cumulativeMintPrice The cumulative mint price of the tokens to cash out.
|
|
149
|
+
/// @param surplusValue The surplus value from the context.
|
|
150
|
+
/// @param totalAmountRedeemed The amount already redeemed.
|
|
151
|
+
/// @param cumulativeCashOutWeight The cumulative cash out weight of the tokens.
|
|
152
|
+
/// @return cashOutCount The computed cash out count.
|
|
153
|
+
function computeCashOutCount(
|
|
154
|
+
DefifaGamePhase gamePhase,
|
|
155
|
+
uint256 cumulativeMintPrice,
|
|
156
|
+
uint256 surplusValue,
|
|
157
|
+
uint256 totalAmountRedeemed,
|
|
158
|
+
uint256 cumulativeCashOutWeight
|
|
159
|
+
)
|
|
160
|
+
public
|
|
161
|
+
pure
|
|
162
|
+
returns (uint256 cashOutCount)
|
|
163
|
+
{
|
|
164
|
+
// If the game is in its minting, refund, or no-contest phase, reclaim amount is the same as it cost to mint.
|
|
165
|
+
if (
|
|
166
|
+
gamePhase == DefifaGamePhase.MINT || gamePhase == DefifaGamePhase.REFUND
|
|
167
|
+
|| gamePhase == DefifaGamePhase.NO_CONTEST
|
|
168
|
+
) {
|
|
169
|
+
cashOutCount = cumulativeMintPrice;
|
|
170
|
+
} else {
|
|
171
|
+
// If the game is in its scoring or complete phase, reclaim amount is based on the tier weights.
|
|
172
|
+
cashOutCount = mulDiv({
|
|
173
|
+
x: surplusValue + totalAmountRedeemed, y: cumulativeCashOutWeight, denominator: TOTAL_CASHOUT_WEIGHT
|
|
174
|
+
});
|
|
175
|
+
}
|
|
92
176
|
}
|
|
93
177
|
|
|
94
178
|
/// @notice Compute the cash out weight for a single token.
|
|
@@ -110,11 +194,9 @@ library DefifaHookLib {
|
|
|
110
194
|
returns (uint256)
|
|
111
195
|
{
|
|
112
196
|
// Keep a reference to the token's tier ID.
|
|
113
|
-
// slither-disable-next-line calls-loop
|
|
114
197
|
uint256 tierId = hookStore.tierIdOfToken(tokenId);
|
|
115
198
|
|
|
116
199
|
// Keep a reference to the tier.
|
|
117
|
-
// slither-disable-next-line calls-loop
|
|
118
200
|
JB721Tier memory tier = hookStore.tierOf({hook: hook, id: tierId, includeResolvedUri: false});
|
|
119
201
|
|
|
120
202
|
// Get the tier's weight.
|
|
@@ -124,7 +206,6 @@ library DefifaHookLib {
|
|
|
124
206
|
if (weight == 0) return 0;
|
|
125
207
|
|
|
126
208
|
// Get the amount of tokens that have already been burned.
|
|
127
|
-
// slither-disable-next-line calls-loop
|
|
128
209
|
uint256 burnedTokens = hookStore.numberOfBurnedFor({hook: hook, tierId: tierId});
|
|
129
210
|
|
|
130
211
|
// If no tiers were minted, nothing to redeem.
|
|
@@ -135,7 +216,6 @@ library DefifaHookLib {
|
|
|
135
216
|
tier.initialSupply - tier.remainingSupply - (burnedTokens - tokensRedeemedFrom[tierId]);
|
|
136
217
|
|
|
137
218
|
// Include pending (unminted) reserve NFTs in the denominator, adjusted for refund-phase burns.
|
|
138
|
-
// slither-disable-next-line calls-loop
|
|
139
219
|
totalTokensForCashoutInTier += IDefifaHook(hook).adjustedPendingReservesFor(tierId);
|
|
140
220
|
|
|
141
221
|
// Calculate the percentage of the tier cashOut amount a single token counts for.
|
|
@@ -178,6 +258,49 @@ library DefifaHookLib {
|
|
|
178
258
|
}
|
|
179
259
|
}
|
|
180
260
|
|
|
261
|
+
/// @notice Compute the cumulative mint price for a set of token IDs.
|
|
262
|
+
/// @param tokenIds The token IDs.
|
|
263
|
+
/// @param hookStore The 721 tiers hook store.
|
|
264
|
+
/// @param hook The hook address.
|
|
265
|
+
/// @param excludeReserveMints Whether reserve-minted tokens should be excluded.
|
|
266
|
+
/// @return cumulativeMintPrice The total mint price.
|
|
267
|
+
function computeCumulativeMintPriceForCashOut(
|
|
268
|
+
uint256[] memory tokenIds,
|
|
269
|
+
IJB721TiersHookStore hookStore,
|
|
270
|
+
address hook,
|
|
271
|
+
bool excludeReserveMints
|
|
272
|
+
)
|
|
273
|
+
public
|
|
274
|
+
view
|
|
275
|
+
returns (uint256 cumulativeMintPrice)
|
|
276
|
+
{
|
|
277
|
+
uint256 numberOfTokenIds = tokenIds.length;
|
|
278
|
+
for (uint256 i; i < numberOfTokenIds; i++) {
|
|
279
|
+
if (excludeReserveMints && IDefifaHook(hook).isReserveMint(tokenIds[i])) continue;
|
|
280
|
+
cumulativeMintPrice += hookStore.tierOfTokenId({
|
|
281
|
+
hook: hook, tokenId: tokenIds[i], includeResolvedUri: false
|
|
282
|
+
}).price;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/// @notice Compute the current supply of a tier (minted - burned).
|
|
287
|
+
/// @param hookStore The 721 tiers hook store.
|
|
288
|
+
/// @param hook The hook address.
|
|
289
|
+
/// @param tierId The ID of the tier.
|
|
290
|
+
/// @return The current supply.
|
|
291
|
+
function computeCurrentSupply(
|
|
292
|
+
IJB721TiersHookStore hookStore,
|
|
293
|
+
address hook,
|
|
294
|
+
uint256 tierId
|
|
295
|
+
)
|
|
296
|
+
public
|
|
297
|
+
view
|
|
298
|
+
returns (uint256)
|
|
299
|
+
{
|
|
300
|
+
JB721Tier memory tier = hookStore.tierOf({hook: hook, id: tierId, includeResolvedUri: false});
|
|
301
|
+
return tier.initialSupply - (tier.remainingSupply + hookStore.numberOfBurnedFor({hook: hook, tierId: tierId}));
|
|
302
|
+
}
|
|
303
|
+
|
|
181
304
|
/// @notice Compute the claimable token amounts for a set of token IDs.
|
|
182
305
|
/// @param tokenIds The token IDs.
|
|
183
306
|
/// @param hookStore The 721 tiers hook store.
|
|
@@ -208,7 +331,6 @@ library DefifaHookLib {
|
|
|
208
331
|
// Calculate the amount paid to mint the tokens that are being burned.
|
|
209
332
|
uint256 cumulativeMintPrice;
|
|
210
333
|
for (uint256 i; i < numberOfTokens; i++) {
|
|
211
|
-
// slither-disable-next-line calls-loop
|
|
212
334
|
cumulativeMintPrice += hookStore.tierOfTokenId({
|
|
213
335
|
hook: hook, tokenId: tokenIds[i], includeResolvedUri: false
|
|
214
336
|
}).price;
|
|
@@ -219,135 +341,118 @@ library DefifaHookLib {
|
|
|
219
341
|
baseProtocolTokenAmount = mulDiv({x: baseProtocolBalance, y: cumulativeMintPrice, denominator: totalMintCost});
|
|
220
342
|
}
|
|
221
343
|
|
|
222
|
-
/// @notice
|
|
223
|
-
/// @param tokenIds The token IDs.
|
|
344
|
+
/// @notice Computes the total mint cost of all pending reserve NFTs across all tiers.
|
|
224
345
|
/// @param hookStore The 721 tiers hook store.
|
|
225
346
|
/// @param hook The hook address.
|
|
226
|
-
/// @return
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
address hook
|
|
231
|
-
)
|
|
232
|
-
public
|
|
233
|
-
view
|
|
234
|
-
returns (uint256 cumulativeMintPrice)
|
|
235
|
-
{
|
|
236
|
-
uint256 numberOfTokenIds = tokenIds.length;
|
|
237
|
-
for (uint256 i; i < numberOfTokenIds; i++) {
|
|
238
|
-
// slither-disable-next-line calls-loop
|
|
239
|
-
cumulativeMintPrice += hookStore.tierOfTokenId({
|
|
240
|
-
hook: hook, tokenId: tokenIds[i], includeResolvedUri: false
|
|
241
|
-
}).price;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
347
|
+
/// @return cost The total pending reserve mint cost.
|
|
348
|
+
function pendingReserveMintCost(IJB721TiersHookStore hookStore, address hook) public view returns (uint256 cost) {
|
|
349
|
+
// Keep a reference to the max tier ID. Tier IDs are 1-indexed, so the loop adds 1 to `i`.
|
|
350
|
+
uint256 numberOfTiers = hookStore.maxTierIdOf(hook);
|
|
244
351
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
/// @param cumulativeMintPrice The cumulative mint price of the tokens to cash out.
|
|
248
|
-
/// @param surplusValue The surplus value from the context.
|
|
249
|
-
/// @param totalAmountRedeemed The amount already redeemed.
|
|
250
|
-
/// @param cumulativeCashOutWeight The cumulative cash out weight of the tokens.
|
|
251
|
-
/// @return cashOutCount The computed cash out count.
|
|
252
|
-
function computeCashOutCount(
|
|
253
|
-
DefifaGamePhase gamePhase,
|
|
254
|
-
uint256 cumulativeMintPrice,
|
|
255
|
-
uint256 surplusValue,
|
|
256
|
-
uint256 totalAmountRedeemed,
|
|
257
|
-
uint256 cumulativeCashOutWeight
|
|
258
|
-
)
|
|
259
|
-
public
|
|
260
|
-
pure
|
|
261
|
-
returns (uint256 cashOutCount)
|
|
262
|
-
{
|
|
263
|
-
// If the game is in its minting, refund, or no-contest phase, reclaim amount is the same as it cost to mint.
|
|
264
|
-
if (
|
|
265
|
-
gamePhase == DefifaGamePhase.MINT || gamePhase == DefifaGamePhase.REFUND
|
|
266
|
-
|| gamePhase == DefifaGamePhase.NO_CONTEST
|
|
267
|
-
) {
|
|
268
|
-
cashOutCount = cumulativeMintPrice;
|
|
269
|
-
} else {
|
|
270
|
-
// If the game is in its scoring or complete phase, reclaim amount is based on the tier weights.
|
|
271
|
-
cashOutCount = mulDiv({
|
|
272
|
-
x: surplusValue + totalAmountRedeemed, y: cumulativeCashOutWeight, denominator: TOTAL_CASHOUT_WEIGHT
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
352
|
+
for (uint256 i; i < numberOfTiers;) {
|
|
353
|
+
uint256 tierId = i + 1;
|
|
276
354
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return tier.initialSupply - (tier.remainingSupply + hookStore.numberOfBurnedFor({hook: hook, tierId: tierId}));
|
|
355
|
+
// Use the hook's adjusted reserve count so refund-phase burns reduce unminted reserve liabilities.
|
|
356
|
+
uint256 pendingReserves = IDefifaHook(hook).adjustedPendingReservesFor(tierId);
|
|
357
|
+
|
|
358
|
+
// Skip empty tiers to avoid an unnecessary store read.
|
|
359
|
+
if (pendingReserves != 0) {
|
|
360
|
+
JB721Tier memory tier = hookStore.tierOf({hook: hook, id: tierId, includeResolvedUri: false});
|
|
361
|
+
|
|
362
|
+
// Pending reserves claim fee tokens at the tier price once minted, so include them in the denominator.
|
|
363
|
+
cost += pendingReserves * tier.price;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
unchecked {
|
|
367
|
+
++i;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
293
370
|
}
|
|
294
371
|
|
|
295
|
-
/// @notice
|
|
296
|
-
/// @
|
|
297
|
-
/// @param tierIdsToMint The tier IDs to mint (must be in ascending order).
|
|
372
|
+
/// @notice Validates tier cash out weights and returns the weight array to store.
|
|
373
|
+
/// @param tierWeights The tier weights to validate and set.
|
|
298
374
|
/// @param hookStore The 721 tiers hook store.
|
|
299
375
|
/// @param hook The hook address.
|
|
300
|
-
/// @return
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
function computeAttestationUnits(
|
|
304
|
-
uint16[] memory tierIdsToMint,
|
|
376
|
+
/// @return weights The 128-element array of validated weights.
|
|
377
|
+
function validateAndBuildWeights(
|
|
378
|
+
DefifaTierCashOutWeight[] memory tierWeights,
|
|
305
379
|
IJB721TiersHookStore hookStore,
|
|
306
380
|
address hook
|
|
307
381
|
)
|
|
308
382
|
public
|
|
309
383
|
view
|
|
310
|
-
returns (uint256[] memory
|
|
384
|
+
returns (uint256[128] memory weights)
|
|
311
385
|
{
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
attestationAmounts = new uint256[](numberOfTiers);
|
|
386
|
+
// Keep a reference to the max tier ID.
|
|
387
|
+
uint256 maxTierId = hookStore.maxTierIdOf(hook);
|
|
315
388
|
|
|
316
|
-
|
|
389
|
+
// Keep a reference to the cumulative amounts.
|
|
390
|
+
uint256 cumulativeCashOutWeight;
|
|
317
391
|
|
|
318
|
-
|
|
319
|
-
uint256
|
|
320
|
-
uint256 accumulated;
|
|
392
|
+
// Keep a reference to the number of tier weights.
|
|
393
|
+
uint256 numberOfTierWeights = tierWeights.length;
|
|
321
394
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
395
|
+
// Keep a reference to the tier being iterated on.
|
|
396
|
+
JB721Tier memory tier;
|
|
397
|
+
|
|
398
|
+
// Keep a reference to the last tier ID to enforce ascending order (no duplicates).
|
|
399
|
+
uint256 lastTierId;
|
|
400
|
+
|
|
401
|
+
for (uint256 i; i < numberOfTierWeights;) {
|
|
402
|
+
// Enforce strict ascending order to prevent duplicate tier IDs.
|
|
403
|
+
if (tierWeights[i].id <= lastTierId && i != 0) {
|
|
404
|
+
revert DefifaHook_BadTierOrder({previousTierId: lastTierId, tierId: tierWeights[i].id});
|
|
405
|
+
}
|
|
406
|
+
lastTierId = tierWeights[i].id;
|
|
407
|
+
|
|
408
|
+
// Get the tier.
|
|
409
|
+
tier = hookStore.tierOf({hook: hook, id: tierWeights[i].id, includeResolvedUri: false});
|
|
410
|
+
|
|
411
|
+
// Guard against uint32 truncation: if the caller passes a tier ID > type(uint32).max,
|
|
412
|
+
// the store may silently truncate and return a different tier.
|
|
413
|
+
if (tierWeights[i].id != tier.id) {
|
|
414
|
+
revert DefifaHook_InvalidTierId({
|
|
415
|
+
tierId: tierWeights[i].id, actualTierId: tier.id, maxTierId: maxTierId, category: tier.category
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Can't set a cashOut weight for tiers not in category 0.
|
|
420
|
+
if (tier.category != 0) {
|
|
421
|
+
revert DefifaHook_InvalidTierId({
|
|
422
|
+
tierId: tierWeights[i].id, actualTierId: tier.id, maxTierId: maxTierId, category: tier.category
|
|
423
|
+
});
|
|
338
424
|
}
|
|
425
|
+
|
|
426
|
+
// Attempting to set the cashOut weight for a tier that does not exist (yet) reverts.
|
|
427
|
+
if (tier.id > maxTierId) {
|
|
428
|
+
revert DefifaHook_InvalidTierId({
|
|
429
|
+
tierId: tierWeights[i].id, actualTierId: tier.id, maxTierId: maxTierId, category: tier.category
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Save the tier weight. Tiers are 1 indexed and should be stored 0 indexed.
|
|
434
|
+
weights[tier.id - 1] = tierWeights[i].cashOutWeight;
|
|
435
|
+
|
|
436
|
+
// Increment the cumulative amount.
|
|
437
|
+
cumulativeCashOutWeight += tierWeights[i].cashOutWeight;
|
|
438
|
+
|
|
339
439
|
unchecked {
|
|
340
440
|
++i;
|
|
341
441
|
}
|
|
342
442
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
443
|
+
|
|
444
|
+
// Make sure the cumulative amount is exactly the total cashOut weight.
|
|
445
|
+
if (cumulativeCashOutWeight != TOTAL_CASHOUT_WEIGHT) {
|
|
446
|
+
revert DefifaHook_InvalidCashoutWeights({
|
|
447
|
+
totalWeight: cumulativeCashOutWeight, expectedWeight: TOTAL_CASHOUT_WEIGHT
|
|
448
|
+
});
|
|
348
449
|
}
|
|
349
450
|
}
|
|
350
451
|
|
|
452
|
+
//*********************************************************************//
|
|
453
|
+
// ----------------------- public transactions ----------------------- //
|
|
454
|
+
//*********************************************************************//
|
|
455
|
+
|
|
351
456
|
/// @notice Claims the defifa and base protocol tokens for a beneficiary.
|
|
352
457
|
/// @dev Executes via delegatecall, so `address(this)` is the calling contract. Transfers are from the hook's
|
|
353
458
|
/// balance.
|
|
@@ -380,7 +485,12 @@ library DefifaHookLib {
|
|
|
380
485
|
if (defifaAmount != 0) defifaToken.safeTransfer({to: beneficiary, value: defifaAmount});
|
|
381
486
|
if (baseProtocolAmount != 0) baseProtocolToken.safeTransfer({to: beneficiary, value: baseProtocolAmount});
|
|
382
487
|
|
|
383
|
-
emit ClaimedTokens(
|
|
488
|
+
emit ClaimedTokens({
|
|
489
|
+
beneficiary: beneficiary,
|
|
490
|
+
defifaTokenAmount: defifaAmount,
|
|
491
|
+
baseProtocolTokenAmount: baseProtocolAmount,
|
|
492
|
+
caller: msg.sender
|
|
493
|
+
});
|
|
384
494
|
|
|
385
495
|
return (defifaAmount != 0 || baseProtocolAmount != 0);
|
|
386
496
|
}
|