@bananapus/core-v6 0.0.52 → 0.0.54

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.
@@ -1,288 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.28;
3
-
4
- import {IJBDirectory} from "../interfaces/IJBDirectory.sol";
5
- import {IJBTerminal} from "../interfaces/IJBTerminal.sol";
6
- import {IJBTerminalStore} from "../interfaces/IJBTerminalStore.sol";
7
- import {JBFee} from "../structs/JBFee.sol";
8
- import {JBConstants} from "./JBConstants.sol";
9
- import {JBFees} from "./JBFees.sol";
10
-
11
- /// @notice Local callback into the terminal's `executeProcessFee(...)`. Kept here because the function is an
12
- /// implementation detail of the library/terminal pair, not a shared public interface.
13
- interface IJBHeldFeesExecutor {
14
- /// @notice Sends a fee amount from a project to the fee-receiving terminal under an external CALL boundary,
15
- /// so the library can wrap the call in `try/catch` (revert in the fee route is forgiven, not propagated).
16
- /// @param projectId The project paying the fee.
17
- /// @param token The token the fee is denominated in.
18
- /// @param amount The fee amount.
19
- /// @param beneficiary The address that receives any platform tokens minted by the fee payment.
20
- /// @param feeTerminal The terminal that'll receive the fee.
21
- function executeProcessFee(
22
- uint256 projectId,
23
- address token,
24
- uint256 amount,
25
- address beneficiary,
26
- IJBTerminal feeTerminal
27
- )
28
- external;
29
- }
30
-
31
- /// @notice Held-fee bookkeeping for `JBMultiTerminal`. Extracted to reduce terminal bytecode size.
32
- /// @dev Called via DELEGATECALL — storage refs (`heldFeesOf`, `nextHeldFeeIndexOf`) point at the terminal's
33
- /// storage. `address(this)` inside library code is the terminal's address. Events are therefore emitted from
34
- /// the terminal.
35
- library JBHeldFeesLib {
36
- //*********************************************************************//
37
- // ------------------------------ events ----------------------------- //
38
- //*********************************************************************//
39
-
40
- /// @notice Emitted when a fee is sent to the fee-receiving terminal.
41
- /// @param projectId The project the fee was for.
42
- /// @param token The token the fee was paid in.
43
- /// @param amount The fee amount.
44
- /// @param wasHeld Whether the fee was previously held (true) or processed inline (false).
45
- /// @param beneficiary The address that received any platform tokens minted by the fee payment.
46
- /// @param caller The address that triggered the fee processing.
47
- event ProcessFee(
48
- uint256 indexed projectId,
49
- address indexed token,
50
- uint256 amount,
51
- bool wasHeld,
52
- address beneficiary,
53
- address caller
54
- );
55
-
56
- /// @notice Emitted when a fee payment reverts and the amount is returned to the project's balance.
57
- /// @param projectId The project the fee was for.
58
- /// @param token The token the fee was paid in.
59
- /// @param feeProjectId The ID of the fee-receiving project.
60
- /// @param amount The fee amount returned.
61
- /// @param reason The revert reason from the fee route.
62
- /// @param caller The address that triggered the fee processing.
63
- event FeeReverted(
64
- uint256 indexed projectId,
65
- address indexed token,
66
- uint256 indexed feeProjectId,
67
- uint256 amount,
68
- bytes reason,
69
- address caller
70
- );
71
-
72
- /// @notice Emitted when held fees are returned to the project's balance (e.g. on an `addToBalanceOf`).
73
- /// @param projectId The project whose held fees were returned.
74
- /// @param token The token the held fees were denominated in.
75
- /// @param amount The amount used as the basis for the return calculation.
76
- /// @param returnedFees The amount of fees actually returned.
77
- /// @param leftoverAmount Any leftover from the basis amount that did not match a held fee.
78
- /// @param caller The address that triggered the return.
79
- event ReturnHeldFees(
80
- uint256 indexed projectId,
81
- address indexed token,
82
- uint256 amount,
83
- uint256 returnedFees,
84
- uint256 leftoverAmount,
85
- address caller
86
- );
87
-
88
- //*********************************************************************//
89
- // ----------------------- internal functions ------------------------ //
90
- //*********************************************************************//
91
-
92
- /// @notice Processes up to `count` unlocked held fees for `(projectId, token)`, forwarding each to the fee
93
- /// terminal and reclaiming the storage slot once the queue is drained.
94
- /// @dev Re-reads `nextHeldFeeIndexOf[projectId][token]` from storage each iteration to be reentrancy-safe
95
- /// against any nested call that may have advanced the index. The entry is deleted and the index advanced
96
- /// BEFORE the external call so a reverting fee route cannot be replayed.
97
- /// @param heldFeesOf Storage ref to the terminal's per-(project,token) held-fee queue.
98
- /// @param nextHeldFeeIndexOf Storage ref to the next-index cursor for each (project,token) queue.
99
- /// @param directory The terminal's directory, used to locate the fee-receiving terminal.
100
- /// @param store The terminal's store, used to credit the fee back to the project on a failed route.
101
- /// @param projectId The project to process held fees for.
102
- /// @param token The token the held fees are denominated in.
103
- /// @param count The maximum number of held fees to process.
104
- function processHeldFees(
105
- mapping(uint256 => mapping(address => JBFee[])) storage heldFeesOf,
106
- mapping(uint256 => mapping(address => uint256)) storage nextHeldFeeIndexOf,
107
- IJBDirectory directory,
108
- IJBTerminalStore store,
109
- uint256 projectId,
110
- address token,
111
- uint256 count
112
- )
113
- external
114
- {
115
- // Resolve the fee-receiving terminal once outside the loop.
116
- IJBTerminal feeTerminal =
117
- directory.primaryTerminalOf({projectId: JBConstants.FEE_BENEFICIARY_PROJECT_ID, token: token});
118
-
119
- for (uint256 i; i < count;) {
120
- uint256 currentIndex = nextHeldFeeIndexOf[projectId][token];
121
-
122
- // Queue exhausted — break early so we can run the array cleanup below.
123
- if (currentIndex >= heldFeesOf[projectId][token].length) break;
124
-
125
- JBFee memory heldFee = heldFeesOf[projectId][token][currentIndex];
126
-
127
- // Fees unlock sequentially; if the head isn't ready, nothing later is either.
128
- // forge-lint: disable-next-line(block-timestamp)
129
- if (heldFee.unlockTimestamp > block.timestamp) break;
130
-
131
- // Delete + advance index BEFORE the external call (reentrancy safety: a reentrant
132
- // `processHeldFeesOf` cannot re-process the same entry).
133
- delete heldFeesOf[projectId][token][currentIndex];
134
- nextHeldFeeIndexOf[projectId][token] = currentIndex + 1;
135
-
136
- processFee({
137
- store: store,
138
- projectId: projectId,
139
- token: token,
140
- amount: JBFees.standardFeeAmountFrom({amountBeforeFee: heldFee.amount}),
141
- beneficiary: heldFee.beneficiary,
142
- feeTerminal: feeTerminal,
143
- wasHeld: true
144
- });
145
-
146
- unchecked {
147
- ++i;
148
- }
149
- }
150
-
151
- // Reclaim the array storage slot once the queue has been fully drained.
152
- if (
153
- nextHeldFeeIndexOf[projectId][token] >= heldFeesOf[projectId][token].length
154
- && heldFeesOf[projectId][token].length > 0
155
- ) {
156
- delete heldFeesOf[projectId][token];
157
- delete nextHeldFeeIndexOf[projectId][token];
158
- }
159
- }
160
-
161
- /// @notice Sends a fee to the fee-receiving terminal under a try/catch boundary.
162
- /// @dev Routed through `IJBHeldFeesExecutor(address(this)).executeProcessFee(...)` so the call is a real
163
- /// external CALL (try/catch semantics require it). Under DELEGATECALL `address(this)` is the terminal, so
164
- /// this becomes a call to the terminal's `executeProcessFee` whose `msg.sender == address(this)` check
165
- /// passes. On revert the amount is forgiven and added back to the project's balance — by design, a broken
166
- /// fee route should not permanently lock project funds.
167
- /// @param store The terminal's store, used for the forgive-on-revert credit.
168
- /// @param projectId The project paying the fee.
169
- /// @param token The token the fee is denominated in.
170
- /// @param amount The fee amount.
171
- /// @param beneficiary The address that receives any platform tokens minted by the fee payment.
172
- /// @param feeTerminal The terminal that'll receive the fee.
173
- /// @param wasHeld Whether the fee was previously held (true) or processed inline (false).
174
- function processFee(
175
- IJBTerminalStore store,
176
- uint256 projectId,
177
- address token,
178
- uint256 amount,
179
- address beneficiary,
180
- IJBTerminal feeTerminal,
181
- bool wasHeld
182
- )
183
- public
184
- {
185
- try IJBHeldFeesExecutor(address(this))
186
- .executeProcessFee({
187
- projectId: projectId, token: token, amount: amount, beneficiary: beneficiary, feeTerminal: feeTerminal
188
- }) {
189
- emit ProcessFee({
190
- projectId: projectId,
191
- token: token,
192
- amount: amount,
193
- wasHeld: wasHeld,
194
- beneficiary: beneficiary,
195
- caller: msg.sender
196
- });
197
- } catch (bytes memory reason) {
198
- // Forgive the fee, credit it back to the project, and surface the failure for off-chain observability.
199
- emit FeeReverted({
200
- projectId: projectId,
201
- token: token,
202
- feeProjectId: JBConstants.FEE_BENEFICIARY_PROJECT_ID,
203
- amount: amount,
204
- reason: reason,
205
- caller: msg.sender
206
- });
207
-
208
- store.recordAddedBalanceFor({projectId: projectId, token: token, amount: amount});
209
- }
210
- }
211
-
212
- /// @notice Returns held fees up to `amount` in FIFO order from the project's held-fee queue.
213
- /// @dev Walks the queue from `nextHeldFeeIndexOf` forward. Whole entries are consumed when `leftoverAmount`
214
- /// covers their post-fee amount; the final entry can be partially consumed by shrinking its `.amount` in
215
- /// place. The fee implied by the partial-consume branch uses `feeAmountResultingIn` (inverse of
216
- /// `feeAmountFrom`) so the credited fee aligns with the amount that was actually returned. Dust amounts
217
- /// below the floor produce `feeAmount == 0`.
218
- /// @param heldFeesOf Storage ref to the terminal's per-(project,token) held-fee queue.
219
- /// @param nextHeldFeeIndexOf Storage ref to the next-index cursor for each (project,token) queue.
220
- /// @param projectId The project to return held fees for.
221
- /// @param token The token the held fees are denominated in.
222
- /// @param amount The reference amount to base the return on (typically the inbound addToBalance amount).
223
- /// @return returnedFees The total fee amount returned to the project.
224
- function returnHeldFees(
225
- mapping(uint256 => mapping(address => JBFee[])) storage heldFeesOf,
226
- mapping(uint256 => mapping(address => uint256)) storage nextHeldFeeIndexOf,
227
- uint256 projectId,
228
- address token,
229
- uint256 amount
230
- )
231
- external
232
- returns (uint256 returnedFees)
233
- {
234
- uint256 startIndex = nextHeldFeeIndexOf[projectId][token];
235
- uint256 numberOfHeldFees = heldFeesOf[projectId][token].length;
236
-
237
- // Empty queue — nothing to return.
238
- if (startIndex >= numberOfHeldFees) return 0;
239
-
240
- uint256 leftoverAmount = amount;
241
- uint256 count = numberOfHeldFees - startIndex;
242
- uint256 newStartIndex = startIndex;
243
-
244
- for (uint256 i; i < count;) {
245
- JBFee memory heldFee = heldFeesOf[projectId][token][startIndex + i];
246
-
247
- if (leftoverAmount == 0) {
248
- break;
249
- } else {
250
- // Recompute the fee charged on the stored gross amount so partial returns stay aligned.
251
- uint256 feeAmount = JBFees.standardFeeAmountFrom({amountBeforeFee: heldFee.amount});
252
- uint256 amountPaidOut = heldFee.amount - feeAmount;
253
-
254
- if (leftoverAmount >= amountPaidOut) {
255
- // Whole entry consumed: credit its full fee, advance the cursor past it.
256
- unchecked {
257
- leftoverAmount -= amountPaidOut;
258
- returnedFees += feeAmount;
259
- }
260
- newStartIndex = startIndex + i + 1;
261
- } else {
262
- // Partial entry: shrink the stored entry by what we consumed (incl. its own fee).
263
- feeAmount = JBFees.standardFeeAmountResultingIn({amountAfterFee: leftoverAmount});
264
- unchecked {
265
- heldFeesOf[projectId][token][startIndex + i].amount -= (leftoverAmount + feeAmount);
266
- returnedFees += feeAmount;
267
- }
268
- leftoverAmount = 0;
269
- }
270
- }
271
-
272
- unchecked {
273
- ++i;
274
- }
275
- }
276
-
277
- if (startIndex != newStartIndex) nextHeldFeeIndexOf[projectId][token] = newStartIndex;
278
-
279
- emit ReturnHeldFees({
280
- projectId: projectId,
281
- token: token,
282
- amount: amount,
283
- returnedFees: returnedFees,
284
- leftoverAmount: leftoverAmount,
285
- caller: msg.sender
286
- });
287
- }
288
- }