@bananapus/core-v6 0.0.21 → 0.0.23
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/ADMINISTRATION.md +0 -1
- package/AUDIT_INSTRUCTIONS.md +1 -1
- package/CHANGE_LOG.md +3 -3
- package/RISKS.md +3 -3
- package/SKILLS.md +8 -8
- package/USER_JOURNEYS.md +1 -1
- package/foundry.toml +0 -1
- package/package.json +1 -1
- package/src/JBMultiTerminal.sol +92 -192
- package/src/JBTerminalStore.sol +414 -256
- package/src/interfaces/IJBMultiTerminal.sol +0 -4
- package/src/interfaces/IJBTerminal.sol +4 -4
- package/src/interfaces/IJBTerminalStore.sol +65 -33
- package/src/libraries/JBPayoutSplitGroupLib.sol +0 -1
- package/src/libraries/JBSurplus.sol +3 -4
- package/test/ComprehensiveInvariant.t.sol +5 -7
- package/test/CoreExploitTests.t.sol +18 -23
- package/test/TestCashOut.sol +6 -6
- package/test/TestMultiTerminalSurplus.sol +4 -4
- package/test/TestMultiTokenSurplus.sol +6 -23
- package/test/TestTerminalMigration.sol +2 -7
- package/test/fork/TestSequencerPriceFeedFork.sol +1 -1
- package/test/fork/TestTerminalPreviewParityFork.sol +0 -1
- package/test/invariants/TerminalStoreInvariant.t.sol +5 -7
- package/test/units/static/JBMultiTerminal/JBMultiTerminalSetup.sol +1 -2
- package/test/units/static/JBMultiTerminal/TestAccountingContextsOf.sol +23 -24
- package/test/units/static/JBMultiTerminal/TestAddAccountingContextsFor.sol +79 -119
- package/test/units/static/JBMultiTerminal/TestAddToBalanceOf.sol +33 -26
- package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +32 -27
- package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +22 -4
- package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +8 -5
- package/test/units/static/JBMultiTerminal/TestPay.sol +41 -33
- package/test/units/static/JBMultiTerminal/TestPreviewCashOutFrom.sol +19 -18
- package/test/units/static/JBMultiTerminal/TestPreviewPayFor.sol +38 -22
- package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +9 -6
- package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +4 -4
- package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +37 -32
- package/test/units/static/JBSurplus/TestSurplusFuzz.sol +5 -20
- package/test/units/static/JBTerminalStore/JBTerminalStoreSetup.sol +17 -0
- package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +120 -246
- package/test/units/static/JBTerminalStore/TestCurrentSurplusOf.sol +29 -7
- package/test/units/static/JBTerminalStore/TestCurrentTotalSurplusOf.sol +88 -20
- package/test/units/static/JBTerminalStore/TestPreviewCashOutFrom.sol +30 -29
- package/test/units/static/JBTerminalStore/TestPreviewPayFrom.sol +46 -16
- package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +24 -53
- package/test/units/static/JBTerminalStore/TestRecordPayoutFor.sol +24 -4
- package/test/units/static/JBTerminalStore/TestRecordUsedAllowanceOf.sol +14 -4
- package/test/units/static/JBTerminalStore/TestUint224Overflow.sol +21 -3
package/ADMINISTRATION.md
CHANGED
|
@@ -258,7 +258,6 @@ The following values are set at deploy time and cannot be changed:
|
|
|
258
258
|
- `PERMISSIONS` -- the permissions contract
|
|
259
259
|
- `PERMIT2` -- the Uniswap Permit2 contract
|
|
260
260
|
- `PROJECTS` -- the projects NFT contract
|
|
261
|
-
- `RULESETS` -- derived from STORE.RULESETS()
|
|
262
261
|
- `SPLITS` -- the splits contract
|
|
263
262
|
- `STORE` -- the terminal store contract
|
|
264
263
|
- `TOKENS` -- the tokens contract
|
package/AUDIT_INSTRUCTIONS.md
CHANGED
|
@@ -263,7 +263,7 @@ These are the patterns that will trip you up if you are not aware of them:
|
|
|
263
263
|
13. **`JBERC20` is cloned via `Clones.clone()`** -- constructor sets invalid name/symbol; real values set in `initialize()`.
|
|
264
264
|
14. **Named returns auto-return** -- several functions use named return variables without explicit `return` statements.
|
|
265
265
|
15. **Preview functions call data hooks** -- `previewPayFor`, `previewCashOutFrom`, and their store-level counterparts invoke data hooks during simulation. A reverting data hook will cause the preview to revert. Preview functions are `view` but still make external calls to hooks.
|
|
266
|
-
16. **Store preview functions
|
|
266
|
+
16. **Store preview functions take an explicit terminal parameter** -- `JBTerminalStore.previewPayFrom` and `previewCashOutFrom` take an explicit `terminal` address for balance/surplus lookups. Callers must pass a registered terminal to get correct results. The terminal-level `JBMultiTerminal.previewPayFor` / `previewCashOutFrom` handle this automatically by passing `address(this)`.
|
|
267
267
|
|
|
268
268
|
## Priority Areas to Audit
|
|
269
269
|
|
package/CHANGE_LOG.md
CHANGED
|
@@ -18,8 +18,8 @@ A new `_feeFreeSurplusOf` mapping (`projectId => token => uint256`) tracks cumul
|
|
|
18
18
|
|
|
19
19
|
Two `view` functions on `JBTerminalStore` and `IJBTerminalStore`:
|
|
20
20
|
|
|
21
|
-
- `previewPayFrom(address payer, JBTokenAmount amount, uint256 projectId, address beneficiary, bytes metadata)` -- Simulates a payment and returns `(JBRuleset ruleset, uint256 tokenCount, JBPayHookSpecification[] hookSpecifications)`. Uses `
|
|
22
|
-
- `previewCashOutFrom(address holder, uint256 projectId, uint256 cashOutCount,
|
|
21
|
+
- `previewPayFrom(address terminal, address payer, JBTokenAmount amount, uint256 projectId, address beneficiary, bytes metadata)` -- Simulates a payment and returns `(JBRuleset ruleset, uint256 tokenCount, JBPayHookSpecification[] hookSpecifications)`. Uses the explicit `terminal` parameter for balance/surplus lookups. Invokes data hooks if configured. Does not modify state.
|
|
22
|
+
- `previewCashOutFrom(address terminal, address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, bool beneficiaryIsFeeless, bytes metadata)` -- Simulates a cash out and returns `(JBRuleset ruleset, uint256 reclaimAmount, uint256 cashOutTaxRate, JBCashOutHookSpecification[] hookSpecifications)`. Uses the explicit `terminal` parameter for balance/surplus lookups. Invokes data hooks if configured. Does not modify state.
|
|
23
23
|
|
|
24
24
|
Internal computation logic was extracted into shared `_computePayFrom` and `_computeCashOutFrom` view helpers; the existing `recordPaymentFrom` and `recordCashOutFor` functions were refactored to call these helpers before writing state.
|
|
25
25
|
|
|
@@ -117,7 +117,7 @@ Parameters changed from `memory` to `calldata` for gas efficiency.
|
|
|
117
117
|
|
|
118
118
|
| Function | Description |
|
|
119
119
|
|----------|-------------|
|
|
120
|
-
| `currentTotalReclaimableSurplusOf(uint256 projectId, uint256 cashOutCount, uint256 decimals, uint256 currency)` | Convenience view that returns the reclaimable surplus across all terminals using all
|
|
120
|
+
| `currentTotalReclaimableSurplusOf(uint256 projectId, uint256 cashOutCount, uint256 decimals, uint256 currency)` | Convenience view that returns the reclaimable surplus across all terminals using all tokens. Delegates to `currentReclaimableSurplusOf` with empty `terminals` and `tokens` arrays. Mirrors the `currentTotalSurplusOf` pattern. |
|
|
121
121
|
|
|
122
122
|
#### IJBTokens / JBTokens
|
|
123
123
|
|
package/RISKS.md
CHANGED
|
@@ -39,7 +39,7 @@ What must be true for the system to remain safe:
|
|
|
39
39
|
|
|
40
40
|
### Surplus Manipulation
|
|
41
41
|
|
|
42
|
-
- **Cross-terminal surplus aggregation.** When `useTotalSurplusForCashOuts` is enabled, `recordCashOutFor` aggregates surplus across all terminals via `JBSurplus.currentSurplusOf()`, which calls `terminal.currentSurplusOf()` on each. If a malicious terminal is added to the project's directory, it could report inflated surplus. Defense: `InadequateTerminalStoreBalance` revert prevents extracting more than the actual terminal balance.
|
|
42
|
+
- **Cross-terminal surplus aggregation.** When `useTotalSurplusForCashOuts` is enabled, `recordCashOutFor` aggregates surplus across all terminals via `JBSurplus.currentSurplusOf()`, which calls `terminal.currentSurplusOf()` on each. If a malicious terminal is added to the project's directory, it could report inflated surplus. Defense: `InadequateTerminalStoreBalance` revert prevents extracting more than the actual terminal balance. When `useTotalSurplusForCashOuts` is false, only the single token being reclaimed contributes to the surplus calculation.
|
|
43
43
|
- **Price feed inconsistency across terminals.** Different tokens in different terminals are converted to a common currency via `JBPrices`. If price feeds between terminals are stale or inconsistent, aggregated surplus can be inflated/deflated, affecting cash out reclaim amounts.
|
|
44
44
|
|
|
45
45
|
## 3. Reentrancy Surface
|
|
@@ -129,7 +129,7 @@ No `ReentrancyGuard` is used. The system relies on state ordering and the `Inade
|
|
|
129
129
|
`JBMultiTerminal.previewPayFor`, `JBMultiTerminal.previewCashOutFrom`, and `JBController.previewMintOf` are `view` functions that simulate operations without modifying state. They compose the same computation paths as the real operations.
|
|
130
130
|
|
|
131
131
|
- **Data hooks are called during previews.** `previewPayFor` and `previewCashOutFrom` invoke `beforePayRecordedWith` and `beforeCashOutRecordedWith` on data hooks. A reverting data hook causes the preview to revert. A gas-consuming hook can cause the preview to run out of gas.
|
|
132
|
-
- **Store previews
|
|
132
|
+
- **Store previews take an explicit terminal parameter.** `JBTerminalStore.previewPayFrom` and `previewCashOutFrom` take an explicit `terminal` address for balance/surplus lookups. Callers must pass a registered terminal to get correct results. The terminal-level functions (`JBMultiTerminal.previewPayFor` / `previewCashOutFrom`) handle this automatically by passing `address(this)`.
|
|
133
133
|
- **No state modification risk.** Preview functions cannot change balances, mint/burn tokens, or consume limits. They are safe to call from any context.
|
|
134
134
|
|
|
135
135
|
## 7. Integration Risks
|
|
@@ -149,7 +149,7 @@ No `ReentrancyGuard` is used. The system relies on state ordering and the `Inade
|
|
|
149
149
|
|
|
150
150
|
### Cross-Terminal Surplus Aggregation
|
|
151
151
|
|
|
152
|
-
- `JBSurplus.currentSurplusOf` calls `terminal.currentSurplusOf()` on each terminal. These are external view calls with no gas limit. A malicious or gas-expensive terminal can cause this aggregation to revert, blocking cash outs for any project that has `useTotalSurplusForCashOuts` enabled and uses that terminal.
|
|
152
|
+
- `JBSurplus.currentSurplusOf` calls `terminal.currentSurplusOf()` on each terminal. These are external view calls with no gas limit. A malicious or gas-expensive terminal can cause this aggregation to revert, blocking cash outs for any project that has `useTotalSurplusForCashOuts` enabled and uses that terminal. When `useTotalSurplusForCashOuts` is false, surplus is computed internally by the store for only the reclaimed token, avoiding cross-terminal external calls.
|
|
153
153
|
- The surplus calculation converts each terminal's balance to a common currency via price feeds. Rounding accumulates across terminals. With N terminals and M tokens each, there are N*M price conversions, each with up to 1 wei of rounding error.
|
|
154
154
|
|
|
155
155
|
### `recordAddedBalanceFor` Access Control
|
package/SKILLS.md
CHANGED
|
@@ -66,7 +66,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
66
66
|
| `migrateBalanceOf(uint256 projectId, address token, IJBTerminal to)` | Migrates a project's token balance to another terminal. Requires `allowTerminalMigration`. |
|
|
67
67
|
| `processHeldFeesOf(uint256 projectId, address token, uint256 count)` | Processes up to `count` held fees for a project, sending them to the fee beneficiary project. |
|
|
68
68
|
| `addAccountingContextsFor(uint256 projectId, JBAccountingContext[] accountingContexts)` | Adds new accounting contexts (token types) to a terminal for a project. |
|
|
69
|
-
| `currentSurplusOf(uint256 projectId,
|
|
69
|
+
| `currentSurplusOf(uint256 projectId, address[] tokens, uint256 decimals, uint256 currency)` | Returns the project's current surplus in this terminal. Empty `tokens` = all tokens. |
|
|
70
70
|
| `accountingContextForTokenOf(uint256 projectId, address token)` | Returns the accounting context for a specific token. |
|
|
71
71
|
| `accountingContextsOf(uint256 projectId)` | Returns all accounting contexts for a project. |
|
|
72
72
|
| `previewPayFor(uint256 projectId, address token, uint256 amount, address beneficiary, bytes metadata)` | Simulates a full payment including the reserved/beneficiary token split. Returns `(ruleset, beneficiaryTokenCount, reservedTokenCount, hookSpecifications)`. Composes `STORE.previewPayFrom` + `controller.previewMintOf`. |
|
|
@@ -87,12 +87,12 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
87
87
|
| `usedPayoutLimitOf(address terminal, uint256 projectId, address token, uint256 rulesetCycleNumber, uint256 currency)` | Returns the used payout limit for a project in a given cycle. |
|
|
88
88
|
| `usedSurplusAllowanceOf(address terminal, uint256 projectId, address token, uint256 rulesetId, uint256 currency)` | Returns the used surplus allowance for a project in a given ruleset. |
|
|
89
89
|
| `currentReclaimableSurplusOf(uint256 projectId, uint256 cashOutCount, uint256 totalSupply, uint256 surplus)` | Returns the reclaimable surplus given raw total supply and surplus values. Applies bonding curve from current ruleset. |
|
|
90
|
-
| `currentReclaimableSurplusOf(uint256 projectId, uint256 cashOutCount, IJBTerminal[] terminals,
|
|
91
|
-
| `currentTotalReclaimableSurplusOf(uint256 projectId, uint256 cashOutCount, uint256 decimals, uint256 currency)` | Convenience view: reclaimable surplus across all terminals
|
|
92
|
-
| `currentSurplusOf(
|
|
93
|
-
| `currentTotalSurplusOf(uint256 projectId, uint256 decimals, uint256 currency)` |
|
|
94
|
-
| `previewPayFrom(address payer, JBTokenAmount amount, uint256 projectId, address beneficiary, bytes metadata)` | Simulates a payment without modifying state. Uses `
|
|
95
|
-
| `previewCashOutFrom(address holder, uint256 projectId, uint256 cashOutCount,
|
|
90
|
+
| `currentReclaimableSurplusOf(uint256 projectId, uint256 cashOutCount, IJBTerminal[] terminals, address[] tokens, uint256 decimals, uint256 currency)` | Returns the reclaimable surplus across specified terminals and tokens. Empty arrays default to all. |
|
|
91
|
+
| `currentTotalReclaimableSurplusOf(uint256 projectId, uint256 cashOutCount, uint256 decimals, uint256 currency)` | Convenience view: reclaimable surplus across all terminals and all tokens. |
|
|
92
|
+
| `currentSurplusOf(uint256 projectId, IJBTerminal[] terminals, address[] tokens, uint256 decimals, uint256 currency)` | Returns the current surplus across specified terminals and tokens. Empty arrays default to all. |
|
|
93
|
+
| `currentTotalSurplusOf(uint256 projectId, uint256 decimals, uint256 currency)` | Convenience view: total surplus across all terminals and all tokens. |
|
|
94
|
+
| `previewPayFrom(address terminal, address payer, JBTokenAmount amount, uint256 projectId, address beneficiary, bytes metadata)` | Simulates a payment without modifying state. Uses the explicit `terminal` parameter for balance/surplus lookups. Invokes data hooks if configured. Returns ruleset, token count, and hook specifications. |
|
|
95
|
+
| `previewCashOutFrom(address terminal, address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, bool beneficiaryIsFeeless, bytes metadata)` | Simulates a cash out without modifying state. Uses the explicit `terminal` parameter for balance/surplus lookups. Invokes data hooks if configured. Returns ruleset, reclaim amount, tax rate, and hook specifications. |
|
|
96
96
|
|
|
97
97
|
### JBRulesets
|
|
98
98
|
|
|
@@ -246,7 +246,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
246
246
|
- Project #1 is the fee beneficiary project (receives all protocol fees)
|
|
247
247
|
- **Fee-free cashout exemption is scoped to fee-free intra-terminal payout amounts.** `_feeFreeSurplusOf[projectId][token]` accumulates the value of fee-free payouts. During cashout with `cashOutTaxRate=0`, the 2.5% fee applies only up to this surplus, then depletes. Once consumed, subsequent cashouts are fee-free again. This prevents a round-trip fee bypass (intra-terminal payout → zero-tax cashout) while scoping fees precisely to the fee-free inflow.
|
|
248
248
|
- `JBProjects` constructor optionally mints project #1 to `feeProjectOwner` -- if `address(0)`, no fee project is created
|
|
249
|
-
- `JBMultiTerminal` derives `DIRECTORY`
|
|
249
|
+
- `JBMultiTerminal` derives `DIRECTORY` from the provided `store` in its constructor -- not passed directly
|
|
250
250
|
- `JBPrices.pricePerUnitOf()` checks project-specific feed, then inverse, then falls back to `DEFAULT_PROJECT_ID = 0`
|
|
251
251
|
- `useAllowanceOf()` takes 8 args including `address payable feeBeneficiary` -- do NOT omit it
|
|
252
252
|
- Cash out tax rate of 0% = proportional (1:1) redemption; 100% = nothing reclaimable (all surplus locked). Do NOT confuse with a "cash out rate" where 100% means full redemption.
|
package/USER_JOURNEYS.md
CHANGED
|
@@ -69,7 +69,7 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
69
69
|
- Data hook can return empty weight (0) to suppress minting while still recording payment
|
|
70
70
|
- Fee-on-transfer tokens: actual amount received is `_balanceOf(token) - balanceBefore` (measured via balance diff)
|
|
71
71
|
|
|
72
|
-
**Preview**: Call `JBMultiTerminal.previewPayFor(projectId, token, amount, beneficiary, metadata)` to simulate the full payment on-chain -- including data hook effects and the reserved/beneficiary token split. Returns `(ruleset, beneficiaryTokenCount, reservedTokenCount, hookSpecifications)`. This is a `view` function that does not modify state. For lower-level access without the mint split, the terminal delegates to `JBTerminalStore.previewPayFrom(payer, amount, projectId, beneficiary, metadata)` which returns the raw `(ruleset, tokenCount, hookSpecifications)`.
|
|
72
|
+
**Preview**: Call `JBMultiTerminal.previewPayFor(projectId, token, amount, beneficiary, metadata)` to simulate the full payment on-chain -- including data hook effects and the reserved/beneficiary token split. Returns `(ruleset, beneficiaryTokenCount, reservedTokenCount, hookSpecifications)`. This is a `view` function that does not modify state. For lower-level access without the mint split, the terminal delegates to `JBTerminalStore.previewPayFrom(terminal, payer, amount, projectId, beneficiary, metadata)` which returns the raw `(ruleset, tokenCount, hookSpecifications)`.
|
|
73
73
|
|
|
74
74
|
---
|
|
75
75
|
|
package/foundry.toml
CHANGED
package/package.json
CHANGED
package/src/JBMultiTerminal.sol
CHANGED
|
@@ -4,12 +4,10 @@ pragma solidity 0.8.26;
|
|
|
4
4
|
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
5
5
|
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
|
|
6
6
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
7
|
-
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
8
7
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
9
8
|
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
10
9
|
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
|
|
11
10
|
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
12
|
-
import {mulDiv} from "@prb/math/src/Common.sol";
|
|
13
11
|
import {IAllowanceTransfer} from "@uniswap/permit2/src/interfaces/IAllowanceTransfer.sol";
|
|
14
12
|
import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
|
|
15
13
|
|
|
@@ -25,7 +23,6 @@ import {IJBPermissioned} from "./interfaces/IJBPermissioned.sol";
|
|
|
25
23
|
import {IJBPermissions} from "./interfaces/IJBPermissions.sol";
|
|
26
24
|
import {IJBPermitTerminal} from "./interfaces/IJBPermitTerminal.sol";
|
|
27
25
|
import {IJBProjects} from "./interfaces/IJBProjects.sol";
|
|
28
|
-
import {IJBRulesets} from "./interfaces/IJBRulesets.sol";
|
|
29
26
|
import {IJBSplitHook} from "./interfaces/IJBSplitHook.sol";
|
|
30
27
|
import {IJBSplits} from "./interfaces/IJBSplits.sol";
|
|
31
28
|
import {IJBTerminal} from "./interfaces/IJBTerminal.sol";
|
|
@@ -61,9 +58,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
61
58
|
// --------------------------- custom errors ------------------------- //
|
|
62
59
|
//*********************************************************************//
|
|
63
60
|
|
|
64
|
-
error JBMultiTerminal_AccountingContextAlreadySet(address token);
|
|
65
|
-
error JBMultiTerminal_AccountingContextDecimalsMismatch();
|
|
66
|
-
error JBMultiTerminal_AddingAccountingContextNotAllowed();
|
|
67
61
|
error JBMultiTerminal_FeeTerminalNotFound(address token);
|
|
68
62
|
error JBMultiTerminal_NoMsgValueAllowed(uint256 value);
|
|
69
63
|
error JBMultiTerminal_OverflowAlert(uint256 value, uint256 limit);
|
|
@@ -75,7 +69,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
75
69
|
error JBMultiTerminal_UnderMinReturnedTokens(uint256 count, uint256 min);
|
|
76
70
|
error JBMultiTerminal_UnderMinTokensPaidOut(uint256 amount, uint256 min);
|
|
77
71
|
error JBMultiTerminal_UnderMinTokensReclaimed(uint256 amount, uint256 min);
|
|
78
|
-
error JBMultiTerminal_ZeroAccountingContextCurrency();
|
|
79
72
|
|
|
80
73
|
//*********************************************************************//
|
|
81
74
|
// ------------------------- public constants ------------------------ //
|
|
@@ -112,9 +105,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
112
105
|
/// @notice Mints ERC-721s that represent project ownership and transfers.
|
|
113
106
|
IJBProjects public immutable override PROJECTS;
|
|
114
107
|
|
|
115
|
-
/// @notice The contract storing and managing project rulesets.
|
|
116
|
-
IJBRulesets public immutable override RULESETS;
|
|
117
|
-
|
|
118
108
|
/// @notice The contract that stores splits for each project.
|
|
119
109
|
IJBSplits public immutable override SPLITS;
|
|
120
110
|
|
|
@@ -128,15 +118,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
128
118
|
// --------------------- internal stored properties ------------------ //
|
|
129
119
|
//*********************************************************************//
|
|
130
120
|
|
|
131
|
-
/// @notice Context describing how a token is accounted for by a project.
|
|
132
|
-
/// @custom:param projectId The ID of the project that the token accounting context applies to.
|
|
133
|
-
/// @custom:param token The address of the token being accounted for.
|
|
134
|
-
mapping(uint256 projectId => mapping(address token => JBAccountingContext)) internal _accountingContextForTokenOf;
|
|
135
|
-
|
|
136
|
-
/// @notice A list of tokens accepted by each project.
|
|
137
|
-
/// @custom:param projectId The ID of the project to get a list of accepted tokens for.
|
|
138
|
-
mapping(uint256 projectId => JBAccountingContext[]) internal _accountingContextsOf;
|
|
139
|
-
|
|
140
121
|
/// @notice The cumulative amount of fee-free intra-terminal payouts a project has received for a given token.
|
|
141
122
|
/// @dev Incremented each time a fee-free payout lands (same terminal, no fee charged). During cashout with
|
|
142
123
|
/// `cashOutTaxRate == 0`, fees are applied only up to this amount, then decremented. This prevents a round-trip
|
|
@@ -189,7 +170,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
189
170
|
DIRECTORY = store.DIRECTORY();
|
|
190
171
|
FEELESS_ADDRESSES = feelessAddresses;
|
|
191
172
|
PROJECTS = projects;
|
|
192
|
-
RULESETS = store.RULESETS();
|
|
193
173
|
SPLITS = splits;
|
|
194
174
|
STORE = store;
|
|
195
175
|
TOKENS = tokens;
|
|
@@ -221,66 +201,12 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
221
201
|
alsoGrantAccessIf: _msgSender() == address(_controllerOf(projectId))
|
|
222
202
|
});
|
|
223
203
|
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// Make sure that if there's a ruleset, it allows adding accounting contexts.
|
|
228
|
-
if (ruleset.id != 0 && !ruleset.allowAddAccountingContext()) {
|
|
229
|
-
revert JBMultiTerminal_AddingAccountingContextNotAllowed();
|
|
230
|
-
}
|
|
204
|
+
// Record all accounting contexts in the store (validates each and reverts if invalid).
|
|
205
|
+
STORE.recordAccountingContextOf({projectId: projectId, contexts: accountingContexts});
|
|
231
206
|
|
|
232
|
-
//
|
|
207
|
+
// Emit an event for each accounting context.
|
|
233
208
|
for (uint256 i; i < accountingContexts.length; i++) {
|
|
234
|
-
|
|
235
|
-
JBAccountingContext memory accountingContext = accountingContexts[i];
|
|
236
|
-
|
|
237
|
-
// Get a storage reference to the currency accounting context for the token.
|
|
238
|
-
JBAccountingContext storage storedAccountingContext =
|
|
239
|
-
_accountingContextForTokenOf[projectId][accountingContext.token];
|
|
240
|
-
|
|
241
|
-
// Make sure the token accounting context isn't already set.
|
|
242
|
-
if (storedAccountingContext.token != address(0)) {
|
|
243
|
-
revert JBMultiTerminal_AccountingContextAlreadySet(storedAccountingContext.token);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Keep track of a flag indiciating if we know the provided decimals are incorrect.
|
|
247
|
-
bool knownInvalidDecimals;
|
|
248
|
-
|
|
249
|
-
// Check if the token is the native token and has the correct decimals
|
|
250
|
-
if (accountingContext.token == JBConstants.NATIVE_TOKEN && accountingContext.decimals != 18) {
|
|
251
|
-
knownInvalidDecimals = true;
|
|
252
|
-
} else if (accountingContext.token != JBConstants.NATIVE_TOKEN) {
|
|
253
|
-
// slither-disable-next-line calls-loop
|
|
254
|
-
try IERC20Metadata(accountingContext.token).decimals() returns (uint8 decimals) {
|
|
255
|
-
// slither-disable-next-line calls-loop
|
|
256
|
-
if (accountingContext.decimals != decimals) {
|
|
257
|
-
knownInvalidDecimals = true;
|
|
258
|
-
}
|
|
259
|
-
} catch {
|
|
260
|
-
// The token didn't support `decimals`.
|
|
261
|
-
// @dev Non-standard ERC20s that revert on `decimals()` will bypass decimal validation.
|
|
262
|
-
// The caller is responsible for providing the correct decimals for such tokens.
|
|
263
|
-
knownInvalidDecimals = false;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Make sure the decimals are correct.
|
|
268
|
-
if (knownInvalidDecimals) {
|
|
269
|
-
revert JBMultiTerminal_AccountingContextDecimalsMismatch();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Make sure the currency is non-zero.
|
|
273
|
-
if (accountingContext.currency == 0) revert JBMultiTerminal_ZeroAccountingContextCurrency();
|
|
274
|
-
|
|
275
|
-
// Define the context from the config.
|
|
276
|
-
storedAccountingContext.token = accountingContext.token;
|
|
277
|
-
storedAccountingContext.decimals = accountingContext.decimals;
|
|
278
|
-
storedAccountingContext.currency = accountingContext.currency;
|
|
279
|
-
|
|
280
|
-
// Add the token to the list of accepted tokens of the project.
|
|
281
|
-
_accountingContextsOf[projectId].push(storedAccountingContext);
|
|
282
|
-
|
|
283
|
-
emit SetAccountingContext({projectId: projectId, context: storedAccountingContext, caller: _msgSender()});
|
|
209
|
+
emit SetAccountingContext({projectId: projectId, context: accountingContexts[i], caller: _msgSender()});
|
|
284
210
|
}
|
|
285
211
|
}
|
|
286
212
|
|
|
@@ -406,7 +332,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
406
332
|
JBSplitHookContext memory context = JBSplitHookContext({
|
|
407
333
|
token: token,
|
|
408
334
|
amount: netPayoutAmount,
|
|
409
|
-
decimals:
|
|
335
|
+
decimals: STORE.accountingContextOf({terminal: address(this), projectId: projectId, token: token})
|
|
336
|
+
.decimals,
|
|
410
337
|
projectId: projectId,
|
|
411
338
|
groupId: uint256(uint160(token)),
|
|
412
339
|
split: split
|
|
@@ -814,29 +741,27 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
814
741
|
override
|
|
815
742
|
returns (JBAccountingContext memory)
|
|
816
743
|
{
|
|
817
|
-
return
|
|
744
|
+
return STORE.accountingContextOf({terminal: address(this), projectId: projectId, token: token});
|
|
818
745
|
}
|
|
819
746
|
|
|
820
747
|
/// @notice The tokens accepted by a project.
|
|
821
748
|
/// @param projectId The ID of the project to get the accepted tokens of.
|
|
822
749
|
/// @return tokenContexts The accounting contexts of the accepted tokens.
|
|
823
750
|
function accountingContextsOf(uint256 projectId) external view override returns (JBAccountingContext[] memory) {
|
|
824
|
-
return
|
|
751
|
+
return STORE.accountingContextsOf({terminal: address(this), projectId: projectId});
|
|
825
752
|
}
|
|
826
753
|
|
|
827
|
-
/// @notice Gets the
|
|
828
|
-
/// @dev
|
|
829
|
-
///
|
|
830
|
-
/// @param
|
|
831
|
-
/// @param accountingContexts The accounting contexts to use to calculate the surplus. Pass an empty array to use
|
|
832
|
-
/// all of the project's accounting contexts.
|
|
754
|
+
/// @notice Gets the current surplus amount in this terminal for a project, in terms of a given currency.
|
|
755
|
+
/// @dev If `tokens` is empty, includes all tokens the project accepts (as returned by `accountingContextsOf(...)`).
|
|
756
|
+
/// @param projectId The ID of the project to get the current surplus of.
|
|
757
|
+
/// @param tokens The tokens to include in the surplus calculation. If empty, all tokens are included.
|
|
833
758
|
/// @param decimals The number of decimals to include in the fixed point returned value.
|
|
834
759
|
/// @param currency The currency to express the returned value in terms of.
|
|
835
760
|
/// @return The current surplus amount the project has in this terminal, in terms of `currency` and with the
|
|
836
761
|
/// specified number of decimals.
|
|
837
762
|
function currentSurplusOf(
|
|
838
763
|
uint256 projectId,
|
|
839
|
-
|
|
764
|
+
address[] calldata tokens,
|
|
840
765
|
uint256 decimals,
|
|
841
766
|
uint256 currency
|
|
842
767
|
)
|
|
@@ -845,12 +770,10 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
845
770
|
override
|
|
846
771
|
returns (uint256)
|
|
847
772
|
{
|
|
773
|
+
IJBTerminal[] memory self = new IJBTerminal[](1);
|
|
774
|
+
self[0] = IJBTerminal(address(this));
|
|
848
775
|
return STORE.currentSurplusOf({
|
|
849
|
-
|
|
850
|
-
projectId: projectId,
|
|
851
|
-
accountingContexts: accountingContexts.length != 0 ? accountingContexts : _accountingContextsOf[projectId],
|
|
852
|
-
decimals: decimals,
|
|
853
|
-
currency: currency
|
|
776
|
+
projectId: projectId, terminals: self, tokens: tokens, decimals: decimals, currency: currency
|
|
854
777
|
});
|
|
855
778
|
}
|
|
856
779
|
|
|
@@ -859,6 +782,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
859
782
|
/// @dev Held fees can be processed at any time by this terminal's owner.
|
|
860
783
|
/// @param projectId The ID of the project that is holding fees.
|
|
861
784
|
/// @param token The token that the fees are held in.
|
|
785
|
+
/// @param count The maximum number of held fees to return.
|
|
786
|
+
/// @return heldFees The held fees.
|
|
862
787
|
function heldFeesOf(
|
|
863
788
|
uint256 projectId,
|
|
864
789
|
address token,
|
|
@@ -919,22 +844,16 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
919
844
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
920
845
|
)
|
|
921
846
|
{
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
cashOutCount: cashOutCount,
|
|
933
|
-
accountingContext: accountingContext,
|
|
934
|
-
balanceAccountingContexts: balanceAccountingContexts,
|
|
935
|
-
beneficiaryIsFeeless: _isFeeless(beneficiary),
|
|
936
|
-
metadata: metadata
|
|
937
|
-
});
|
|
847
|
+
(ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications) =
|
|
848
|
+
STORE.previewCashOutFrom({
|
|
849
|
+
terminal: address(this),
|
|
850
|
+
holder: holder,
|
|
851
|
+
projectId: projectId,
|
|
852
|
+
cashOutCount: cashOutCount,
|
|
853
|
+
tokenToReclaim: tokenToReclaim,
|
|
854
|
+
beneficiaryIsFeeless: _isFeeless(beneficiary),
|
|
855
|
+
metadata: metadata
|
|
856
|
+
});
|
|
938
857
|
}
|
|
939
858
|
|
|
940
859
|
/// @notice Simulates paying a project through this terminal without modifying state.
|
|
@@ -969,6 +888,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
969
888
|
|
|
970
889
|
// Preview the payment through the store.
|
|
971
890
|
(ruleset, tokenCount, hookSpecifications) = STORE.previewPayFrom({
|
|
891
|
+
terminal: address(this),
|
|
972
892
|
payer: _msgSender(),
|
|
973
893
|
amount: _tokenAmountOf({projectId: projectId, token: token, value: amount}),
|
|
974
894
|
projectId: projectId,
|
|
@@ -1150,25 +1070,15 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1150
1070
|
// Keep a reference to the cash out tax rate being used.
|
|
1151
1071
|
uint256 cashOutTaxRate;
|
|
1152
1072
|
|
|
1153
|
-
//
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
(ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications) = STORE.recordCashOutFor({
|
|
1163
|
-
holder: holder,
|
|
1164
|
-
projectId: projectId,
|
|
1165
|
-
accountingContext: accountingContext,
|
|
1166
|
-
balanceAccountingContexts: balanceAccountingContexts,
|
|
1167
|
-
cashOutCount: cashOutCount,
|
|
1168
|
-
beneficiaryIsFeeless: _isFeeless(beneficiary),
|
|
1169
|
-
metadata: metadata
|
|
1170
|
-
});
|
|
1171
|
-
}
|
|
1073
|
+
// Record the cash out.
|
|
1074
|
+
(ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications) = STORE.recordCashOutFor({
|
|
1075
|
+
holder: holder,
|
|
1076
|
+
projectId: projectId,
|
|
1077
|
+
cashOutCount: cashOutCount,
|
|
1078
|
+
tokenToReclaim: tokenToReclaim,
|
|
1079
|
+
beneficiaryIsFeeless: _isFeeless(beneficiary),
|
|
1080
|
+
metadata: metadata
|
|
1081
|
+
});
|
|
1172
1082
|
|
|
1173
1083
|
// Burn the project tokens.
|
|
1174
1084
|
if (cashOutCount != 0) {
|
|
@@ -1215,11 +1125,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1215
1125
|
ruleset: ruleset,
|
|
1216
1126
|
cashOutTaxRate: cashOutTaxRate,
|
|
1217
1127
|
beneficiary: beneficiary,
|
|
1218
|
-
beneficiaryReclaimAmount:
|
|
1219
|
-
token: tokenToReclaim,
|
|
1220
|
-
decimals: accountingContext.decimals,
|
|
1221
|
-
currency: accountingContext.currency,
|
|
1222
|
-
value: reclaimAmount
|
|
1128
|
+
beneficiaryReclaimAmount: _tokenAmountOf({
|
|
1129
|
+
projectId: projectId, token: tokenToReclaim, value: reclaimAmount
|
|
1223
1130
|
}),
|
|
1224
1131
|
specifications: hookSpecifications,
|
|
1225
1132
|
metadata: metadata
|
|
@@ -1251,6 +1158,52 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1251
1158
|
});
|
|
1252
1159
|
}
|
|
1253
1160
|
|
|
1161
|
+
/// @notice Fund a project either by calling this terminal's internal `addToBalance` function or by calling the
|
|
1162
|
+
/// recipient terminal's `addToBalance` function.
|
|
1163
|
+
/// @param terminal The terminal on which the project is expecting to receive funds.
|
|
1164
|
+
/// @param projectId The ID of the project being funded.
|
|
1165
|
+
/// @param token The token being used.
|
|
1166
|
+
/// @param amount The amount being funded, as a fixed point number with the amount of decimals that the terminal's
|
|
1167
|
+
/// accounting context specifies.
|
|
1168
|
+
/// @param metadata Additional metadata to include with the payment.
|
|
1169
|
+
function _efficientAddToBalance(
|
|
1170
|
+
IJBTerminal terminal,
|
|
1171
|
+
uint256 projectId,
|
|
1172
|
+
address token,
|
|
1173
|
+
uint256 amount,
|
|
1174
|
+
bytes memory metadata
|
|
1175
|
+
)
|
|
1176
|
+
internal
|
|
1177
|
+
{
|
|
1178
|
+
// Call the internal method if this terminal is being used.
|
|
1179
|
+
if (terminal == IJBTerminal(address(this))) {
|
|
1180
|
+
_addToBalanceOf({
|
|
1181
|
+
projectId: projectId,
|
|
1182
|
+
token: token,
|
|
1183
|
+
amount: amount,
|
|
1184
|
+
shouldReturnHeldFees: false,
|
|
1185
|
+
memo: "",
|
|
1186
|
+
metadata: metadata
|
|
1187
|
+
});
|
|
1188
|
+
} else {
|
|
1189
|
+
// Trigger any inherited pre-transfer logic.
|
|
1190
|
+
// Keep a reference to the amount that'll be paid as a `msg.value`.
|
|
1191
|
+
// slither-disable-next-line reentrancy-events
|
|
1192
|
+
uint256 payValue = _beforeTransferTo({to: address(terminal), token: token, amount: amount});
|
|
1193
|
+
|
|
1194
|
+
// Add to balance.
|
|
1195
|
+
// If this terminal's token is the native token, send it in `msg.value`.
|
|
1196
|
+
terminal.addToBalanceOf{value: payValue}({
|
|
1197
|
+
projectId: projectId,
|
|
1198
|
+
token: token,
|
|
1199
|
+
amount: amount,
|
|
1200
|
+
shouldReturnHeldFees: false,
|
|
1201
|
+
memo: "",
|
|
1202
|
+
metadata: metadata
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1254
1207
|
/// @notice Pay a project either by calling this terminal's internal `pay` function or by calling the recipient
|
|
1255
1208
|
/// terminal's `pay` function.
|
|
1256
1209
|
/// @param terminal The terminal on which the project is expecting to receive payments.
|
|
@@ -1588,52 +1541,6 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1588
1541
|
}
|
|
1589
1542
|
}
|
|
1590
1543
|
|
|
1591
|
-
/// @notice Fund a project either by calling this terminal's internal `addToBalance` function or by calling the
|
|
1592
|
-
/// recipient terminal's `addToBalance` function.
|
|
1593
|
-
/// @param terminal The terminal on which the project is expecting to receive funds.
|
|
1594
|
-
/// @param projectId The ID of the project being funded.
|
|
1595
|
-
/// @param token The token being used.
|
|
1596
|
-
/// @param amount The amount being funded, as a fixed point number with the amount of decimals that the terminal's
|
|
1597
|
-
/// accounting context specifies.
|
|
1598
|
-
/// @param metadata Additional metadata to include with the payment.
|
|
1599
|
-
function _efficientAddToBalance(
|
|
1600
|
-
IJBTerminal terminal,
|
|
1601
|
-
uint256 projectId,
|
|
1602
|
-
address token,
|
|
1603
|
-
uint256 amount,
|
|
1604
|
-
bytes memory metadata
|
|
1605
|
-
)
|
|
1606
|
-
internal
|
|
1607
|
-
{
|
|
1608
|
-
// Call the internal method if this terminal is being used.
|
|
1609
|
-
if (terminal == IJBTerminal(address(this))) {
|
|
1610
|
-
_addToBalanceOf({
|
|
1611
|
-
projectId: projectId,
|
|
1612
|
-
token: token,
|
|
1613
|
-
amount: amount,
|
|
1614
|
-
shouldReturnHeldFees: false,
|
|
1615
|
-
memo: "",
|
|
1616
|
-
metadata: metadata
|
|
1617
|
-
});
|
|
1618
|
-
} else {
|
|
1619
|
-
// Trigger any inherited pre-transfer logic.
|
|
1620
|
-
// Keep a reference to the amount that'll be paid as a `msg.value`.
|
|
1621
|
-
// slither-disable-next-line reentrancy-events
|
|
1622
|
-
uint256 payValue = _beforeTransferTo({to: address(terminal), token: token, amount: amount});
|
|
1623
|
-
|
|
1624
|
-
// Add to balance.
|
|
1625
|
-
// If this terminal's token is the native token, send it in `msg.value`.
|
|
1626
|
-
terminal.addToBalanceOf{value: payValue}({
|
|
1627
|
-
projectId: projectId,
|
|
1628
|
-
token: token,
|
|
1629
|
-
amount: amount,
|
|
1630
|
-
shouldReturnHeldFees: false,
|
|
1631
|
-
memo: "",
|
|
1632
|
-
metadata: metadata
|
|
1633
|
-
});
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
1544
|
/// @notice Records an added balance for a project.
|
|
1638
1545
|
/// @param projectId The ID of the project to record the added balance for.
|
|
1639
1546
|
/// @param token The token to record the added balance for.
|
|
@@ -1721,6 +1628,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1721
1628
|
});
|
|
1722
1629
|
}
|
|
1723
1630
|
|
|
1631
|
+
/// @notice Sends payouts to a project's payout split group using the specified ruleset.
|
|
1724
1632
|
/// @param projectId The ID of the project to send the payouts of.
|
|
1725
1633
|
/// @param token The token being paid out.
|
|
1726
1634
|
/// @param amount The number of terminal tokens to pay out, as a fixed point number with same number of decimals as
|
|
@@ -1741,12 +1649,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1741
1649
|
JBRuleset memory ruleset;
|
|
1742
1650
|
|
|
1743
1651
|
// Record the payout.
|
|
1744
|
-
(ruleset, amountPaidOut) =
|
|
1745
|
-
projectId: projectId,
|
|
1746
|
-
accountingContext: _accountingContextForTokenOf[projectId][token],
|
|
1747
|
-
amount: amount,
|
|
1748
|
-
currency: currency
|
|
1749
|
-
});
|
|
1652
|
+
(ruleset, amountPaidOut) =
|
|
1653
|
+
STORE.recordPayoutFor({projectId: projectId, token: token, amount: amount, currency: currency});
|
|
1750
1654
|
|
|
1751
1655
|
// Get a reference to the project's owner.
|
|
1752
1656
|
// The owner will receive tokens minted by paying the platform fee and receive any leftover funds not sent to
|
|
@@ -1943,12 +1847,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1943
1847
|
uint256 amountPaidOut;
|
|
1944
1848
|
|
|
1945
1849
|
// Record the use of the allowance.
|
|
1946
|
-
(ruleset, amountPaidOut) =
|
|
1947
|
-
projectId: projectId,
|
|
1948
|
-
accountingContext: _accountingContextForTokenOf[projectId][token],
|
|
1949
|
-
amount: amount,
|
|
1950
|
-
currency: currency
|
|
1951
|
-
});
|
|
1850
|
+
(ruleset, amountPaidOut) =
|
|
1851
|
+
STORE.recordUsedAllowanceOf({projectId: projectId, token: token, amount: amount, currency: currency});
|
|
1952
1852
|
|
|
1953
1853
|
// Take a fee from the `amountPaidOut`, if needed.
|
|
1954
1854
|
// The net amount is the final amount withdrawn after the fee has been taken.
|
|
@@ -2001,7 +1901,7 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
2001
1901
|
returns (JBAccountingContext memory context)
|
|
2002
1902
|
{
|
|
2003
1903
|
// Keep a reference to the accounting context configured for the token.
|
|
2004
|
-
context =
|
|
1904
|
+
context = STORE.accountingContextOf({terminal: address(this), projectId: projectId, token: token});
|
|
2005
1905
|
|
|
2006
1906
|
// Revert if the token is not accepted by the project.
|
|
2007
1907
|
if (context.token == address(0)) revert JBMultiTerminal_TokenNotAccepted(token);
|