@bananapus/core-v6 0.0.9 → 0.0.10
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/foundry.toml +0 -1
- package/package.json +2 -2
- package/src/JBChainlinkV3PriceFeed.sol +1 -5
- package/src/JBChainlinkV3SequencerPriceFeed.sol +1 -1
- package/src/JBController.sol +277 -277
- package/src/JBDeadline.sol +1 -1
- package/src/JBDirectory.sol +93 -93
- package/src/JBERC20.sol +43 -39
- package/src/JBFeelessAddresses.sol +12 -12
- package/src/JBFundAccessLimits.sol +82 -82
- package/src/JBMultiTerminal.sol +313 -313
- package/src/JBPermissions.sol +104 -100
- package/src/JBPrices.sol +68 -68
- package/src/JBProjects.sol +31 -31
- package/src/JBRulesets.sol +422 -422
- package/src/JBSplits.sol +116 -116
- package/src/JBTerminalStore.sol +651 -651
- package/src/JBTokens.sol +41 -41
- package/src/interfaces/IJBCashOutTerminal.sol +25 -7
- package/src/interfaces/IJBController.sol +78 -3
- package/src/interfaces/IJBDirectory.sol +25 -0
- package/src/interfaces/IJBFeeTerminal.sol +31 -0
- package/src/interfaces/IJBFeelessAddresses.sol +4 -0
- package/src/interfaces/IJBFundAccessLimits.sol +5 -0
- package/src/interfaces/IJBMigratable.sol +12 -8
- package/src/interfaces/IJBPayoutTerminal.sol +56 -9
- package/src/interfaces/IJBPermissions.sol +14 -7
- package/src/interfaces/IJBPermitTerminal.sol +4 -0
- package/src/interfaces/IJBPrices.sol +6 -0
- package/src/interfaces/IJBProjects.sol +8 -0
- package/src/interfaces/IJBRulesetApprovalHook.sol +1 -1
- package/src/interfaces/IJBRulesetDataHook.sol +23 -23
- package/src/interfaces/IJBRulesets.sol +54 -33
- package/src/interfaces/IJBSplits.sol +6 -0
- package/src/interfaces/IJBTerminal.sol +36 -0
- package/src/interfaces/IJBTerminalStore.sol +63 -63
- package/src/interfaces/IJBToken.sol +5 -5
- package/src/interfaces/IJBTokens.sol +50 -8
- package/test/TestDurationUnderflow.sol +3 -2
package/src/JBTerminalStore.sol
CHANGED
|
@@ -132,777 +132,777 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
//*********************************************************************//
|
|
135
|
-
//
|
|
135
|
+
// ---------------------- external transactions ---------------------- //
|
|
136
136
|
//*********************************************************************//
|
|
137
137
|
|
|
138
|
-
/// @notice
|
|
139
|
-
///
|
|
140
|
-
/// @param
|
|
141
|
-
/// @param
|
|
138
|
+
/// @notice Records funds being added to a project's balance.
|
|
139
|
+
/// @param projectId The ID of the project which funds are being added to the balance of.
|
|
140
|
+
/// @param token The token being added to the balance.
|
|
141
|
+
/// @param amount The amount of terminal tokens added, as a fixed point number with the same amount of decimals as
|
|
142
|
+
/// its relative terminal.
|
|
143
|
+
function recordAddedBalanceFor(uint256 projectId, address token, uint256 amount) external override {
|
|
144
|
+
// Increment the balance.
|
|
145
|
+
balanceOf[msg.sender][projectId][token] = balanceOf[msg.sender][projectId][token] + amount;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// @notice Records a cash out from a project.
|
|
149
|
+
/// @dev Cashs out the project's tokens according to values provided by the ruleset's data hook. If the ruleset has
|
|
150
|
+
/// no
|
|
151
|
+
/// data hook, cashs out tokens along a cash out bonding curve that is a function of the number of tokens being
|
|
152
|
+
/// burned.
|
|
153
|
+
/// @param holder The account that is cashing out tokens.
|
|
154
|
+
/// @param projectId The ID of the project being cashing out from.
|
|
155
|
+
/// @param cashOutCount The number of project tokens to cash out, as a fixed point number with 18 decimals.
|
|
156
|
+
/// @param accountingContext The accounting context of the token being reclaimed by the cash out.
|
|
157
|
+
/// @param balanceAccountingContexts The accounting contexts of the tokens whose balances should contribute to the
|
|
158
|
+
/// surplus being reclaimed from.
|
|
159
|
+
/// @param metadata Bytes to send to the data hook, if the project's current ruleset specifies one.
|
|
160
|
+
/// @return ruleset The ruleset during the cash out was made during, as a `JBRuleset` struct. This ruleset will
|
|
161
|
+
/// have a cash out tax rate provided by the cash out hook if applicable.
|
|
162
|
+
/// @return reclaimAmount The amount of tokens reclaimed from the terminal, as a fixed point number with 18
|
|
142
163
|
/// decimals.
|
|
143
|
-
/// @
|
|
144
|
-
/// @
|
|
145
|
-
///
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
/// @return cashOutTaxRate The cash out tax rate influencing the reclaim amount.
|
|
165
|
+
/// @return hookSpecifications A list of cash out hooks, including data and amounts to send to them. The terminal
|
|
166
|
+
/// should fulfill these specifications.
|
|
167
|
+
function recordCashOutFor(
|
|
168
|
+
address holder,
|
|
148
169
|
uint256 projectId,
|
|
149
170
|
uint256 cashOutCount,
|
|
150
|
-
|
|
151
|
-
|
|
171
|
+
JBAccountingContext calldata accountingContext,
|
|
172
|
+
JBAccountingContext[] calldata balanceAccountingContexts,
|
|
173
|
+
bytes memory metadata
|
|
152
174
|
)
|
|
153
175
|
external
|
|
154
|
-
view
|
|
155
176
|
override
|
|
156
|
-
returns (
|
|
177
|
+
returns (
|
|
178
|
+
JBRuleset memory ruleset,
|
|
179
|
+
uint256 reclaimAmount,
|
|
180
|
+
uint256 cashOutTaxRate,
|
|
181
|
+
JBCashOutHookSpecification[] memory hookSpecifications
|
|
182
|
+
)
|
|
157
183
|
{
|
|
158
|
-
//
|
|
159
|
-
|
|
184
|
+
// Get a reference to the project's current ruleset.
|
|
185
|
+
ruleset = RULESETS.currentOf(projectId);
|
|
160
186
|
|
|
161
|
-
//
|
|
162
|
-
if
|
|
187
|
+
// Get the current surplus amount.
|
|
188
|
+
// Use the local surplus if the ruleset specifies that it should be used. Otherwise, use the project's total
|
|
189
|
+
// surplus across all of its terminals.
|
|
190
|
+
uint256 currentSurplus = ruleset.useTotalSurplusForCashOuts()
|
|
191
|
+
? JBSurplus.currentSurplusOf({
|
|
192
|
+
projectId: projectId,
|
|
193
|
+
terminals: DIRECTORY.terminalsOf(projectId),
|
|
194
|
+
accountingContexts: new JBAccountingContext[](0),
|
|
195
|
+
decimals: accountingContext.decimals,
|
|
196
|
+
currency: accountingContext.currency
|
|
197
|
+
})
|
|
198
|
+
: _surplusFrom({
|
|
199
|
+
terminal: msg.sender,
|
|
200
|
+
projectId: projectId,
|
|
201
|
+
accountingContexts: balanceAccountingContexts,
|
|
202
|
+
ruleset: ruleset,
|
|
203
|
+
targetDecimals: accountingContext.decimals,
|
|
204
|
+
targetCurrency: accountingContext.currency
|
|
205
|
+
});
|
|
163
206
|
|
|
164
|
-
// Get
|
|
165
|
-
|
|
207
|
+
// Get the total number of outstanding project tokens.
|
|
208
|
+
uint256 totalSupply =
|
|
209
|
+
IJBController(address(DIRECTORY.controllerOf(projectId))).totalTokenSupplyWithReservedTokensOf(projectId);
|
|
166
210
|
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
211
|
+
// Can't cash out more tokens than are in the supply.
|
|
212
|
+
if (cashOutCount > totalSupply) revert JBTerminalStore_InsufficientTokens(cashOutCount, totalSupply);
|
|
213
|
+
|
|
214
|
+
// SECURITY NOTE: The data hook has absolute control over cash-out economics.
|
|
215
|
+
// It can set totalSupply, cashOutCount, and cashOutTaxRate to arbitrary values,
|
|
216
|
+
// completely overriding the terminal's bonding curve math. For example, setting
|
|
217
|
+
// totalSupply = surplus makes reclaimAmount = cashOutCount, bypassing the curve.
|
|
218
|
+
// Project owners MUST audit their data hooks with the same rigor as the terminal.
|
|
219
|
+
|
|
220
|
+
// If the ruleset has a data hook which is enabled for cash outs, use it to derive a claim amount and memo.
|
|
221
|
+
if (ruleset.useDataHookForCashOut() && ruleset.dataHook() != address(0)) {
|
|
222
|
+
// Create the cash out context that'll be sent to the data hook.
|
|
223
|
+
JBBeforeCashOutRecordedContext memory context = JBBeforeCashOutRecordedContext({
|
|
224
|
+
terminal: msg.sender,
|
|
225
|
+
holder: holder,
|
|
226
|
+
projectId: projectId,
|
|
227
|
+
rulesetId: ruleset.id,
|
|
228
|
+
cashOutCount: cashOutCount,
|
|
229
|
+
totalSupply: totalSupply,
|
|
230
|
+
surplus: JBTokenAmount({
|
|
231
|
+
token: accountingContext.token,
|
|
232
|
+
value: currentSurplus,
|
|
233
|
+
decimals: accountingContext.decimals,
|
|
234
|
+
currency: accountingContext.currency
|
|
235
|
+
}),
|
|
236
|
+
useTotalSurplus: ruleset.useTotalSurplusForCashOuts(),
|
|
237
|
+
cashOutTaxRate: ruleset.cashOutTaxRate(),
|
|
238
|
+
metadata: metadata
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
(cashOutTaxRate, cashOutCount, totalSupply, hookSpecifications) =
|
|
242
|
+
IJBRulesetDataHook(ruleset.dataHook()).beforeCashOutRecordedWith(context);
|
|
243
|
+
} else {
|
|
244
|
+
cashOutTaxRate = ruleset.cashOutTaxRate();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (currentSurplus != 0) {
|
|
248
|
+
// Calculate reclaim amount using the current surplus amount.
|
|
249
|
+
reclaimAmount = JBCashOuts.cashOutFrom({
|
|
250
|
+
surplus: currentSurplus,
|
|
251
|
+
cashOutCount: cashOutCount,
|
|
252
|
+
totalSupply: totalSupply,
|
|
253
|
+
cashOutTaxRate: cashOutTaxRate
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Keep a reference to the amount that should be added to the project's balance.
|
|
258
|
+
uint256 balanceDiff = reclaimAmount;
|
|
259
|
+
|
|
260
|
+
// Ensure that the specifications have valid amounts.
|
|
261
|
+
if (hookSpecifications.length != 0) {
|
|
262
|
+
// Keep a reference to the number of cash out hooks specified.
|
|
263
|
+
uint256 numberOfSpecifications = hookSpecifications.length;
|
|
264
|
+
|
|
265
|
+
// Loop through each specification.
|
|
266
|
+
for (uint256 i; i < numberOfSpecifications; i++) {
|
|
267
|
+
// Get a reference to the specification's amount.
|
|
268
|
+
uint256 specificationAmount = hookSpecifications[i].amount;
|
|
269
|
+
|
|
270
|
+
// Ensure the amount is non-zero.
|
|
271
|
+
if (specificationAmount != 0) {
|
|
272
|
+
// Increment the total amount being subtracted from the balance.
|
|
273
|
+
balanceDiff += specificationAmount;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// The amount being reclaimed must be within the project's balance.
|
|
279
|
+
if (balanceDiff > balanceOf[msg.sender][projectId][accountingContext.token]) {
|
|
280
|
+
revert JBTerminalStore_InadequateTerminalStoreBalance(
|
|
281
|
+
balanceDiff, balanceOf[msg.sender][projectId][accountingContext.token]
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Remove the reclaimed funds from the project's balance.
|
|
286
|
+
if (balanceDiff != 0) {
|
|
287
|
+
unchecked {
|
|
288
|
+
balanceOf[msg.sender][projectId][accountingContext.token] =
|
|
289
|
+
balanceOf[msg.sender][projectId][accountingContext.token] - balanceDiff;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
174
292
|
}
|
|
175
293
|
|
|
176
|
-
/// @notice
|
|
177
|
-
///
|
|
178
|
-
///
|
|
179
|
-
/// @
|
|
180
|
-
///
|
|
181
|
-
///
|
|
182
|
-
/// @param
|
|
183
|
-
/// @param
|
|
184
|
-
///
|
|
185
|
-
/// @param
|
|
186
|
-
///
|
|
187
|
-
/// @
|
|
188
|
-
/// @
|
|
189
|
-
///
|
|
190
|
-
|
|
191
|
-
|
|
294
|
+
/// @notice Records a payment to a project.
|
|
295
|
+
/// @dev Mints the project's tokens according to values provided by the ruleset's data hook. If the ruleset has no
|
|
296
|
+
/// data hook, mints tokens in proportion with the amount paid.
|
|
297
|
+
/// @param payer The address that made the payment to the terminal.
|
|
298
|
+
/// @param amount The amount of tokens being paid. Includes the token being paid, their value, the number of
|
|
299
|
+
/// decimals included, and the currency of the amount.
|
|
300
|
+
/// @param projectId The ID of the project being paid.
|
|
301
|
+
/// @param beneficiary The address that should be the beneficiary of anything the payment yields (including project
|
|
302
|
+
/// tokens minted by the payment).
|
|
303
|
+
/// @param metadata Bytes to send to the data hook, if the project's current ruleset specifies one.
|
|
304
|
+
/// @return ruleset The ruleset the payment was made during, as a `JBRuleset` struct.
|
|
305
|
+
/// @return tokenCount The number of project tokens that were minted, as a fixed point number with 18 decimals.
|
|
306
|
+
/// @return hookSpecifications A list of pay hooks, including data and amounts to send to them. The terminal should
|
|
307
|
+
/// fulfill these specifications.
|
|
308
|
+
function recordPaymentFrom(
|
|
309
|
+
address payer,
|
|
310
|
+
JBTokenAmount calldata amount,
|
|
192
311
|
uint256 projectId,
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
JBAccountingContext[] calldata accountingContexts,
|
|
196
|
-
uint256 decimals,
|
|
197
|
-
uint256 currency
|
|
312
|
+
address beneficiary,
|
|
313
|
+
bytes calldata metadata
|
|
198
314
|
)
|
|
199
315
|
external
|
|
200
|
-
view
|
|
201
316
|
override
|
|
202
|
-
returns (uint256)
|
|
317
|
+
returns (JBRuleset memory ruleset, uint256 tokenCount, JBPayHookSpecification[] memory hookSpecifications)
|
|
203
318
|
{
|
|
204
319
|
// Get a reference to the project's current ruleset.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// Get the current surplus amount.
|
|
208
|
-
// If a terminal wasn't provided, use the total surplus across all terminals. Otherwise,
|
|
209
|
-
// get the `terminal`'s surplus.
|
|
210
|
-
uint256 currentSurplus = JBSurplus.currentSurplusOf({
|
|
211
|
-
projectId: projectId,
|
|
212
|
-
terminals: terminals.length != 0 ? terminals : DIRECTORY.terminalsOf(projectId),
|
|
213
|
-
accountingContexts: accountingContexts,
|
|
214
|
-
decimals: decimals,
|
|
215
|
-
currency: currency
|
|
216
|
-
});
|
|
320
|
+
ruleset = RULESETS.currentOf(projectId);
|
|
217
321
|
|
|
218
|
-
//
|
|
219
|
-
if (
|
|
322
|
+
// The project must have a ruleset.
|
|
323
|
+
if (ruleset.cycleNumber == 0) revert JBTerminalStore_RulesetNotFound(projectId);
|
|
220
324
|
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
IJBController(address(DIRECTORY.controllerOf(projectId))).totalTokenSupplyWithReservedTokensOf(projectId);
|
|
325
|
+
// The ruleset must not have payments paused.
|
|
326
|
+
if (ruleset.pausePay()) revert JBTerminalStore_RulesetPaymentPaused();
|
|
224
327
|
|
|
225
|
-
//
|
|
226
|
-
|
|
328
|
+
// The weight according to which new tokens are to be minted, as a fixed point number with 18 decimals.
|
|
329
|
+
uint256 weight;
|
|
227
330
|
|
|
228
|
-
//
|
|
229
|
-
return
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
totalSupply: totalSupply,
|
|
233
|
-
cashOutTaxRate: ruleset.cashOutTaxRate()
|
|
234
|
-
});
|
|
235
|
-
}
|
|
331
|
+
// SECURITY NOTE: The data hook has absolute control over payment token minting.
|
|
332
|
+
// It can return an arbitrary weight (overriding the ruleset's weight) and hook specifications
|
|
333
|
+
// that divert payment funds to external hooks before they reach the project's balance.
|
|
334
|
+
// Project owners MUST audit their data hooks with the same rigor as the terminal.
|
|
236
335
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
JBAccountingContext[] calldata accountingContexts,
|
|
252
|
-
uint256 decimals,
|
|
253
|
-
uint256 currency
|
|
254
|
-
)
|
|
255
|
-
external
|
|
256
|
-
view
|
|
257
|
-
override
|
|
258
|
-
returns (uint256)
|
|
259
|
-
{
|
|
260
|
-
// Return the surplus during the project's current ruleset.
|
|
261
|
-
return _surplusFrom({
|
|
262
|
-
terminal: terminal,
|
|
263
|
-
projectId: projectId,
|
|
264
|
-
accountingContexts: accountingContexts,
|
|
265
|
-
ruleset: RULESETS.currentOf(projectId),
|
|
266
|
-
targetDecimals: decimals,
|
|
267
|
-
targetCurrency: currency
|
|
268
|
-
});
|
|
269
|
-
}
|
|
336
|
+
// If the ruleset has a data hook enabled for payments, use it to derive a weight and memo.
|
|
337
|
+
if (ruleset.useDataHookForPay() && ruleset.dataHook() != address(0)) {
|
|
338
|
+
// Create the pay context that'll be sent to the data hook.
|
|
339
|
+
JBBeforePayRecordedContext memory context = JBBeforePayRecordedContext({
|
|
340
|
+
terminal: msg.sender,
|
|
341
|
+
payer: payer,
|
|
342
|
+
amount: amount,
|
|
343
|
+
projectId: projectId,
|
|
344
|
+
rulesetId: ruleset.id,
|
|
345
|
+
beneficiary: beneficiary,
|
|
346
|
+
weight: ruleset.weight,
|
|
347
|
+
reservedPercent: ruleset.reservedPercent(),
|
|
348
|
+
metadata: metadata
|
|
349
|
+
});
|
|
270
350
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
uint256 projectId,
|
|
278
|
-
uint256 decimals,
|
|
279
|
-
uint256 currency
|
|
280
|
-
)
|
|
281
|
-
external
|
|
282
|
-
view
|
|
283
|
-
override
|
|
284
|
-
returns (uint256)
|
|
285
|
-
{
|
|
286
|
-
return JBSurplus.currentSurplusOf({
|
|
287
|
-
projectId: projectId,
|
|
288
|
-
terminals: DIRECTORY.terminalsOf(projectId),
|
|
289
|
-
accountingContexts: new JBAccountingContext[](0),
|
|
290
|
-
decimals: decimals,
|
|
291
|
-
currency: currency
|
|
292
|
-
});
|
|
293
|
-
}
|
|
351
|
+
(weight, hookSpecifications) = IJBRulesetDataHook(ruleset.dataHook()).beforePayRecordedWith(context);
|
|
352
|
+
}
|
|
353
|
+
// Otherwise use the ruleset's weight
|
|
354
|
+
else {
|
|
355
|
+
weight = ruleset.weight;
|
|
356
|
+
}
|
|
294
357
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
//*********************************************************************//
|
|
358
|
+
// Keep a reference to the amount that should be added to the project's balance.
|
|
359
|
+
uint256 balanceDiff = amount.value;
|
|
298
360
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
/// @param terminal The terminal the surplus is being calculated for.
|
|
304
|
-
/// @param projectId The ID of the project to get the surplus for.
|
|
305
|
-
/// @param accountingContexts The accounting contexts of tokens whose balances should contribute to the surplus
|
|
306
|
-
/// being calculated.
|
|
307
|
-
/// @param ruleset The ID of the ruleset to base the surplus on.
|
|
308
|
-
/// @param targetDecimals The number of decimals to include in the resulting fixed point number.
|
|
309
|
-
/// @param targetCurrency The currency that the reported surplus is expected to be in terms of.
|
|
310
|
-
/// @return surplus The surplus of funds in terms of `targetCurrency`, as a fixed point number with
|
|
311
|
-
/// `targetDecimals` decimals.
|
|
312
|
-
function _surplusFrom(
|
|
313
|
-
address terminal,
|
|
314
|
-
uint256 projectId,
|
|
315
|
-
JBAccountingContext[] memory accountingContexts,
|
|
316
|
-
JBRuleset memory ruleset,
|
|
317
|
-
uint256 targetDecimals,
|
|
318
|
-
uint256 targetCurrency
|
|
319
|
-
)
|
|
320
|
-
internal
|
|
321
|
-
view
|
|
322
|
-
returns (uint256 surplus)
|
|
323
|
-
{
|
|
324
|
-
// Keep a reference to the number of tokens being iterated on.
|
|
325
|
-
uint256 numberOfTokenAccountingContexts = accountingContexts.length;
|
|
361
|
+
// Scoped section preventing stack too deep.
|
|
362
|
+
{
|
|
363
|
+
// Keep a reference to the number of hook specifications.
|
|
364
|
+
uint256 numberOfSpecifications = hookSpecifications.length;
|
|
326
365
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
366
|
+
// Ensure that the specifications have valid amounts.
|
|
367
|
+
for (uint256 i; i < numberOfSpecifications; i++) {
|
|
368
|
+
// Get a reference to the specification's amount.
|
|
369
|
+
uint256 specifiedAmount = hookSpecifications[i].amount;
|
|
370
|
+
|
|
371
|
+
// Ensure the amount is non-zero.
|
|
372
|
+
if (specifiedAmount != 0) {
|
|
373
|
+
// Can't send more to hook than was paid.
|
|
374
|
+
if (specifiedAmount > balanceDiff) {
|
|
375
|
+
revert JBTerminalStore_InvalidAmountToForwardHook(specifiedAmount, balanceDiff);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Decrement the total amount being added to the local balance.
|
|
379
|
+
balanceDiff -= specifiedAmount;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// If there's no amount being recorded, there's nothing left to do.
|
|
385
|
+
if (amount.value == 0) return (ruleset, 0, hookSpecifications);
|
|
386
|
+
|
|
387
|
+
// Add the correct balance difference to the token balance of the project.
|
|
388
|
+
if (balanceDiff != 0) {
|
|
389
|
+
balanceOf[msg.sender][projectId][amount.token] =
|
|
390
|
+
balanceOf[msg.sender][projectId][amount.token] + balanceDiff;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// If there's no weight, the token count must be 0, so there's nothing left to do.
|
|
394
|
+
if (weight == 0) return (ruleset, 0, hookSpecifications);
|
|
395
|
+
|
|
396
|
+
// If the terminal should base its weight on a currency other than the terminal's currency, determine the
|
|
397
|
+
// factor. The weight is always a fixed point mumber with 18 decimals. To ensure this, the ratio should use the
|
|
398
|
+
// same
|
|
399
|
+
// number of decimals as the `amount`.
|
|
400
|
+
uint256 weightRatio = amount.currency == ruleset.baseCurrency()
|
|
401
|
+
? 10 ** amount.decimals
|
|
402
|
+
: PRICES.pricePerUnitOf({
|
|
331
403
|
projectId: projectId,
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
targetCurrency: targetCurrency
|
|
404
|
+
pricingCurrency: amount.currency,
|
|
405
|
+
unitCurrency: ruleset.baseCurrency(),
|
|
406
|
+
decimals: amount.decimals
|
|
336
407
|
});
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
408
|
+
|
|
409
|
+
// Find the number of tokens to mint, as a fixed point number with as many decimals as `weight` has.
|
|
410
|
+
tokenCount = mulDiv(amount.value, weight, weightRatio);
|
|
340
411
|
}
|
|
341
412
|
|
|
342
|
-
/// @notice
|
|
343
|
-
///
|
|
344
|
-
///
|
|
345
|
-
///
|
|
346
|
-
/// @param
|
|
347
|
-
/// @param
|
|
348
|
-
/// @param
|
|
349
|
-
///
|
|
350
|
-
/// @
|
|
351
|
-
/// @
|
|
352
|
-
///
|
|
353
|
-
|
|
354
|
-
/// `targetDecimals` decimals.
|
|
355
|
-
function _tokenSurplusFrom(
|
|
356
|
-
address terminal,
|
|
413
|
+
/// @notice Records a payout from a project.
|
|
414
|
+
/// @dev The balance is decremented and the used payout limit is incremented before the payout limit validation.
|
|
415
|
+
/// This is safe because the entire transaction reverts atomically if the validation fails, but callers should
|
|
416
|
+
/// be aware of this ordering.
|
|
417
|
+
/// @param projectId The ID of the project that is paying out funds.
|
|
418
|
+
/// @param accountingContext The context of the token being paid out.
|
|
419
|
+
/// @param amount The amount to pay out (use from the payout limit), as a fixed point number.
|
|
420
|
+
/// @param currency The currency of the `amount`. This must match the project's current ruleset's currency.
|
|
421
|
+
/// @return ruleset The ruleset the payout was made during, as a `JBRuleset` struct.
|
|
422
|
+
/// @return amountPaidOut The amount of terminal tokens paid out, as a fixed point number with the same amount of
|
|
423
|
+
/// decimals as its relative terminal.
|
|
424
|
+
function recordPayoutFor(
|
|
357
425
|
uint256 projectId,
|
|
358
|
-
JBAccountingContext
|
|
359
|
-
|
|
360
|
-
uint256
|
|
361
|
-
uint256 targetCurrency
|
|
426
|
+
JBAccountingContext calldata accountingContext,
|
|
427
|
+
uint256 amount,
|
|
428
|
+
uint256 currency
|
|
362
429
|
)
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
returns (uint256
|
|
430
|
+
external
|
|
431
|
+
override
|
|
432
|
+
returns (JBRuleset memory ruleset, uint256 amountPaidOut)
|
|
366
433
|
{
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
// If needed, adjust the decimals of the fixed point number to have the correct decimals.
|
|
371
|
-
surplus = accountingContext.decimals == targetDecimals
|
|
372
|
-
? surplus
|
|
373
|
-
: JBFixedPointNumber.adjustDecimals({
|
|
374
|
-
value: surplus, decimals: accountingContext.decimals, targetDecimals: targetDecimals
|
|
375
|
-
});
|
|
434
|
+
// Get a reference to the project's current ruleset.
|
|
435
|
+
ruleset = RULESETS.currentOf(projectId);
|
|
376
436
|
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
?
|
|
437
|
+
// Convert the amount to the balance's currency.
|
|
438
|
+
amountPaidOut = (currency == accountingContext.currency)
|
|
439
|
+
? amount
|
|
380
440
|
: mulDiv(
|
|
381
|
-
|
|
382
|
-
10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the
|
|
383
|
-
//
|
|
441
|
+
amount,
|
|
442
|
+
10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the `_amount`'s
|
|
443
|
+
// fidelity as possible when converting.
|
|
384
444
|
PRICES.pricePerUnitOf({
|
|
385
445
|
projectId: projectId,
|
|
386
|
-
pricingCurrency:
|
|
387
|
-
unitCurrency:
|
|
446
|
+
pricingCurrency: currency,
|
|
447
|
+
unitCurrency: accountingContext.currency,
|
|
388
448
|
decimals: _MAX_FIXED_POINT_FIDELITY
|
|
389
449
|
})
|
|
390
450
|
);
|
|
391
451
|
|
|
392
|
-
//
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
452
|
+
// The amount being paid out must be available.
|
|
453
|
+
if (amountPaidOut > balanceOf[msg.sender][projectId][accountingContext.token]) {
|
|
454
|
+
revert JBTerminalStore_InadequateTerminalStoreBalance(
|
|
455
|
+
amountPaidOut, balanceOf[msg.sender][projectId][accountingContext.token]
|
|
456
|
+
);
|
|
457
|
+
}
|
|
398
458
|
|
|
399
|
-
//
|
|
400
|
-
|
|
459
|
+
// Removed the paid out funds from the project's token balance.
|
|
460
|
+
unchecked {
|
|
461
|
+
balanceOf[msg.sender][projectId][accountingContext.token] =
|
|
462
|
+
balanceOf[msg.sender][projectId][accountingContext.token] - amountPaidOut;
|
|
463
|
+
}
|
|
401
464
|
|
|
402
|
-
//
|
|
403
|
-
|
|
404
|
-
|
|
465
|
+
// The new total amount which has been paid out during this ruleset.
|
|
466
|
+
uint256 newUsedPayoutLimitOf =
|
|
467
|
+
usedPayoutLimitOf[msg.sender][projectId][accountingContext.token][ruleset.cycleNumber][currency] + amount;
|
|
405
468
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
- usedPayoutLimitOf[
|
|
410
|
-
terminal
|
|
411
|
-
][projectId][accountingContext.token][ruleset.cycleNumber][payoutLimit.currency];
|
|
412
|
-
if (remaining > type(uint224).max) revert JBTerminalStore_Uint224Overflow(remaining);
|
|
413
|
-
payoutLimit.amount = uint224(remaining);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Adjust the decimals of the fixed point number if needed to have the correct decimals.
|
|
417
|
-
if (accountingContext.decimals != targetDecimals) {
|
|
418
|
-
uint256 adjusted = JBFixedPointNumber.adjustDecimals({
|
|
419
|
-
value: payoutLimit.amount, decimals: accountingContext.decimals, targetDecimals: targetDecimals
|
|
420
|
-
});
|
|
421
|
-
if (adjusted > type(uint224).max) revert JBTerminalStore_Uint224Overflow(adjusted);
|
|
422
|
-
payoutLimit.amount = uint224(adjusted);
|
|
423
|
-
}
|
|
469
|
+
// Store the new amount.
|
|
470
|
+
usedPayoutLimitOf[msg.sender][projectId][accountingContext.token][ruleset.cycleNumber][currency] =
|
|
471
|
+
newUsedPayoutLimitOf;
|
|
424
472
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
unitCurrency: targetCurrency,
|
|
435
|
-
decimals: _MAX_FIXED_POINT_FIDELITY
|
|
436
|
-
})
|
|
437
|
-
);
|
|
438
|
-
if (converted > type(uint224).max) revert JBTerminalStore_Uint224Overflow(converted);
|
|
439
|
-
payoutLimit.amount = uint224(converted);
|
|
440
|
-
}
|
|
473
|
+
// Amount must be within what is still available to pay out.
|
|
474
|
+
uint256 payoutLimit = IJBController(address(DIRECTORY.controllerOf(projectId))).FUND_ACCESS_LIMITS()
|
|
475
|
+
.payoutLimitOf({
|
|
476
|
+
projectId: projectId,
|
|
477
|
+
rulesetId: ruleset.id,
|
|
478
|
+
terminal: msg.sender,
|
|
479
|
+
token: accountingContext.token,
|
|
480
|
+
currency: currency
|
|
481
|
+
});
|
|
441
482
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
} else {
|
|
446
|
-
return 0;
|
|
447
|
-
}
|
|
483
|
+
// Make sure the new used amount is within the payout limit.
|
|
484
|
+
if (newUsedPayoutLimitOf > payoutLimit || payoutLimit == 0) {
|
|
485
|
+
revert JBTerminalStore_InadequateControllerPayoutLimit(newUsedPayoutLimitOf, payoutLimit);
|
|
448
486
|
}
|
|
449
487
|
}
|
|
450
488
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
489
|
+
/// @notice Records the migration of funds from this store.
|
|
490
|
+
/// @param projectId The ID of the project being migrated.
|
|
491
|
+
/// @param token The token being migrated.
|
|
492
|
+
/// @return balance The project's current balance (which is being migrated), as a fixed point number with the same
|
|
493
|
+
/// amount of decimals as its relative terminal.
|
|
494
|
+
function recordTerminalMigration(uint256 projectId, address token) external override returns (uint256 balance) {
|
|
495
|
+
// Get a reference to the project's current ruleset.
|
|
496
|
+
JBRuleset memory ruleset = RULESETS.currentOf(projectId);
|
|
454
497
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
498
|
+
// Terminal migration must be allowed.
|
|
499
|
+
if (!ruleset.allowTerminalMigration()) {
|
|
500
|
+
revert JBTerminalStore_TerminalMigrationNotAllowed();
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Return the current balance, which is the amount being migrated.
|
|
504
|
+
balance = balanceOf[msg.sender][projectId][token];
|
|
505
|
+
|
|
506
|
+
// Set the balance to 0.
|
|
507
|
+
balanceOf[msg.sender][projectId][token] = 0;
|
|
463
508
|
}
|
|
464
509
|
|
|
465
|
-
/// @notice Records a
|
|
466
|
-
/// @dev
|
|
467
|
-
///
|
|
468
|
-
///
|
|
469
|
-
///
|
|
470
|
-
/// @param
|
|
471
|
-
/// @param
|
|
472
|
-
/// @
|
|
473
|
-
/// @
|
|
474
|
-
///
|
|
475
|
-
|
|
476
|
-
/// @param metadata Bytes to send to the data hook, if the project's current ruleset specifies one.
|
|
477
|
-
/// @return ruleset The ruleset during the cash out was made during, as a `JBRuleset` struct. This ruleset will
|
|
478
|
-
/// have a cash out tax rate provided by the cash out hook if applicable.
|
|
479
|
-
/// @return reclaimAmount The amount of tokens reclaimed from the terminal, as a fixed point number with 18
|
|
480
|
-
/// decimals.
|
|
481
|
-
/// @return cashOutTaxRate The cash out tax rate influencing the reclaim amount.
|
|
482
|
-
/// @return hookSpecifications A list of cash out hooks, including data and amounts to send to them. The terminal
|
|
483
|
-
/// should fulfill these specifications.
|
|
484
|
-
function recordCashOutFor(
|
|
485
|
-
address holder,
|
|
510
|
+
/// @notice Records a use of a project's surplus allowance.
|
|
511
|
+
/// @dev When surplus allowance is "used", it is taken out of the project's surplus within a terminal.
|
|
512
|
+
/// @param projectId The ID of the project to use the surplus allowance of.
|
|
513
|
+
/// @param accountingContext The accounting context of the token whose balances should contribute to the surplus
|
|
514
|
+
/// allowance being reclaimed from.
|
|
515
|
+
/// @param amount The amount to use from the surplus allowance, as a fixed point number.
|
|
516
|
+
/// @param currency The currency of the `amount`. Must match the currency of the surplus allowance.
|
|
517
|
+
/// @return ruleset The ruleset during the surplus allowance is being used during, as a `JBRuleset` struct.
|
|
518
|
+
/// @return usedAmount The amount of terminal tokens used, as a fixed point number with the same amount of decimals
|
|
519
|
+
/// as its relative terminal.
|
|
520
|
+
function recordUsedAllowanceOf(
|
|
486
521
|
uint256 projectId,
|
|
487
|
-
uint256 cashOutCount,
|
|
488
522
|
JBAccountingContext calldata accountingContext,
|
|
489
|
-
|
|
490
|
-
|
|
523
|
+
uint256 amount,
|
|
524
|
+
uint256 currency
|
|
491
525
|
)
|
|
492
526
|
external
|
|
493
527
|
override
|
|
494
|
-
returns (
|
|
495
|
-
JBRuleset memory ruleset,
|
|
496
|
-
uint256 reclaimAmount,
|
|
497
|
-
uint256 cashOutTaxRate,
|
|
498
|
-
JBCashOutHookSpecification[] memory hookSpecifications
|
|
499
|
-
)
|
|
528
|
+
returns (JBRuleset memory ruleset, uint256 usedAmount)
|
|
500
529
|
{
|
|
501
530
|
// Get a reference to the project's current ruleset.
|
|
502
531
|
ruleset = RULESETS.currentOf(projectId);
|
|
503
532
|
|
|
504
|
-
//
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
accountingContexts: balanceAccountingContexts,
|
|
519
|
-
ruleset: ruleset,
|
|
520
|
-
targetDecimals: accountingContext.decimals,
|
|
521
|
-
targetCurrency: accountingContext.currency
|
|
522
|
-
});
|
|
533
|
+
// Convert the amount to this store's terminal's token.
|
|
534
|
+
usedAmount = currency == accountingContext.currency
|
|
535
|
+
? amount
|
|
536
|
+
: mulDiv(
|
|
537
|
+
amount,
|
|
538
|
+
10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the `amount`'s
|
|
539
|
+
// fidelity as possible when converting.
|
|
540
|
+
PRICES.pricePerUnitOf({
|
|
541
|
+
projectId: projectId,
|
|
542
|
+
pricingCurrency: currency,
|
|
543
|
+
unitCurrency: accountingContext.currency,
|
|
544
|
+
decimals: _MAX_FIXED_POINT_FIDELITY
|
|
545
|
+
})
|
|
546
|
+
);
|
|
523
547
|
|
|
524
|
-
//
|
|
525
|
-
|
|
526
|
-
|
|
548
|
+
// Set the token being used as the only one to look for surplus within.
|
|
549
|
+
JBAccountingContext[] memory accountingContexts = new JBAccountingContext[](1);
|
|
550
|
+
accountingContexts[0] = accountingContext;
|
|
527
551
|
|
|
528
|
-
|
|
529
|
-
|
|
552
|
+
uint256 surplus = _surplusFrom({
|
|
553
|
+
terminal: msg.sender,
|
|
554
|
+
projectId: projectId,
|
|
555
|
+
accountingContexts: accountingContexts,
|
|
556
|
+
ruleset: ruleset,
|
|
557
|
+
targetDecimals: accountingContext.decimals,
|
|
558
|
+
targetCurrency: accountingContext.currency
|
|
559
|
+
});
|
|
530
560
|
|
|
531
|
-
//
|
|
532
|
-
|
|
533
|
-
// completely overriding the terminal's bonding curve math. For example, setting
|
|
534
|
-
// totalSupply = surplus makes reclaimAmount = cashOutCount, bypassing the curve.
|
|
535
|
-
// Project owners MUST audit their data hooks with the same rigor as the terminal.
|
|
561
|
+
// The amount being used must be available in the surplus.
|
|
562
|
+
if (usedAmount > surplus) revert JBTerminalStore_InadequateTerminalStoreBalance(usedAmount, surplus);
|
|
536
563
|
|
|
537
|
-
//
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
564
|
+
// Update the project's balance.
|
|
565
|
+
balanceOf[msg.sender][projectId][accountingContext.token] =
|
|
566
|
+
balanceOf[msg.sender][projectId][accountingContext.token] - usedAmount;
|
|
567
|
+
|
|
568
|
+
// Get a reference to the new used surplus allowance for this ruleset ID.
|
|
569
|
+
uint256 newUsedSurplusAllowanceOf =
|
|
570
|
+
usedSurplusAllowanceOf[msg.sender][projectId][accountingContext.token][ruleset.id][currency] + amount;
|
|
571
|
+
|
|
572
|
+
// Store the incremented value.
|
|
573
|
+
usedSurplusAllowanceOf[msg.sender][projectId][accountingContext.token][ruleset.id][currency] =
|
|
574
|
+
newUsedSurplusAllowanceOf;
|
|
575
|
+
|
|
576
|
+
// There must be sufficient surplus allowance available.
|
|
577
|
+
uint256 surplusAllowance = IJBController(address(DIRECTORY.controllerOf(projectId))).FUND_ACCESS_LIMITS()
|
|
578
|
+
.surplusAllowanceOf({
|
|
543
579
|
projectId: projectId,
|
|
544
580
|
rulesetId: ruleset.id,
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
token: accountingContext.token,
|
|
549
|
-
value: currentSurplus,
|
|
550
|
-
decimals: accountingContext.decimals,
|
|
551
|
-
currency: accountingContext.currency
|
|
552
|
-
}),
|
|
553
|
-
useTotalSurplus: ruleset.useTotalSurplusForCashOuts(),
|
|
554
|
-
cashOutTaxRate: ruleset.cashOutTaxRate(),
|
|
555
|
-
metadata: metadata
|
|
581
|
+
terminal: msg.sender,
|
|
582
|
+
token: accountingContext.token,
|
|
583
|
+
currency: currency
|
|
556
584
|
});
|
|
557
585
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
cashOutTaxRate = ruleset.cashOutTaxRate();
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (currentSurplus != 0) {
|
|
565
|
-
// Calculate reclaim amount using the current surplus amount.
|
|
566
|
-
reclaimAmount = JBCashOuts.cashOutFrom({
|
|
567
|
-
surplus: currentSurplus,
|
|
568
|
-
cashOutCount: cashOutCount,
|
|
569
|
-
totalSupply: totalSupply,
|
|
570
|
-
cashOutTaxRate: cashOutTaxRate
|
|
571
|
-
});
|
|
586
|
+
// Make sure the new used amount is within the allowance.
|
|
587
|
+
if (newUsedSurplusAllowanceOf > surplusAllowance || surplusAllowance == 0) {
|
|
588
|
+
revert JBTerminalStore_InadequateControllerAllowance(newUsedSurplusAllowanceOf, surplusAllowance);
|
|
572
589
|
}
|
|
590
|
+
}
|
|
573
591
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
// Ensure that the specifications have valid amounts.
|
|
578
|
-
if (hookSpecifications.length != 0) {
|
|
579
|
-
// Keep a reference to the number of cash out hooks specified.
|
|
580
|
-
uint256 numberOfSpecifications = hookSpecifications.length;
|
|
581
|
-
|
|
582
|
-
// Loop through each specification.
|
|
583
|
-
for (uint256 i; i < numberOfSpecifications; i++) {
|
|
584
|
-
// Get a reference to the specification's amount.
|
|
585
|
-
uint256 specificationAmount = hookSpecifications[i].amount;
|
|
592
|
+
//*********************************************************************//
|
|
593
|
+
// ------------------------- external views -------------------------- //
|
|
594
|
+
//*********************************************************************//
|
|
586
595
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
596
|
+
/// @notice Returns the number of surplus terminal tokens that would be reclaimed by cashing out a given project's
|
|
597
|
+
/// tokens based on its current ruleset and the given total project token supply and total terminal token surplus.
|
|
598
|
+
/// @param projectId The ID of the project whose project tokens would be cashed out.
|
|
599
|
+
/// @param cashOutCount The number of project tokens that would be cashed out, as a fixed point number with 18
|
|
600
|
+
/// decimals.
|
|
601
|
+
/// @param totalSupply The total project token supply, as a fixed point number with 18 decimals.
|
|
602
|
+
/// @param surplus The total terminal token surplus amount, as a fixed point number.
|
|
603
|
+
/// @return The number of surplus terminal tokens that would be reclaimed, as a fixed point number with the same
|
|
604
|
+
/// number of decimals as the provided `surplus`.
|
|
605
|
+
function currentReclaimableSurplusOf(
|
|
606
|
+
uint256 projectId,
|
|
607
|
+
uint256 cashOutCount,
|
|
608
|
+
uint256 totalSupply,
|
|
609
|
+
uint256 surplus
|
|
610
|
+
)
|
|
611
|
+
external
|
|
612
|
+
view
|
|
613
|
+
override
|
|
614
|
+
returns (uint256)
|
|
615
|
+
{
|
|
616
|
+
// If there's no surplus, nothing can be reclaimed.
|
|
617
|
+
if (surplus == 0) return 0;
|
|
594
618
|
|
|
595
|
-
//
|
|
596
|
-
if (
|
|
597
|
-
revert JBTerminalStore_InadequateTerminalStoreBalance(
|
|
598
|
-
balanceDiff, balanceOf[msg.sender][projectId][accountingContext.token]
|
|
599
|
-
);
|
|
600
|
-
}
|
|
619
|
+
// Can't cash out more tokens than are in the total supply.
|
|
620
|
+
if (cashOutCount > totalSupply) return 0;
|
|
601
621
|
|
|
602
|
-
//
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
622
|
+
// Get a reference to the project's current ruleset.
|
|
623
|
+
JBRuleset memory ruleset = RULESETS.currentOf(projectId);
|
|
624
|
+
|
|
625
|
+
// Return the amount of surplus terminal tokens that would be reclaimed.
|
|
626
|
+
return JBCashOuts.cashOutFrom({
|
|
627
|
+
surplus: surplus,
|
|
628
|
+
cashOutCount: cashOutCount,
|
|
629
|
+
totalSupply: totalSupply,
|
|
630
|
+
cashOutTaxRate: ruleset.cashOutTaxRate()
|
|
631
|
+
});
|
|
609
632
|
}
|
|
610
633
|
|
|
611
|
-
/// @notice
|
|
612
|
-
///
|
|
613
|
-
///
|
|
614
|
-
/// @
|
|
615
|
-
///
|
|
616
|
-
///
|
|
617
|
-
/// @param
|
|
618
|
-
/// @param
|
|
619
|
-
///
|
|
620
|
-
/// @param
|
|
621
|
-
///
|
|
622
|
-
/// @
|
|
623
|
-
/// @
|
|
624
|
-
///
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
JBTokenAmount calldata amount,
|
|
634
|
+
/// @notice Returns the number of surplus terminal tokens that would be reclaimed from a terminal by cashing out a
|
|
635
|
+
/// given number of tokens, based on the total token supply and total surplus.
|
|
636
|
+
/// @dev The returned amount in terms of the specified `terminal`'s base currency.
|
|
637
|
+
/// @dev The returned amount is represented as a fixed point number with the same amount of decimals as the
|
|
638
|
+
/// specified terminal.
|
|
639
|
+
/// @param projectId The ID of the project whose tokens would be cashed out.
|
|
640
|
+
/// @param cashOutCount The number of tokens that would be cashed out, as a fixed point number with 18 decimals.
|
|
641
|
+
/// @param terminals The terminals that would be cashed out from. If this is an empty array, surplus within all
|
|
642
|
+
/// the project's terminals are considered.
|
|
643
|
+
/// @param accountingContexts The accounting contexts of the surplus terminal tokens that would be reclaimed. Pass
|
|
644
|
+
/// an empty array to use all of the project's accounting contexts.
|
|
645
|
+
/// @param decimals The number of decimals to include in the resulting fixed point number.
|
|
646
|
+
/// @param currency The currency that the resulting number will be in terms of.
|
|
647
|
+
/// @return The amount of surplus terminal tokens that would be reclaimed by cashing out `cashOutCount`
|
|
648
|
+
/// tokens.
|
|
649
|
+
function currentReclaimableSurplusOf(
|
|
628
650
|
uint256 projectId,
|
|
629
|
-
|
|
630
|
-
|
|
651
|
+
uint256 cashOutCount,
|
|
652
|
+
IJBTerminal[] calldata terminals,
|
|
653
|
+
JBAccountingContext[] calldata accountingContexts,
|
|
654
|
+
uint256 decimals,
|
|
655
|
+
uint256 currency
|
|
631
656
|
)
|
|
632
657
|
external
|
|
658
|
+
view
|
|
633
659
|
override
|
|
634
|
-
returns (
|
|
660
|
+
returns (uint256)
|
|
635
661
|
{
|
|
636
662
|
// Get a reference to the project's current ruleset.
|
|
637
|
-
ruleset = RULESETS.currentOf(projectId);
|
|
638
|
-
|
|
639
|
-
// The project must have a ruleset.
|
|
640
|
-
if (ruleset.cycleNumber == 0) revert JBTerminalStore_RulesetNotFound(projectId);
|
|
641
|
-
|
|
642
|
-
// The ruleset must not have payments paused.
|
|
643
|
-
if (ruleset.pausePay()) revert JBTerminalStore_RulesetPaymentPaused();
|
|
644
|
-
|
|
645
|
-
// The weight according to which new tokens are to be minted, as a fixed point number with 18 decimals.
|
|
646
|
-
uint256 weight;
|
|
647
|
-
|
|
648
|
-
// SECURITY NOTE: The data hook has absolute control over payment token minting.
|
|
649
|
-
// It can return an arbitrary weight (overriding the ruleset's weight) and hook specifications
|
|
650
|
-
// that divert payment funds to external hooks before they reach the project's balance.
|
|
651
|
-
// Project owners MUST audit their data hooks with the same rigor as the terminal.
|
|
652
|
-
|
|
653
|
-
// If the ruleset has a data hook enabled for payments, use it to derive a weight and memo.
|
|
654
|
-
if (ruleset.useDataHookForPay() && ruleset.dataHook() != address(0)) {
|
|
655
|
-
// Create the pay context that'll be sent to the data hook.
|
|
656
|
-
JBBeforePayRecordedContext memory context = JBBeforePayRecordedContext({
|
|
657
|
-
terminal: msg.sender,
|
|
658
|
-
payer: payer,
|
|
659
|
-
amount: amount,
|
|
660
|
-
projectId: projectId,
|
|
661
|
-
rulesetId: ruleset.id,
|
|
662
|
-
beneficiary: beneficiary,
|
|
663
|
-
weight: ruleset.weight,
|
|
664
|
-
reservedPercent: ruleset.reservedPercent(),
|
|
665
|
-
metadata: metadata
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
(weight, hookSpecifications) = IJBRulesetDataHook(ruleset.dataHook()).beforePayRecordedWith(context);
|
|
669
|
-
}
|
|
670
|
-
// Otherwise use the ruleset's weight
|
|
671
|
-
else {
|
|
672
|
-
weight = ruleset.weight;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// Keep a reference to the amount that should be added to the project's balance.
|
|
676
|
-
uint256 balanceDiff = amount.value;
|
|
677
|
-
|
|
678
|
-
// Scoped section preventing stack too deep.
|
|
679
|
-
{
|
|
680
|
-
// Keep a reference to the number of hook specifications.
|
|
681
|
-
uint256 numberOfSpecifications = hookSpecifications.length;
|
|
682
|
-
|
|
683
|
-
// Ensure that the specifications have valid amounts.
|
|
684
|
-
for (uint256 i; i < numberOfSpecifications; i++) {
|
|
685
|
-
// Get a reference to the specification's amount.
|
|
686
|
-
uint256 specifiedAmount = hookSpecifications[i].amount;
|
|
663
|
+
JBRuleset memory ruleset = RULESETS.currentOf(projectId);
|
|
687
664
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
665
|
+
// Get the current surplus amount.
|
|
666
|
+
// If a terminal wasn't provided, use the total surplus across all terminals. Otherwise,
|
|
667
|
+
// get the `terminal`'s surplus.
|
|
668
|
+
uint256 currentSurplus = JBSurplus.currentSurplusOf({
|
|
669
|
+
projectId: projectId,
|
|
670
|
+
terminals: terminals.length != 0 ? terminals : DIRECTORY.terminalsOf(projectId),
|
|
671
|
+
accountingContexts: accountingContexts,
|
|
672
|
+
decimals: decimals,
|
|
673
|
+
currency: currency
|
|
674
|
+
});
|
|
694
675
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
676
|
+
// If there's no surplus, nothing can be reclaimed.
|
|
677
|
+
if (currentSurplus == 0) return 0;
|
|
700
678
|
|
|
701
|
-
//
|
|
702
|
-
|
|
679
|
+
// Get the project token's total supply.
|
|
680
|
+
uint256 totalSupply =
|
|
681
|
+
IJBController(address(DIRECTORY.controllerOf(projectId))).totalTokenSupplyWithReservedTokensOf(projectId);
|
|
703
682
|
|
|
704
|
-
//
|
|
705
|
-
if (
|
|
706
|
-
balanceOf[msg.sender][projectId][amount.token] =
|
|
707
|
-
balanceOf[msg.sender][projectId][amount.token] + balanceDiff;
|
|
708
|
-
}
|
|
683
|
+
// Can't cash out more tokens than are in the total supply.
|
|
684
|
+
if (cashOutCount > totalSupply) return 0;
|
|
709
685
|
|
|
710
|
-
//
|
|
711
|
-
|
|
686
|
+
// Return the amount of surplus terminal tokens that would be reclaimed.
|
|
687
|
+
return JBCashOuts.cashOutFrom({
|
|
688
|
+
surplus: currentSurplus,
|
|
689
|
+
cashOutCount: cashOutCount,
|
|
690
|
+
totalSupply: totalSupply,
|
|
691
|
+
cashOutTaxRate: ruleset.cashOutTaxRate()
|
|
692
|
+
});
|
|
693
|
+
}
|
|
712
694
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
695
|
+
/// @notice Gets the current surplus amount in a terminal for a specified project.
|
|
696
|
+
/// @dev The surplus is the amount of funds a project has in a terminal in excess of its payout limit.
|
|
697
|
+
/// @dev The surplus is represented as a fixed point number with the same amount of decimals as the specified
|
|
698
|
+
/// terminal.
|
|
699
|
+
/// @param terminal The terminal the surplus is being calculated for.
|
|
700
|
+
/// @param projectId The ID of the project to get surplus for.
|
|
701
|
+
/// @param accountingContexts The accounting contexts of tokens whose balances should contribute to the surplus
|
|
702
|
+
/// being calculated.
|
|
703
|
+
/// @param currency The currency the resulting amount should be in terms of.
|
|
704
|
+
/// @param decimals The number of decimals to expect in the resulting fixed point number.
|
|
705
|
+
/// @return The current surplus amount the project has in the specified terminal.
|
|
706
|
+
function currentSurplusOf(
|
|
707
|
+
address terminal,
|
|
708
|
+
uint256 projectId,
|
|
709
|
+
JBAccountingContext[] calldata accountingContexts,
|
|
710
|
+
uint256 decimals,
|
|
711
|
+
uint256 currency
|
|
712
|
+
)
|
|
713
|
+
external
|
|
714
|
+
view
|
|
715
|
+
override
|
|
716
|
+
returns (uint256)
|
|
717
|
+
{
|
|
718
|
+
// Return the surplus during the project's current ruleset.
|
|
719
|
+
return _surplusFrom({
|
|
720
|
+
terminal: terminal,
|
|
721
|
+
projectId: projectId,
|
|
722
|
+
accountingContexts: accountingContexts,
|
|
723
|
+
ruleset: RULESETS.currentOf(projectId),
|
|
724
|
+
targetDecimals: decimals,
|
|
725
|
+
targetCurrency: currency
|
|
726
|
+
});
|
|
727
|
+
}
|
|
725
728
|
|
|
726
|
-
|
|
727
|
-
|
|
729
|
+
/// @notice Gets the current surplus amount for a specified project across all terminals.
|
|
730
|
+
/// @param projectId The ID of the project to get the total surplus for.
|
|
731
|
+
/// @param decimals The number of decimals that the fixed point surplus should include.
|
|
732
|
+
/// @param currency The currency that the total surplus should be in terms of.
|
|
733
|
+
/// @return The current total surplus amount that the project has across all terminals.
|
|
734
|
+
function currentTotalSurplusOf(
|
|
735
|
+
uint256 projectId,
|
|
736
|
+
uint256 decimals,
|
|
737
|
+
uint256 currency
|
|
738
|
+
)
|
|
739
|
+
external
|
|
740
|
+
view
|
|
741
|
+
override
|
|
742
|
+
returns (uint256)
|
|
743
|
+
{
|
|
744
|
+
return JBSurplus.currentSurplusOf({
|
|
745
|
+
projectId: projectId,
|
|
746
|
+
terminals: DIRECTORY.terminalsOf(projectId),
|
|
747
|
+
accountingContexts: new JBAccountingContext[](0),
|
|
748
|
+
decimals: decimals,
|
|
749
|
+
currency: currency
|
|
750
|
+
});
|
|
728
751
|
}
|
|
729
752
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
/// @
|
|
735
|
-
///
|
|
736
|
-
/// @
|
|
737
|
-
///
|
|
738
|
-
/// @
|
|
739
|
-
/// @
|
|
740
|
-
///
|
|
741
|
-
|
|
753
|
+
//*********************************************************************//
|
|
754
|
+
// -------------------------- internal views ------------------------- //
|
|
755
|
+
//*********************************************************************//
|
|
756
|
+
|
|
757
|
+
/// @notice Gets a project's surplus amount in a terminal as measured by a given ruleset, across multiple accounting
|
|
758
|
+
/// contexts.
|
|
759
|
+
/// @dev This amount changes as the value of the balance changes in relation to the currency being used to measure
|
|
760
|
+
/// various payout limits.
|
|
761
|
+
/// @param terminal The terminal the surplus is being calculated for.
|
|
762
|
+
/// @param projectId The ID of the project to get the surplus for.
|
|
763
|
+
/// @param accountingContexts The accounting contexts of tokens whose balances should contribute to the surplus
|
|
764
|
+
/// being calculated.
|
|
765
|
+
/// @param ruleset The ID of the ruleset to base the surplus on.
|
|
766
|
+
/// @param targetDecimals The number of decimals to include in the resulting fixed point number.
|
|
767
|
+
/// @param targetCurrency The currency that the reported surplus is expected to be in terms of.
|
|
768
|
+
/// @return surplus The surplus of funds in terms of `targetCurrency`, as a fixed point number with
|
|
769
|
+
/// `targetDecimals` decimals.
|
|
770
|
+
function _surplusFrom(
|
|
771
|
+
address terminal,
|
|
742
772
|
uint256 projectId,
|
|
743
|
-
JBAccountingContext
|
|
744
|
-
|
|
745
|
-
uint256
|
|
773
|
+
JBAccountingContext[] memory accountingContexts,
|
|
774
|
+
JBRuleset memory ruleset,
|
|
775
|
+
uint256 targetDecimals,
|
|
776
|
+
uint256 targetCurrency
|
|
746
777
|
)
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
returns (
|
|
778
|
+
internal
|
|
779
|
+
view
|
|
780
|
+
returns (uint256 surplus)
|
|
750
781
|
{
|
|
751
|
-
//
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
// Convert the amount to the balance's currency.
|
|
755
|
-
amountPaidOut = (currency == accountingContext.currency)
|
|
756
|
-
? amount
|
|
757
|
-
: mulDiv(
|
|
758
|
-
amount,
|
|
759
|
-
10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the `_amount`'s
|
|
760
|
-
// fidelity as possible when converting.
|
|
761
|
-
PRICES.pricePerUnitOf({
|
|
762
|
-
projectId: projectId,
|
|
763
|
-
pricingCurrency: currency,
|
|
764
|
-
unitCurrency: accountingContext.currency,
|
|
765
|
-
decimals: _MAX_FIXED_POINT_FIDELITY
|
|
766
|
-
})
|
|
767
|
-
);
|
|
768
|
-
|
|
769
|
-
// The amount being paid out must be available.
|
|
770
|
-
if (amountPaidOut > balanceOf[msg.sender][projectId][accountingContext.token]) {
|
|
771
|
-
revert JBTerminalStore_InadequateTerminalStoreBalance(
|
|
772
|
-
amountPaidOut, balanceOf[msg.sender][projectId][accountingContext.token]
|
|
773
|
-
);
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// Removed the paid out funds from the project's token balance.
|
|
777
|
-
unchecked {
|
|
778
|
-
balanceOf[msg.sender][projectId][accountingContext.token] =
|
|
779
|
-
balanceOf[msg.sender][projectId][accountingContext.token] - amountPaidOut;
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// The new total amount which has been paid out during this ruleset.
|
|
783
|
-
uint256 newUsedPayoutLimitOf =
|
|
784
|
-
usedPayoutLimitOf[msg.sender][projectId][accountingContext.token][ruleset.cycleNumber][currency] + amount;
|
|
785
|
-
|
|
786
|
-
// Store the new amount.
|
|
787
|
-
usedPayoutLimitOf[msg.sender][projectId][accountingContext.token][ruleset.cycleNumber][currency] =
|
|
788
|
-
newUsedPayoutLimitOf;
|
|
782
|
+
// Keep a reference to the number of tokens being iterated on.
|
|
783
|
+
uint256 numberOfTokenAccountingContexts = accountingContexts.length;
|
|
789
784
|
|
|
790
|
-
//
|
|
791
|
-
uint256
|
|
792
|
-
|
|
785
|
+
// Add payout limits from each token.
|
|
786
|
+
for (uint256 i; i < numberOfTokenAccountingContexts; i++) {
|
|
787
|
+
uint256 tokenSurplus = _tokenSurplusFrom({
|
|
788
|
+
terminal: terminal,
|
|
793
789
|
projectId: projectId,
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
790
|
+
accountingContext: accountingContexts[i],
|
|
791
|
+
ruleset: ruleset,
|
|
792
|
+
targetDecimals: targetDecimals,
|
|
793
|
+
targetCurrency: targetCurrency
|
|
798
794
|
});
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
if (newUsedPayoutLimitOf > payoutLimit || payoutLimit == 0) {
|
|
802
|
-
revert JBTerminalStore_InadequateControllerPayoutLimit(newUsedPayoutLimitOf, payoutLimit);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
/// @notice Records the migration of funds from this store.
|
|
807
|
-
/// @param projectId The ID of the project being migrated.
|
|
808
|
-
/// @param token The token being migrated.
|
|
809
|
-
/// @return balance The project's current balance (which is being migrated), as a fixed point number with the same
|
|
810
|
-
/// amount of decimals as its relative terminal.
|
|
811
|
-
function recordTerminalMigration(uint256 projectId, address token) external override returns (uint256 balance) {
|
|
812
|
-
// Get a reference to the project's current ruleset.
|
|
813
|
-
JBRuleset memory ruleset = RULESETS.currentOf(projectId);
|
|
814
|
-
|
|
815
|
-
// Terminal migration must be allowed.
|
|
816
|
-
if (!ruleset.allowTerminalMigration()) {
|
|
817
|
-
revert JBTerminalStore_TerminalMigrationNotAllowed();
|
|
795
|
+
// Increment the surplus with any remaining balance.
|
|
796
|
+
if (tokenSurplus > 0) surplus += tokenSurplus;
|
|
818
797
|
}
|
|
819
|
-
|
|
820
|
-
// Return the current balance, which is the amount being migrated.
|
|
821
|
-
balance = balanceOf[msg.sender][projectId][token];
|
|
822
|
-
|
|
823
|
-
// Set the balance to 0.
|
|
824
|
-
balanceOf[msg.sender][projectId][token] = 0;
|
|
825
798
|
}
|
|
826
799
|
|
|
827
|
-
/// @notice
|
|
828
|
-
///
|
|
829
|
-
/// @
|
|
830
|
-
///
|
|
831
|
-
///
|
|
832
|
-
/// @param
|
|
833
|
-
/// @param
|
|
834
|
-
///
|
|
835
|
-
/// @
|
|
836
|
-
///
|
|
837
|
-
|
|
800
|
+
/// @notice Get a project's surplus amount of a specific token in a given terminal as measured by a given ruleset
|
|
801
|
+
/// (one specific accounting context).
|
|
802
|
+
/// @dev This amount changes as the value of the balance changes in relation to the currency being used to measure
|
|
803
|
+
/// the payout limits.
|
|
804
|
+
/// @param terminal The terminal the surplus is being calculated for.
|
|
805
|
+
/// @param projectId The ID of the project to get the surplus of.
|
|
806
|
+
/// @param accountingContext The accounting context of the token whose balance should contribute to the surplus
|
|
807
|
+
/// being measured.
|
|
808
|
+
/// @param ruleset The ID of the ruleset to base the surplus calculation on.
|
|
809
|
+
/// @param targetDecimals The number of decimals to include in the resulting fixed point number.
|
|
810
|
+
/// @param targetCurrency The currency that the reported surplus is expected to be in terms of.
|
|
811
|
+
/// @return surplus The surplus of funds in terms of `targetCurrency`, as a fixed point number with
|
|
812
|
+
/// `targetDecimals` decimals.
|
|
813
|
+
function _tokenSurplusFrom(
|
|
814
|
+
address terminal,
|
|
838
815
|
uint256 projectId,
|
|
839
|
-
JBAccountingContext
|
|
840
|
-
|
|
841
|
-
uint256
|
|
816
|
+
JBAccountingContext memory accountingContext,
|
|
817
|
+
JBRuleset memory ruleset,
|
|
818
|
+
uint256 targetDecimals,
|
|
819
|
+
uint256 targetCurrency
|
|
842
820
|
)
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
returns (
|
|
821
|
+
internal
|
|
822
|
+
view
|
|
823
|
+
returns (uint256 surplus)
|
|
846
824
|
{
|
|
847
|
-
//
|
|
848
|
-
|
|
825
|
+
// Keep a reference to the balance.
|
|
826
|
+
surplus = balanceOf[terminal][projectId][accountingContext.token];
|
|
849
827
|
|
|
850
|
-
//
|
|
851
|
-
|
|
852
|
-
?
|
|
828
|
+
// If needed, adjust the decimals of the fixed point number to have the correct decimals.
|
|
829
|
+
surplus = accountingContext.decimals == targetDecimals
|
|
830
|
+
? surplus
|
|
831
|
+
: JBFixedPointNumber.adjustDecimals({
|
|
832
|
+
value: surplus, decimals: accountingContext.decimals, targetDecimals: targetDecimals
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
// Add up all the balances.
|
|
836
|
+
surplus = (surplus == 0 || accountingContext.currency == targetCurrency)
|
|
837
|
+
? surplus
|
|
853
838
|
: mulDiv(
|
|
854
|
-
|
|
855
|
-
10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the
|
|
856
|
-
// fidelity as possible when converting.
|
|
839
|
+
surplus,
|
|
840
|
+
10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the
|
|
841
|
+
// `_payoutLimitRemaining`'s fidelity as possible when converting.
|
|
857
842
|
PRICES.pricePerUnitOf({
|
|
858
843
|
projectId: projectId,
|
|
859
|
-
pricingCurrency: currency,
|
|
860
|
-
unitCurrency:
|
|
844
|
+
pricingCurrency: accountingContext.currency,
|
|
845
|
+
unitCurrency: targetCurrency,
|
|
861
846
|
decimals: _MAX_FIXED_POINT_FIDELITY
|
|
862
847
|
})
|
|
863
848
|
);
|
|
864
849
|
|
|
865
|
-
//
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
projectId: projectId,
|
|
872
|
-
accountingContexts: accountingContexts,
|
|
873
|
-
ruleset: ruleset,
|
|
874
|
-
targetDecimals: accountingContext.decimals,
|
|
875
|
-
targetCurrency: accountingContext.currency
|
|
876
|
-
});
|
|
850
|
+
// Get a reference to the payout limit during the ruleset for the token.
|
|
851
|
+
JBCurrencyAmount[] memory payoutLimits = IJBController(address(DIRECTORY.controllerOf(projectId)))
|
|
852
|
+
.FUND_ACCESS_LIMITS()
|
|
853
|
+
.payoutLimitsOf({
|
|
854
|
+
projectId: projectId, rulesetId: ruleset.id, terminal: address(terminal), token: accountingContext.token
|
|
855
|
+
});
|
|
877
856
|
|
|
878
|
-
//
|
|
879
|
-
|
|
857
|
+
// Keep a reference to the number of payout limits being iterated on.
|
|
858
|
+
uint256 numberOfPayoutLimits = payoutLimits.length;
|
|
880
859
|
|
|
881
|
-
//
|
|
882
|
-
|
|
883
|
-
|
|
860
|
+
// Loop through each payout limit to determine the cumulative normalized payout limit remaining.
|
|
861
|
+
for (uint256 i; i < numberOfPayoutLimits; i++) {
|
|
862
|
+
JBCurrencyAmount memory payoutLimit = payoutLimits[i];
|
|
884
863
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
864
|
+
// Set the payout limit value to the amount still available to pay out during the ruleset.
|
|
865
|
+
{
|
|
866
|
+
uint256 remaining = payoutLimit.amount
|
|
867
|
+
- usedPayoutLimitOf[
|
|
868
|
+
terminal
|
|
869
|
+
][projectId][accountingContext.token][ruleset.cycleNumber][payoutLimit.currency];
|
|
870
|
+
if (remaining > type(uint224).max) revert JBTerminalStore_Uint224Overflow(remaining);
|
|
871
|
+
payoutLimit.amount = uint224(remaining);
|
|
872
|
+
}
|
|
888
873
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
874
|
+
// Adjust the decimals of the fixed point number if needed to have the correct decimals.
|
|
875
|
+
if (accountingContext.decimals != targetDecimals) {
|
|
876
|
+
uint256 adjusted = JBFixedPointNumber.adjustDecimals({
|
|
877
|
+
value: payoutLimit.amount, decimals: accountingContext.decimals, targetDecimals: targetDecimals
|
|
878
|
+
});
|
|
879
|
+
if (adjusted > type(uint224).max) revert JBTerminalStore_Uint224Overflow(adjusted);
|
|
880
|
+
payoutLimit.amount = uint224(adjusted);
|
|
881
|
+
}
|
|
892
882
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
883
|
+
// Convert the `payoutLimit`'s amount to be in terms of the provided currency.
|
|
884
|
+
if (payoutLimit.amount != 0 && payoutLimit.currency != targetCurrency) {
|
|
885
|
+
uint256 converted = mulDiv(
|
|
886
|
+
payoutLimit.amount,
|
|
887
|
+
10 ** _MAX_FIXED_POINT_FIDELITY, // Use `_MAX_FIXED_POINT_FIDELITY` to keep as much of the
|
|
888
|
+
// `payoutLimitRemaining`'s fidelity as possible when converting.
|
|
889
|
+
PRICES.pricePerUnitOf({
|
|
890
|
+
projectId: projectId,
|
|
891
|
+
pricingCurrency: payoutLimit.currency,
|
|
892
|
+
unitCurrency: targetCurrency,
|
|
893
|
+
decimals: _MAX_FIXED_POINT_FIDELITY
|
|
894
|
+
})
|
|
895
|
+
);
|
|
896
|
+
if (converted > type(uint224).max) revert JBTerminalStore_Uint224Overflow(converted);
|
|
897
|
+
payoutLimit.amount = uint224(converted);
|
|
898
|
+
}
|
|
902
899
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
900
|
+
// Decrement from the balance until it reaches zero.
|
|
901
|
+
if (surplus > payoutLimit.amount) {
|
|
902
|
+
surplus -= payoutLimit.amount;
|
|
903
|
+
} else {
|
|
904
|
+
return 0;
|
|
905
|
+
}
|
|
906
906
|
}
|
|
907
907
|
}
|
|
908
908
|
}
|