@bananapus/core-v6 0.0.64 → 0.0.66
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/README.md +3 -1
- package/package.json +1 -1
- package/references/entrypoints.md +20 -8
- package/references/types-errors-events.md +13 -7
- package/script/Deploy.s.sol +2 -2
- package/script/DeployPeriphery.s.sol +20 -24
- package/src/JBController.sol +2 -3
- package/src/JBFundAccessLimits.sol +4 -4
- package/src/JBMultiTerminal.sol +19 -25
- package/src/JBTerminalStore.sol +24 -9
- package/src/interfaces/IJBCashOutTerminal.sol +1 -1
- package/src/interfaces/IJBMultiTerminal.sol +2 -2
- package/src/interfaces/IJBPayoutTerminal.sol +2 -2
- package/src/interfaces/IJBTerminalStore.sol +2 -3
- package/src/libraries/JBPayoutSplitGroupLib.sol +4 -1
- package/src/libraries/JBRulesetMetadataResolver.sol +6 -6
- package/src/structs/JBFee.sol +3 -3
- package/src/structs/JBFundAccessLimitGroup.sol +6 -6
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ This package provides:
|
|
|
21
21
|
- token issuance, ruleset queueing, token setup, and splits through `JBController`
|
|
22
22
|
- multi-token terminal accounting through `JBMultiTerminal` and `JBTerminalStore`
|
|
23
23
|
- operator permissions through `JBPermissions`
|
|
24
|
-
- on-chain price-feed routing through `JBPrices`
|
|
24
|
+
- on-chain price-feed routing and fallback feeds through `JBPrices`
|
|
25
25
|
|
|
26
26
|
Use this repo when you need the protocol's canonical accounting and execution logic. Do not copy that logic into downstream repos unless the repo is explicitly meant to wrap or extend core.
|
|
27
27
|
|
|
@@ -72,6 +72,7 @@ The shortest reading path is:
|
|
|
72
72
|
- Data hooks and cash-out hooks can change economics and side effects. They are part of the protocol surface.
|
|
73
73
|
- Permission checks are not always against the project owner. Some flows are scoped to the token holder instead.
|
|
74
74
|
- Preview and execution are intentionally close, but callers should still treat them as separate surfaces when hooks or routing can change behavior.
|
|
75
|
+
- Fee-bearing cash-out, payout, and allowance calls can carry a referral project ID. This credits fee volume; it does not redirect the fee itself.
|
|
75
76
|
|
|
76
77
|
## Where State Lives
|
|
77
78
|
|
|
@@ -79,6 +80,7 @@ The shortest reading path is:
|
|
|
79
80
|
- controller and terminal routing: `JBDirectory`
|
|
80
81
|
- ruleset history and activation: `JBRulesets`
|
|
81
82
|
- balances, surplus, fees, and reclaim accounting: `JBTerminalStore`
|
|
83
|
+
- referral fee-volume accounting: `JBTerminalStore`
|
|
82
84
|
- operator authority: `JBPermissions`
|
|
83
85
|
|
|
84
86
|
When a flow is unclear, read the contract that owns the state before the contract that forwards into it.
|
package/package.json
CHANGED
|
@@ -20,9 +20,9 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
20
20
|
| `JBTokens` | Dual-balance system: credits (internal) + ERC-20. Credits burned first on burn. |
|
|
21
21
|
| `JBSplits` | Split configurations per project/ruleset/group. Packed storage for gas efficiency. |
|
|
22
22
|
| `JBFundAccessLimits` | Payout limits and surplus allowances per project/ruleset/terminal/token. |
|
|
23
|
-
| `JBPrices` |
|
|
23
|
+
| `JBPrices` | Append-only price feed registry with project-specific feeds, protocol defaults, inverse lookup, and backup feeds. |
|
|
24
24
|
| `JBERC20` | Cloneable ERC-20 with Votes + Permit + ERC-1271. Controlled by `JBTokens` via `onlyTokens`. Deployed via `Clones.clone()`. |
|
|
25
|
-
| `JBFeelessAddresses` |
|
|
25
|
+
| `JBFeelessAddresses` | Static and hook-driven fee-exemption registry. |
|
|
26
26
|
| `JBChainlinkV3PriceFeed` | Chainlink AggregatorV3 price feed with staleness threshold. Rejects negative/zero prices, incomplete rounds (`updatedAt == 0`), and stale answers carried from previous rounds (`answeredInRound < roundId`). |
|
|
27
27
|
| `JBChainlinkV3SequencerPriceFeed` | L2 sequencer-aware Chainlink feed (Optimism/Arbitrum) with grace period after restart. Treats any non-zero sequencer answer as down (`answer != 0`). |
|
|
28
28
|
| `JBDeadline` | Approval hook: rejects rulesets queued within `DURATION` seconds of start. Ships as `JBDeadline3Hours`, `JBDeadline1Day`, `JBDeadline3Days`, `JBDeadline7Days`. |
|
|
@@ -61,9 +61,9 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
61
61
|
| Function | What it does |
|
|
62
62
|
|----------|--------------|
|
|
63
63
|
| `pay(uint256 projectId, address token, uint256 amount, address beneficiary, uint256 minReturnedTokens, string memo, bytes metadata)` | Pays a project. Mints project tokens to beneficiary based on ruleset weight. Returns token count. |
|
|
64
|
-
| `cashOutTokensOf(address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, uint256 minTokensReclaimed, address payable beneficiary, bytes metadata)` | Burns project tokens and reclaims surplus terminal tokens via bonding curve. |
|
|
65
|
-
| `sendPayoutsOf(uint256 projectId, address token, uint256 amount, uint256 currency, uint256 minTokensPaidOut)` | Distributes payouts from the project's balance to its payout split group, up to the payout limit. |
|
|
66
|
-
| `useAllowanceOf(uint256 projectId, address token, uint256 amount, uint256 currency, uint256 minTokensPaidOut, address payable beneficiary, address payable feeBeneficiary, string memo)` | Withdraws from the project's surplus allowance to a beneficiary. The `feeBeneficiary` receives tokens minted by the fee payment. |
|
|
64
|
+
| `cashOutTokensOf(address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, uint256 minTokensReclaimed, address payable beneficiary, bytes metadata, uint256 referralProjectId)` | Burns project tokens and reclaims surplus terminal tokens via bonding curve. Optional referral credits protocol fee volume. |
|
|
65
|
+
| `sendPayoutsOf(uint256 projectId, address token, uint256 amount, uint256 currency, uint256 minTokensPaidOut, uint256 referralProjectId)` | Distributes payouts from the project's balance to its payout split group, up to the payout limit. Optional referral credits protocol fee volume. |
|
|
66
|
+
| `useAllowanceOf(uint256 projectId, address token, uint256 amount, uint256 currency, uint256 minTokensPaidOut, address payable beneficiary, address payable feeBeneficiary, string memo, uint256 referralProjectId)` | Withdraws from the project's surplus allowance to a beneficiary. The `feeBeneficiary` receives tokens minted by the fee payment. Optional referral credits protocol fee volume. |
|
|
67
67
|
| `addToBalanceOf(uint256 projectId, address token, uint256 amount, bool shouldReturnHeldFees, string memo, bytes metadata)` | Adds funds to a project's balance without minting tokens. Can unlock held fees. |
|
|
68
68
|
| `migrateBalanceOf(uint256 projectId, address token, IJBTerminal to)` | Migrates a project's token balance to another terminal. Requires `allowTerminalMigration`. |
|
|
69
69
|
| `processHeldFeesOf(uint256 projectId, address token, uint256 count)` | Processes up to `count` held fees for a project, sending them to the fee beneficiary project. |
|
|
@@ -74,6 +74,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
74
74
|
| `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`. |
|
|
75
75
|
| `previewCashOutFrom(address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, address payable beneficiary, bytes metadata)` | Simulates a full cash out including bonding curve and data hook effects. Cash-out data hooks may alter pricing inputs, but not the caller-supplied burn count. Returns `(ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications)`. Delegates to `STORE.previewCashOutFrom`. |
|
|
76
76
|
| `heldFeesOf(uint256 projectId, address token, uint256 count)` | Returns up to `count` held fees for a project/token. |
|
|
77
|
+
| `currentReferralProjectId()` | Returns the in-flight packed referral `(chainId << 48) | projectId`, or 0 outside a fee-bearing call. |
|
|
77
78
|
|
|
78
79
|
### JBTerminalStore
|
|
79
80
|
|
|
@@ -93,8 +94,11 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
93
94
|
| `currentTotalReclaimableSurplusOf(uint256 projectId, uint256 cashOutCount, uint256 decimals, uint256 currency)` | Convenience view: reclaimable surplus across all terminals and all tokens. |
|
|
94
95
|
| `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. |
|
|
95
96
|
| `currentTotalSurplusOf(uint256 projectId, uint256 decimals, uint256 currency)` | Convenience view: total surplus across all terminals and all tokens. |
|
|
97
|
+
| `feeVolumeByReferralOf(address terminal, uint256 referralChainId, uint256 referralProjectId)` | Returns cumulative protocol fee volume credited to a referrer through a terminal. |
|
|
96
98
|
| `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. |
|
|
97
99
|
| `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, including any pricing-only adjustments they return. Returns ruleset, reclaim amount, tax rate, and hook specifications. |
|
|
100
|
+
| `recordFeeReferralCreditOf(uint256 referralProjectId, JBTokenAmount amount)` | Credits normalized fee volume to a packed referral project for the calling terminal. No-ops for zero referral, zero amount, or missing price feed. |
|
|
101
|
+
| `totalFeeVolumeOf(address terminal)` | Returns total referral-creditable fee volume recorded for a terminal. |
|
|
98
102
|
|
|
99
103
|
### JBRulesets
|
|
100
104
|
|
|
@@ -130,8 +134,11 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
130
134
|
|
|
131
135
|
| Function | What it does |
|
|
132
136
|
|----------|--------------|
|
|
133
|
-
| `pricePerUnitOf(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency, uint256 decimals)` | Returns the price of 1 `unitCurrency` in `pricingCurrency`. Checks project
|
|
134
|
-
| `addPriceFeedFor(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency, IJBPriceFeed feed)` |
|
|
137
|
+
| `pricePerUnitOf(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency, uint256 decimals)` | Returns the price of 1 `unitCurrency` in `pricingCurrency`. Checks project direct, project inverse, default direct, then default inverse feeds; skips feeds that revert or return zero. |
|
|
138
|
+
| `addPriceFeedFor(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency, IJBPriceFeed feed)` | Appends a feed for an exact pair. Project ID 0 sets protocol-wide defaults (owner-only); nonzero project IDs are controller-only. |
|
|
139
|
+
| `priceFeedAt(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency, uint256 index)` | Returns the feed at an exact pair's index. |
|
|
140
|
+
| `priceFeedCountFor(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency)` | Returns how many feeds are configured for an exact pair. |
|
|
141
|
+
| `priceFeedFor(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency)` | Returns the primary feed for an exact pair, or zero if none is configured. |
|
|
135
142
|
|
|
136
143
|
### JBTokens
|
|
137
144
|
|
|
@@ -141,6 +148,8 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
141
148
|
| `totalBalanceOf(address holder, uint256 projectId)` | Returns combined credit + ERC-20 balance. |
|
|
142
149
|
| `creditBalanceOf(address holder, uint256 projectId)` | Returns the holder's credit balance. |
|
|
143
150
|
| `tokenOf(uint256 projectId)` | Returns the ERC-20 token for a project (`IJBToken`). |
|
|
151
|
+
| `projectIdOf(IJBToken token)` | Returns the project ID associated with an ERC-20 token. |
|
|
152
|
+
| `totalCreditSupplyOf(uint256 projectId)` | Returns the internal credit supply for a project. |
|
|
144
153
|
| `setTokenMetadataFor(uint256 projectId, string name, string symbol)` | Sets the name and symbol of a project's token. Controller-only. |
|
|
145
154
|
|
|
146
155
|
### JBSplits
|
|
@@ -157,6 +166,9 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
157
166
|
|----------|--------------|
|
|
158
167
|
| `setFeelessAddress(address addr, bool flag)` | Adds or removes an address from the global (all-project) fee exemption list. Owner-only. (`JBFeelessAddresses`) |
|
|
159
168
|
| `setFeelessAddressFor(uint256 projectId, address addr, bool flag)` | Adds or removes an address from a project's fee exemption list. `projectId = 0` = global wildcard. Owner-only. (`JBFeelessAddresses`) |
|
|
160
|
-
| `
|
|
169
|
+
| `setFeelessHook(IJBFeelessHook hook)` | Sets or clears the optional hook consulted by `isFeelessFor`. Owner-only. (`JBFeelessAddresses`) |
|
|
170
|
+
| `isFeelessFor(address addr, uint256 projectId, address caller)` | Returns whether an address is feeless for a project, checking static grants and the optional caller-aware hook. (`JBFeelessAddresses`) |
|
|
171
|
+
| `creationFee()` / `creationFeeReceiver()` | Return the current project creation fee and receiver. (`JBProjects`) |
|
|
172
|
+
| `setCreationFee(uint256 fee, address payable receiver)` | Sets the native-token project creation fee. Owner-only. (`JBProjects`) |
|
|
161
173
|
| `setControllerAllowed(uint256 projectId)` | Returns whether a project's controller can currently be set. (`IJBDirectoryAccessControl`) |
|
|
162
174
|
| `setTerminalsAllowed(uint256 projectId)` | Returns whether a project's terminals can currently be set. (`IJBDirectoryAccessControl`) |
|
|
@@ -17,7 +17,7 @@ Use this file when you need deeper protocol reference material after the repo-lo
|
|
|
17
17
|
| `JBCurrencyAmount` | `amount (uint224)`, `currency (uint32)` | Payout limits and surplus allowances |
|
|
18
18
|
| `JBFundAccessLimitGroup` | `terminal (address)`, `token (address)`, `payoutLimits (JBCurrencyAmount[])`, `surplusAllowances (JBCurrencyAmount[])` | `JBRulesetConfig.fundAccessLimitGroups` |
|
|
19
19
|
| `JBPermissionsData` | `operator (address)`, `projectId (uint64)`, `permissionIds (uint8[])` | `setPermissionsFor()` input |
|
|
20
|
-
| `JBFee` | `amount (
|
|
20
|
+
| `JBFee` | `amount (uint224)`, `referralChainId (uint32)`, `beneficiary (address)`, `unlockTimestamp (uint48)`, `referralProjectId (uint48)` | Held fees in `JBMultiTerminal`; referral fields preserve fee attribution until processing |
|
|
21
21
|
| `JBSingleAllowance` | `sigDeadline (uint256)`, `amount (uint160)`, `expiration (uint48)`, `nonce (uint48)`, `signature (bytes)` | Permit2 allowance in terminal payments |
|
|
22
22
|
| `JBRulesetWithMetadata` | `ruleset (JBRuleset)`, `metadata (JBRulesetMetadata)` | `allRulesetsOf()`, `currentRulesetOf()` return values |
|
|
23
23
|
| `JBRulesetWeightCache` | `weight (uint112)`, `weightCutMultiple (uint168)` | Weight caching for long-running rulesets in `JBRulesets` |
|
|
@@ -69,7 +69,7 @@ Use this file when you need deeper protocol reference material after the repo-lo
|
|
|
69
69
|
| `projectId = 0` | `JBPermissionsData` | Wildcard: permission applies to ALL projects. Cannot be combined with ROOT (1). |
|
|
70
70
|
| `permissionId = 1` | `JBPermissions` | ROOT: grants all permissions for the scoped project. |
|
|
71
71
|
| `rulesetId = 0` | `JBSplits.splitsOf()` | Fallback split group used when no splits are set for a specific ruleset. |
|
|
72
|
-
| `projectId = 0` | `JBPrices.addPriceFeedFor()` |
|
|
72
|
+
| `projectId = 0` | `JBPrices.addPriceFeedFor()` | Appends a protocol-wide default price feed (owner-only). |
|
|
73
73
|
|
|
74
74
|
## Gotchas
|
|
75
75
|
|
|
@@ -92,8 +92,10 @@ Use this file when you need deeper protocol reference material after the repo-lo
|
|
|
92
92
|
- **Fee-free cashout exemption is scoped to fee-free intra-terminal payout amounts.** `_feeFreeSurplusOf[projectId][token]` accumulates the value of fee-free payouts. After any outflow (payouts, `useAllowanceOf`, non-zero-tax or feeless cashouts), the counter is capped at the remaining balance — non-fee-free funds leave first, preserving the fee-free counter. 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. Cleared on terminal migration. This prevents a round-trip fee bypass (intra-terminal payout → zero-tax cashout) while scoping fees precisely to the fee-free inflow.
|
|
93
93
|
- `JBProjects` constructor optionally mints project #1 to `feeProjectOwner` -- if `address(0)`, no fee project is created
|
|
94
94
|
- `JBMultiTerminal` derives `DIRECTORY` from the provided `store` in its constructor -- not passed directly
|
|
95
|
-
- `JBPrices.pricePerUnitOf()` checks project
|
|
96
|
-
- `useAllowanceOf()` takes
|
|
95
|
+
- `JBPrices.pricePerUnitOf()` checks project direct feeds, project inverse feeds, default direct feeds, then default inverse feeds. It skips feeds that revert or return zero.
|
|
96
|
+
- `useAllowanceOf()` takes 9 args including `address payable feeBeneficiary` and `uint256 referralProjectId` -- do NOT omit them
|
|
97
|
+
- `sendPayoutsOf()` and `cashOutTokensOf()` also take `uint256 referralProjectId`; pass `0` for no referral credit
|
|
98
|
+
- `JBFeelessAddresses.isFeelessFor()` takes 3 args: `(addr, projectId, caller)`. The optional hook can use `caller` to scope dynamic grants.
|
|
97
99
|
- 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.
|
|
98
100
|
- `cashOutTaxRate` in `JBRulesetMetadata` is `uint16` (max 10,000 basis points), NOT 9-decimal precision
|
|
99
101
|
- `reservedPercent` in `JBRulesetMetadata` is `uint16` (max 10,000 basis points), NOT 9-decimal precision
|
|
@@ -103,7 +105,7 @@ Use this file when you need deeper protocol reference material after the repo-lo
|
|
|
103
105
|
- `JBController`, `JBMultiTerminal`, `JBProjects`, `JBPrices`, `JBPermissions` all support ERC-2771 meta-transactions
|
|
104
106
|
- `JBRulesetMetadataResolver` bit layout: version (4 bits), reservedPercent (16), cashOutTaxRate (16), baseCurrency (32), 14 boolean flags (1 bit each), dataHook address (160), metadata (14)
|
|
105
107
|
- `IJBDirectoryAccessControl` has `setControllerAllowed()` and `setTerminalsAllowed()` -- NOT `setControllerAllowedFor()`
|
|
106
|
-
- Price feeds are
|
|
108
|
+
- Price feeds are append-only in `JBPrices`. Existing feeds cannot be replaced or removed; later feeds are fallbacks after the primary feed.
|
|
107
109
|
- `JBFundAccessLimits` requires payout limits and surplus allowances to be in strictly increasing currency order to prevent duplicates
|
|
108
110
|
- **Empty `fundAccessLimitGroups` = zero payouts, NOT unlimited.** If a ruleset's `fundAccessLimitGroups` array is empty (or has no entry for the terminal/token), `payoutLimitsOf()` returns an empty array → cumulative limit is 0 → `sendPayoutsOf()` caps to 0 and returns 0. To allow unlimited payouts, explicitly set a payout limit with `amount: type(uint224).max`.
|
|
109
111
|
- **`groupId` (uint256) vs `currency` (uint32) are different types for the same address.** `JBSplitGroup.groupId` is `uint256(uint160(tokenAddress))` while `JBAccountingContext.currency` is `uint32(uint160(tokenAddress))`. These truncate differently — only `NATIVE_TOKEN` (0x000000000000000000000000000000000000EEEe) matches by coincidence. Don't confuse them.
|
|
@@ -180,7 +182,7 @@ Errors an agent is most likely to encounter. All are custom errors (revert with
|
|
|
180
182
|
| `JBTokens_InsufficientCredits` | `JBTokens` | `claimTokensFor` count exceeds credit balance. |
|
|
181
183
|
| `JBTokens_TokensMustHave18Decimals` | `JBTokens` | Custom token does not use 18 decimals. |
|
|
182
184
|
| `JBSplits_TotalPercentExceeds100` | `JBSplits` | Split percentages sum exceeds `SPLITS_TOTAL_PERCENT`. |
|
|
183
|
-
| `
|
|
185
|
+
| `JBPrices_PriceFeedAlreadyAdded` | `JBPrices` | The same feed address is already configured for that exact pair. Other backup feeds can still be appended. |
|
|
184
186
|
| `JBPrices_PriceFeedNotFound` | `JBPrices` | No feed found for the requested currency pair. |
|
|
185
187
|
| `JBRulesets_InvalidWeight` | `JBRulesets` | Weight exceeds `uint112.max`. |
|
|
186
188
|
| `JBRulesets_InvalidWeightCutPercent` | `JBRulesets` | `weightCutPercent` exceeds `MAX_WEIGHT_CUT_PERCENT`. |
|
|
@@ -211,7 +213,10 @@ The most important events for indexing and off-chain monitoring. Indexed params
|
|
|
211
213
|
| `HoldFee` | `IJBFeeTerminal` | `projectId*`, `token*`, `amount*`, `fee`, `beneficiary` |
|
|
212
214
|
| `ProcessFee` | `IJBFeeTerminal` | `projectId*`, `token*`, `amount*`, `wasHeld`, `beneficiary` |
|
|
213
215
|
| `ReturnHeldFees` | `IJBFeeTerminal` | `projectId*`, `token*`, `amount*`, `returnedFees`, `leftoverAmount` |
|
|
216
|
+
| `FeeReverted` | `IJBFeeTerminal` | `projectId*`, `token*`, `feeProjectId*`, `amount`, `reason` |
|
|
217
|
+
| `ReferralCredit` | `IJBTerminalStore` | `terminal*`, `referralChainId*`, `referralProjectId*`, `amount`, `newTotal` |
|
|
214
218
|
| `Create` | `IJBProjects` | `projectId*`, `owner*` |
|
|
219
|
+
| `SetTokenMetadata` | `IJBTokens` | `projectId*`, `name`, `symbol` |
|
|
215
220
|
| `OperatorPermissionsSet` | `IJBPermissions` | (operator, account, projectId, permissionIds, packed, caller) |
|
|
216
221
|
| `RulesetQueued` | `IJBRulesets` | (rulesetId, projectId, duration, weight, weightCutPercent, approvalHook, metadata, mustStartAtOrAfter, caller) |
|
|
217
222
|
| `SetSplit` | `IJBSplits` | (projectId, rulesetId, groupId, split, caller) |
|
|
@@ -241,11 +246,12 @@ function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata conte
|
|
|
241
246
|
uint256 cashOutTaxRate, // Overrides the ruleset's cash out tax rate
|
|
242
247
|
uint256 effectiveCashOutCount, // Overrides the token count used for pricing only
|
|
243
248
|
uint256 effectiveTotalSupply, // Overrides total supply used for bonding curve calc
|
|
249
|
+
uint256 effectiveSurplusValue, // Overrides surplus used for bonding curve calc
|
|
244
250
|
JBCashOutHookSpecification[] memory hookSpecifications // Cash out hooks to call + amounts to forward
|
|
245
251
|
);
|
|
246
252
|
```
|
|
247
253
|
|
|
248
|
-
The data hook can override `cashOutTaxRate` (0 = proportional, 10000 = nothing reclaimable), `effectiveCashOutCount`, and `
|
|
254
|
+
The data hook can override `cashOutTaxRate` (0 = proportional, 10000 = nothing reclaimable), `effectiveCashOutCount`, `effectiveTotalSupply`, and `effectiveSurplusValue` to shift cash-out pricing, and return `hookSpecifications` to redirect reclaimed funds to cash out hooks. The terminal still burns the caller-supplied `cashOutCount` and caps the reclaim at locally available funds.
|
|
249
255
|
|
|
250
256
|
### `IJBRulesetDataHook.hasMintPermissionFor()`
|
|
251
257
|
|
package/script/Deploy.s.sol
CHANGED
|
@@ -25,11 +25,11 @@ contract Deploy is Script, Sphinx {
|
|
|
25
25
|
/// @notice The universal PERMIT2 address.
|
|
26
26
|
IPermit2 private constant _PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3);
|
|
27
27
|
|
|
28
|
-
/// @notice The address that is allowed to forward calls to the terminal and controller on
|
|
28
|
+
/// @notice The address that is allowed to forward calls to the terminal and controller on behalf of users.
|
|
29
29
|
string private constant _TRUSTED_FORWARDER_NAME = "Juicebox";
|
|
30
30
|
address private trustedForwarder;
|
|
31
31
|
|
|
32
|
-
/// @notice The address that will manage
|
|
32
|
+
/// @notice The address that will manage privileged protocol functions.
|
|
33
33
|
address private manager;
|
|
34
34
|
|
|
35
35
|
/// @notice The address that will own the fee-project.
|
|
@@ -69,11 +69,10 @@ contract DeployPeriphery is Script, Sphinx {
|
|
|
69
69
|
IJBPriceFeed matchingPriceFeed;
|
|
70
70
|
matchingPriceFeed = new JBMatchingPriceFeed();
|
|
71
71
|
|
|
72
|
-
// Same
|
|
72
|
+
// Same grace period used in Chainlink sequencer-feed examples.
|
|
73
73
|
uint256 l2GracePeriod = 3600 seconds;
|
|
74
74
|
|
|
75
|
-
//
|
|
76
|
-
// Sequencer feeds come from this url `https://docs.chain.link/data-feeds/l2-sequencer-feeds`.
|
|
75
|
+
// Feed addresses come from Chainlink price-feed and L2 sequencer-feed docs.
|
|
77
76
|
|
|
78
77
|
// Perform the deploy for L1(s).
|
|
79
78
|
if (block.chainid == 1) {
|
|
@@ -148,8 +147,8 @@ contract DeployPeriphery is Script, Sphinx {
|
|
|
148
147
|
feed: feed
|
|
149
148
|
}) {}
|
|
150
149
|
catch {}
|
|
151
|
-
// `addPriceFeedFor` can revert if this feed was already registered. Still require
|
|
152
|
-
// different
|
|
150
|
+
// `addPriceFeedFor` can revert if this feed was already registered. Still require this feed to be the
|
|
151
|
+
// authoritative default so a different existing feed can't silently satisfy this deployment step.
|
|
153
152
|
require(
|
|
154
153
|
address(
|
|
155
154
|
core.prices
|
|
@@ -158,30 +157,27 @@ contract DeployPeriphery is Script, Sphinx {
|
|
|
158
157
|
pricingCurrency: JBCurrencyIds.USD,
|
|
159
158
|
unitCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
160
159
|
})
|
|
161
|
-
)
|
|
162
|
-
"
|
|
160
|
+
) == address(feed),
|
|
161
|
+
"Unexpected USD/native price feed"
|
|
163
162
|
);
|
|
164
163
|
|
|
165
|
-
//
|
|
166
|
-
// chains where Ether is the native asset. We *NEED* to update this when we deploy to a non-ether chain!
|
|
164
|
+
// This feed also prices USD/ETH, which is valid only on ETH-native chains.
|
|
167
165
|
try core.prices
|
|
168
166
|
.addPriceFeedFor({
|
|
169
167
|
projectId: 0, pricingCurrency: JBCurrencyIds.USD, unitCurrency: JBCurrencyIds.ETH, feed: feed
|
|
170
168
|
}) {}
|
|
171
169
|
catch {}
|
|
172
|
-
// `addPriceFeedFor` can revert if this feed was already registered. Still require
|
|
173
|
-
// different
|
|
170
|
+
// `addPriceFeedFor` can revert if this feed was already registered. Still require this feed to be the
|
|
171
|
+
// authoritative default so a different existing feed can't silently satisfy this deployment step.
|
|
174
172
|
require(
|
|
175
173
|
address(
|
|
176
174
|
core.prices
|
|
177
175
|
.priceFeedFor({projectId: 0, pricingCurrency: JBCurrencyIds.USD, unitCurrency: JBCurrencyIds.ETH})
|
|
178
|
-
)
|
|
179
|
-
"
|
|
176
|
+
) == address(feed),
|
|
177
|
+
"Unexpected USD/ETH price feed"
|
|
180
178
|
);
|
|
181
179
|
|
|
182
|
-
//
|
|
183
|
-
// NOTE: We need to refactor this the moment we add a chain where its native token is *NOT* ether.
|
|
184
|
-
// As otherwise prices for the `NATIVE_TOKEN` will be incorrect!
|
|
180
|
+
// ETH/native is 1:1 only on ETH-native chains. Add a separate feed before deploying elsewhere.
|
|
185
181
|
try core.prices
|
|
186
182
|
.addPriceFeedFor({
|
|
187
183
|
projectId: 0,
|
|
@@ -190,8 +186,8 @@ contract DeployPeriphery is Script, Sphinx {
|
|
|
190
186
|
feed: matchingPriceFeed
|
|
191
187
|
}) {}
|
|
192
188
|
catch {}
|
|
193
|
-
// `addPriceFeedFor` can revert if this feed was already registered. Still require
|
|
194
|
-
// different
|
|
189
|
+
// `addPriceFeedFor` can revert if this feed was already registered. Still require this feed to be the
|
|
190
|
+
// authoritative default so a different existing feed can't silently satisfy this deployment step.
|
|
195
191
|
require(
|
|
196
192
|
address(
|
|
197
193
|
core.prices
|
|
@@ -200,8 +196,8 @@ contract DeployPeriphery is Script, Sphinx {
|
|
|
200
196
|
pricingCurrency: JBCurrencyIds.ETH,
|
|
201
197
|
unitCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
202
198
|
})
|
|
203
|
-
)
|
|
204
|
-
"
|
|
199
|
+
) == address(matchingPriceFeed),
|
|
200
|
+
"Unexpected ETH/native price feed"
|
|
205
201
|
);
|
|
206
202
|
|
|
207
203
|
// Deploy the USDC/USD price feed.
|
|
@@ -318,14 +314,14 @@ contract DeployPeriphery is Script, Sphinx {
|
|
|
318
314
|
projectId: 0, pricingCurrency: JBCurrencyIds.USD, unitCurrency: usdcCurrencyId, feed: usdcFeed
|
|
319
315
|
}) {}
|
|
320
316
|
catch {}
|
|
321
|
-
// `addPriceFeedFor` can revert if this feed was already registered. Still require
|
|
322
|
-
// different
|
|
317
|
+
// `addPriceFeedFor` can revert if this feed was already registered. Still require this feed to be the
|
|
318
|
+
// authoritative default so a different existing feed can't silently satisfy this deployment step.
|
|
323
319
|
require(
|
|
324
320
|
address(
|
|
325
321
|
core.prices
|
|
326
322
|
.priceFeedFor({projectId: 0, pricingCurrency: JBCurrencyIds.USD, unitCurrency: usdcCurrencyId})
|
|
327
|
-
)
|
|
328
|
-
"
|
|
323
|
+
) == address(usdcFeed),
|
|
324
|
+
"Unexpected USD/USDC price feed"
|
|
329
325
|
);
|
|
330
326
|
}
|
|
331
327
|
|
package/src/JBController.sol
CHANGED
|
@@ -42,9 +42,8 @@ import {JBSplitHookContext} from "./structs/JBSplitHookContext.sol";
|
|
|
42
42
|
import {JBTerminalConfig} from "./structs/JBTerminalConfig.sol";
|
|
43
43
|
|
|
44
44
|
/// @notice The orchestrator for every Juicebox project's lifecycle. Use the controller to launch a project, queue new
|
|
45
|
-
/// rulesets
|
|
46
|
-
///
|
|
47
|
-
/// splits (distribution).
|
|
45
|
+
/// rulesets, mint or burn tokens, deploy an ERC-20, distribute reserved tokens, and manage permissions. The controller
|
|
46
|
+
/// coordinates between the terminal (money), rulesets (rules), tokens (issuance), and splits (distribution).
|
|
48
47
|
/// @dev Supports ERC-2771 meta-transactions. Implements `IJBMigratable` for controller-to-controller migration.
|
|
49
48
|
/// An omnichain deployer address is trusted to launch and queue rulesets on behalf of any project for cross-chain
|
|
50
49
|
/// coordination.
|
|
@@ -7,9 +7,9 @@ import {IJBFundAccessLimits} from "./interfaces/IJBFundAccessLimits.sol";
|
|
|
7
7
|
import {JBCurrencyAmount} from "./structs/JBCurrencyAmount.sol";
|
|
8
8
|
import {JBFundAccessLimitGroup} from "./structs/JBFundAccessLimitGroup.sol";
|
|
9
9
|
|
|
10
|
-
/// @notice Controls how much a project can withdraw from its terminals each
|
|
11
|
-
/// **Payout limits** cap
|
|
12
|
-
///
|
|
10
|
+
/// @notice Controls how much a project can withdraw from its terminals during each ruleset. Two types of limits:
|
|
11
|
+
/// **Payout limits** cap distributions to splits and the project owner. **Surplus allowances** cap how much the
|
|
12
|
+
/// project owner can pull from surplus (funds above payout limits).
|
|
13
13
|
/// @dev Limits are denominated in a currency (which may differ from the held token) and resolved at withdrawal time
|
|
14
14
|
/// via `JBPrices`. An empty `fundAccessLimitGroups` array means zero access (not unlimited) — use `type(uint224).max`
|
|
15
15
|
/// for unlimited.
|
|
@@ -74,7 +74,7 @@ contract JBFundAccessLimits is JBControlled, IJBFundAccessLimits {
|
|
|
74
74
|
|
|
75
75
|
/// @notice Configure how much a project can withdraw from each of its terminals during a ruleset. Payout limits
|
|
76
76
|
/// cap how much can be distributed to splits/owner; surplus allowances cap how much extra the owner can pull from
|
|
77
|
-
/// surplus.
|
|
77
|
+
/// surplus. Payout usage resets by ruleset cycle number; surplus-allowance usage resets by ruleset ID.
|
|
78
78
|
/// @dev Only a project's controller can set fund access limits (called during `queueRulesetsOf`).
|
|
79
79
|
/// @dev Limits within each group must be sorted by currency in strictly increasing order to prevent duplicates.
|
|
80
80
|
/// @param projectId The ID of the project to set fund access limits for.
|
package/src/JBMultiTerminal.sol
CHANGED
|
@@ -115,13 +115,13 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
115
115
|
//*********************************************************************//
|
|
116
116
|
|
|
117
117
|
/// @notice The cumulative amount of fee-free intra-terminal payouts a project has received for a given token.
|
|
118
|
-
/// @dev Incremented each time a fee-free payout lands (same terminal, no fee charged). During
|
|
118
|
+
/// @dev Incremented each time a fee-free payout lands (same terminal, no fee charged). During cash out with
|
|
119
119
|
/// `cashOutTaxRate == 0`, fees are applied only up to this amount, then decremented. This prevents a round-trip
|
|
120
|
-
/// fee bypass (intra-terminal payout
|
|
121
|
-
/// — legitimate
|
|
120
|
+
/// fee bypass (intra-terminal payout -> zero-tax cash out) while scoping the fee precisely to the fee-free inflow
|
|
121
|
+
/// — legitimate cash outs beyond this amount remain fee-free.
|
|
122
122
|
/// @dev Lifecycle: incremented on fee-free intra-terminal payouts. After any outflow (payouts, useAllowanceOf,
|
|
123
|
-
/// non-zero-tax or feeless
|
|
124
|
-
/// first, preserving the fee-free counter. Consumed during zero-tax
|
|
123
|
+
/// non-zero-tax or feeless cash outs), capped at remaining balance — non-fee-free funds are considered to leave
|
|
124
|
+
/// first, preserving the fee-free counter. Consumed during zero-tax cash outs. Cleared on terminal migration.
|
|
125
125
|
/// @dev Persists across rulesets — projects switching from zero-tax to non-zero-tax carry forward any
|
|
126
126
|
/// unconsumed balance. There is no admin function to reset it.
|
|
127
127
|
/// @custom:param projectId The ID of the project that received the payout.
|
|
@@ -155,8 +155,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
155
155
|
/// their own chain ID.
|
|
156
156
|
/// @dev Set by the three entry points via the `_setReferralProjectId` save-restore wrapper. Read inside `_pay`
|
|
157
157
|
/// to credit `feeVolumeByReferralOf` when the fee project's pay call is recorded locally.
|
|
158
|
-
/// @dev Public so pay
|
|
159
|
-
/// apply referral-specific logic). Reads `0` outside any fee-paying call.
|
|
158
|
+
/// @dev Public so pay, cash out, and split hooks can introspect which referral originated the in-flight call (e.g.
|
|
159
|
+
/// to apply referral-specific logic). Reads `0` outside any fee-paying call.
|
|
160
160
|
uint256 public transient override currentReferralProjectId;
|
|
161
161
|
|
|
162
162
|
//*********************************************************************//
|
|
@@ -291,8 +291,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
291
291
|
/// call to succeed, as a fixed point number with the same number of decimals as the terminal token's accounting
|
|
292
292
|
/// context. If fewer terminal tokens would be reclaimed, the cash out is reverted.
|
|
293
293
|
/// @param beneficiary The address to send the reclaimed terminal tokens to, and to pass along to the ruleset's
|
|
294
|
-
/// data hook and cash out
|
|
295
|
-
/// @param metadata Bytes to send along to the emitted event, as well as the data hook and cash out
|
|
294
|
+
/// data hook and cash out hooks if applicable.
|
|
295
|
+
/// @param metadata Bytes to send along to the emitted event, as well as the data hook and cash out hooks if
|
|
296
296
|
/// applicable.
|
|
297
297
|
/// @param referralProjectId Optional referrer reference to credit with the protocol fee volume taken by this
|
|
298
298
|
/// call, encoded as `(referralChainId << 48) | referralProjectId` (chain ID in the upper 32 bits, project ID
|
|
@@ -715,6 +715,9 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
715
715
|
/// @param token The token to process held fees for.
|
|
716
716
|
/// @param count The number of fees to process.
|
|
717
717
|
function processHeldFeesOf(uint256 projectId, address token, uint256 count) external override {
|
|
718
|
+
// Preserve any in-flight referral context if this function is reached through reentrancy.
|
|
719
|
+
uint256 previousReferralProjectId = currentReferralProjectId;
|
|
720
|
+
|
|
718
721
|
// Keep a reference to the terminal that'll receive the fees.
|
|
719
722
|
IJBTerminal feeTerminal = _primaryTerminalOf({projectId: JBConstants.FEE_BENEFICIARY_PROJECT_ID, token: token});
|
|
720
723
|
|
|
@@ -749,11 +752,9 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
749
752
|
_nextHeldFeeIndexOf[projectId][token] = currentIndex + 1;
|
|
750
753
|
}
|
|
751
754
|
|
|
752
|
-
// Restore the originating fee-paying call's referral project for
|
|
753
|
-
//
|
|
754
|
-
//
|
|
755
|
-
// cleanup happens once after the loop instead of per-iteration. Reconstructs the packed encoding from
|
|
756
|
-
// the two struct halves: `(chainId << 48) | projectId`.
|
|
755
|
+
// Restore the originating fee-paying call's referral project for this fee's processing so the credit in
|
|
756
|
+
// `_processFee` attributes to the right (chain, project) pair. Reconstructs the packed encoding from the
|
|
757
|
+
// two struct halves: `(chainId << 48) | projectId`.
|
|
757
758
|
currentReferralProjectId = (uint256(heldFee.referralChainId) << 48) | uint256(heldFee.referralProjectId);
|
|
758
759
|
|
|
759
760
|
// Process the standard fee on the original gross amount recorded when the held fee was created.
|
|
@@ -771,10 +772,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
771
772
|
}
|
|
772
773
|
}
|
|
773
774
|
|
|
774
|
-
//
|
|
775
|
-
|
|
776
|
-
// reentry is the only case where this matters.
|
|
777
|
-
currentReferralProjectId = 0;
|
|
775
|
+
// Restore the previous transient value so reentrant held-fee processing does not erase an outer referral.
|
|
776
|
+
currentReferralProjectId = previousReferralProjectId;
|
|
778
777
|
|
|
779
778
|
// If all held fees have been processed, reset the array and index entirely to bound storage growth.
|
|
780
779
|
if (
|
|
@@ -1339,13 +1338,8 @@ contract JBMultiTerminal is JBPermissioned, ERC2771Context, IJBMultiTerminal {
|
|
|
1339
1338
|
});
|
|
1340
1339
|
}
|
|
1341
1340
|
|
|
1342
|
-
// Cap fee-free surplus
|
|
1343
|
-
//
|
|
1344
|
-
// zero tax, and feeless beneficiary) gets the cap. Without it, the zero-tax path would use
|
|
1345
|
-
// a stale _feeFreeSurplusOf that may exceed STORE.balanceOf (e.g. if pay hooks reduced the
|
|
1346
|
-
// balance during a prior inbound payout), overcharging fees on round-trip prevention.
|
|
1347
|
-
// Placed after hook fulfillment so any further balance reductions from cashout hooks are
|
|
1348
|
-
// also accounted for.
|
|
1341
|
+
// Cap fee-free surplus after every cash-out path so stale `_feeFreeSurplusOf` cannot survive after
|
|
1342
|
+
// associated surplus leaves. Do this after hook fulfillment so hook-driven balance reductions are included.
|
|
1349
1343
|
_capFeeFreeSurplus({projectId: projectId, token: tokenToReclaim});
|
|
1350
1344
|
|
|
1351
1345
|
// Take the fee from all outbound reclaimings.
|
package/src/JBTerminalStore.sol
CHANGED
|
@@ -289,8 +289,8 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
289
289
|
/// @param beneficiaryIsFeeless Whether the cash out's beneficiary is a feeless address. Passed through to data
|
|
290
290
|
/// hooks so they can skip their own fees when value stays in the protocol (e.g. project-to-project routing).
|
|
291
291
|
/// @param metadata Bytes to send to the data hook, if the project's current ruleset specifies one.
|
|
292
|
-
/// @return ruleset The ruleset during the cash out was made during, as a `JBRuleset` struct.
|
|
293
|
-
///
|
|
292
|
+
/// @return ruleset The ruleset during the cash out was made during, as a `JBRuleset` struct. Its cash out tax
|
|
293
|
+
/// rate may be overridden by the data hook.
|
|
294
294
|
/// @return reclaimAmount The amount of tokens reclaimed from the terminal, as a fixed point number with 18
|
|
295
295
|
/// decimals.
|
|
296
296
|
/// @return cashOutTaxRate The cash out tax rate influencing the reclaim amount.
|
|
@@ -339,18 +339,28 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
//
|
|
343
|
-
|
|
342
|
+
// The selected terminal token can only pay out its own local surplus. Cash-out pricing may use shared surplus
|
|
343
|
+
// across terminals/tokens, but settlement must not dip into this token's payout-limit reserve.
|
|
344
|
+
JBAccountingContext memory accountingContext =
|
|
345
|
+
_accountingContextForTokenOf[msg.sender][projectId][tokenToReclaim];
|
|
346
|
+
uint256 localSurplus = _tokenSurplusFrom({
|
|
347
|
+
terminal: msg.sender,
|
|
348
|
+
projectId: projectId,
|
|
349
|
+
accountingContext: accountingContext,
|
|
350
|
+
ruleset: ruleset,
|
|
351
|
+
targetDecimals: accountingContext.decimals,
|
|
352
|
+
targetCurrency: accountingContext.currency
|
|
353
|
+
});
|
|
344
354
|
|
|
345
|
-
// The amount being reclaimed must be within the project's
|
|
346
|
-
if (balanceDiff >
|
|
347
|
-
revert JBTerminalStore_InadequateTerminalStoreBalance({amount: balanceDiff, balance:
|
|
355
|
+
// The amount being reclaimed must be within the project's local surplus.
|
|
356
|
+
if (balanceDiff > localSurplus) {
|
|
357
|
+
revert JBTerminalStore_InadequateTerminalStoreBalance({amount: balanceDiff, balance: localSurplus});
|
|
348
358
|
}
|
|
349
359
|
|
|
350
360
|
// Remove the reclaimed funds from the project's balance.
|
|
351
361
|
if (balanceDiff != 0) {
|
|
352
362
|
unchecked {
|
|
353
|
-
balanceOf[msg.sender][projectId][tokenToReclaim]
|
|
363
|
+
balanceOf[msg.sender][projectId][tokenToReclaim] -= balanceDiff;
|
|
354
364
|
}
|
|
355
365
|
}
|
|
356
366
|
}
|
|
@@ -564,6 +574,11 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
564
574
|
})
|
|
565
575
|
});
|
|
566
576
|
|
|
577
|
+
// If cross-currency conversion rounded to zero, return without consuming any surplus allowance.
|
|
578
|
+
if (usedAmount == 0) {
|
|
579
|
+
return (ruleset, 0);
|
|
580
|
+
}
|
|
581
|
+
|
|
567
582
|
// Set the token being used as the only one to look for surplus within.
|
|
568
583
|
JBAccountingContext[] memory accountingContexts = new JBAccountingContext[](1);
|
|
569
584
|
accountingContexts[0] = accountingContext;
|
|
@@ -1378,7 +1393,7 @@ contract JBTerminalStore is IJBTerminalStore {
|
|
|
1378
1393
|
{
|
|
1379
1394
|
// Saturating subtraction: if a new ruleset activates with a lower payout limit than
|
|
1380
1395
|
// what was already used under the previous limit, `used` can exceed `amount`. Clamping
|
|
1381
|
-
// to zero prevents an underflow revert that would
|
|
1396
|
+
// to zero prevents an underflow revert that would DoS cash outs and surplus views.
|
|
1382
1397
|
uint256 used = usedPayoutLimitOf[
|
|
1383
1398
|
terminal
|
|
1384
1399
|
][projectId][accountingContext.token][ruleset.cycleNumber][payoutLimit.currency];
|
|
@@ -85,7 +85,7 @@ interface IJBCashOutTerminal is IJBTerminal {
|
|
|
85
85
|
/// @param beneficiary The address to send the reclaimed terminal tokens to.
|
|
86
86
|
/// @param metadata Extra data to send to the data hook and cash out hooks.
|
|
87
87
|
/// @param referralProjectId Optional project to credit with the protocol fee volume taken by this call. Pass `0`
|
|
88
|
-
///
|
|
88
|
+
/// for no referral credit.
|
|
89
89
|
/// @return reclaimAmount The number of terminal tokens reclaimed from the project's surplus.
|
|
90
90
|
function cashOutTokensOf(
|
|
91
91
|
address holder,
|
|
@@ -38,9 +38,9 @@ interface IJBMultiTerminal is IJBTerminal, IJBFeeTerminal, IJBCashOutTerminal, I
|
|
|
38
38
|
/// resolved to the current execution chain via `block.chainid` at the entry point, so storage and indexers
|
|
39
39
|
/// always see a fully-resolved `(chainId, projectId)` pair.
|
|
40
40
|
/// @dev Backed by transient storage. Set by `cashOutTokensOf`, `sendPayoutsOf`, and `useAllowanceOf` (save-
|
|
41
|
-
/// restore wrapper) so hooks invoked during that call (pay hooks,
|
|
41
|
+
/// restore wrapper) so hooks invoked during that call (pay hooks, cash out hooks, split hooks) can introspect
|
|
42
42
|
/// which referrer originated the activity. Reads `0` outside any fee-paying call.
|
|
43
43
|
/// @dev Per-referrer cumulative fee payment amounts credited via this terminal are stored in
|
|
44
|
-
/// `JBTerminalStore.feeVolumeByReferralOf(address terminal, uint256 referralProjectId)`.
|
|
44
|
+
/// `JBTerminalStore.feeVolumeByReferralOf(address terminal, uint256 referralChainId, uint256 referralProjectId)`.
|
|
45
45
|
function currentReferralProjectId() external view returns (uint256);
|
|
46
46
|
}
|
|
@@ -103,7 +103,7 @@ interface IJBPayoutTerminal is IJBTerminal {
|
|
|
103
103
|
/// @param currency The currency the amount is denominated in.
|
|
104
104
|
/// @param minTokensPaidOut The minimum number of terminal tokens expected to be paid out.
|
|
105
105
|
/// @param referralProjectId Optional project to credit with the protocol fee volume taken by this call. Pass `0`
|
|
106
|
-
///
|
|
106
|
+
/// for no referral credit.
|
|
107
107
|
/// @return amountPaidOut The total amount paid out.
|
|
108
108
|
function sendPayoutsOf(
|
|
109
109
|
uint256 projectId,
|
|
@@ -126,7 +126,7 @@ interface IJBPayoutTerminal is IJBTerminal {
|
|
|
126
126
|
/// @param feeBeneficiary The address that will receive any project tokens minted from fees.
|
|
127
127
|
/// @param memo A memo to pass along to the emitted event.
|
|
128
128
|
/// @param referralProjectId Optional project to credit with the protocol fee volume taken by this call. Pass `0`
|
|
129
|
-
///
|
|
129
|
+
/// for no referral credit.
|
|
130
130
|
/// @return netAmountPaidOut The net amount paid out to the beneficiary after fees.
|
|
131
131
|
function useAllowanceOf(
|
|
132
132
|
uint256 projectId,
|
|
@@ -17,9 +17,8 @@ import {JBTokenAmount} from "../structs/JBTokenAmount.sol";
|
|
|
17
17
|
interface IJBTerminalStore {
|
|
18
18
|
/// @notice Emitted when a referrer is credited with a fee payment amount.
|
|
19
19
|
/// @dev `referralChainId` and `referralProjectId` are emitted as separate indexed topics so off-chain consumers
|
|
20
|
-
/// can filter directly on either dimension. The `feeVolumeByReferralOf`
|
|
21
|
-
/// `(referralChainId
|
|
22
|
-
/// cumulative balance for a referrer.
|
|
20
|
+
/// can filter directly on either dimension. The public `feeVolumeByReferralOf` getter exposes the same unpacked
|
|
21
|
+
/// `(terminal, referralChainId, referralProjectId)` tuple.
|
|
23
22
|
/// @param terminal The terminal that originated the fee-paying call (`msg.sender` on `recordFeeReferralCreditOf`).
|
|
24
23
|
/// @param referralChainId The EIP-155 chain ID of the referrer's home chain.
|
|
25
24
|
/// @param referralProjectId The referrer's bare project ID on `referralChainId` (no chain bits).
|
|
@@ -207,7 +207,10 @@ library JBPayoutSplitGroupLib {
|
|
|
207
207
|
uint256 sentAmount, uint256 feeEligible
|
|
208
208
|
) {
|
|
209
209
|
netPayoutAmount = sentAmount;
|
|
210
|
-
|
|
210
|
+
// The standard fee is `STANDARD_FEE / MAX_FEE`, currently 25 / 1000 = 1 / 40. The `40` below is
|
|
211
|
+
// that reduced denominator, not an independent fee parameter. Round each split's fee basis down to
|
|
212
|
+
// it so aggregation cannot charge more than per-split floors.
|
|
213
|
+
amountEligibleForFees += feeEligible - (feeEligible % 40);
|
|
211
214
|
} catch (bytes memory failureReason) {
|
|
212
215
|
emit PayoutReverted({
|
|
213
216
|
projectId: projectId, split: split, amount: payoutAmount, reason: failureReason, caller: caller
|
|
@@ -90,7 +90,7 @@ library JBRulesetMetadataResolver {
|
|
|
90
90
|
return uint16(ruleset.metadata >> 242);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
/// @notice Pack
|
|
93
|
+
/// @notice Pack ruleset metadata.
|
|
94
94
|
/// @param rulesetMetadata The ruleset metadata to validate and pack.
|
|
95
95
|
/// @return packed The packed uint256 of all metadata params. The first 4 bits (bits 0-3) specify the version;
|
|
96
96
|
/// supported values are 0-15. A future protocol version that needs more than 16 distinct metadata layouts must
|
|
@@ -131,18 +131,18 @@ library JBRulesetMetadataResolver {
|
|
|
131
131
|
if (rulesetMetadata.holdFees) packed |= 1 << 78;
|
|
132
132
|
// scopeCashOutsToLocalBalances in bit 79.
|
|
133
133
|
if (rulesetMetadata.scopeCashOutsToLocalBalances) packed |= 1 << 79;
|
|
134
|
-
// use pay data
|
|
134
|
+
// use pay data hook in bit 80.
|
|
135
135
|
if (rulesetMetadata.useDataHookForPay) packed |= 1 << 80;
|
|
136
|
-
// use cash out data
|
|
136
|
+
// use cash out data hook in bit 81.
|
|
137
137
|
if (rulesetMetadata.useDataHookForCashOut) packed |= 1 << 81;
|
|
138
|
-
// data
|
|
138
|
+
// data hook address in bits 82-241.
|
|
139
139
|
packed |= uint256(uint160(address(rulesetMetadata.dataHook))) << 82;
|
|
140
140
|
// metadata in bits 242-255 (14 bits).
|
|
141
141
|
packed |= (uint256(rulesetMetadata.metadata) & 0x3FFF) << 242;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
/// @notice Expand
|
|
145
|
-
/// @param ruleset The
|
|
144
|
+
/// @notice Expand ruleset metadata.
|
|
145
|
+
/// @param ruleset The ruleset whose metadata is expanded.
|
|
146
146
|
/// @return rulesetMetadata The ruleset's metadata object.
|
|
147
147
|
function expandMetadata(JBRuleset memory ruleset) internal pure returns (JBRulesetMetadata memory) {
|
|
148
148
|
return JBRulesetMetadata({
|
package/src/structs/JBFee.sol
CHANGED
|
@@ -11,9 +11,9 @@ pragma solidity ^0.8.0;
|
|
|
11
11
|
/// @custom:member beneficiary The address that will receive the tokens that are minted as a result of the fee payment.
|
|
12
12
|
/// @custom:member unlockTimestamp The timestamp at which the fee is unlocked and can be processed.
|
|
13
13
|
/// @custom:member referralProjectId The referrer's project ID on `referralChainId`. Captured from the lower bits of
|
|
14
|
-
/// `currentReferralProjectId` at fee-take time so held-fee attribution survives the 28-day hold window. The
|
|
15
|
-
///
|
|
16
|
-
///
|
|
14
|
+
/// `currentReferralProjectId` at fee-take time so held-fee attribution survives the 28-day hold window. The transient
|
|
15
|
+
/// slot and terminal entry-point arguments use the packed `(referralChainId << 48) | referralProjectId` form; this
|
|
16
|
+
/// struct stores the two halves separately for cheap storage.
|
|
17
17
|
struct JBFee {
|
|
18
18
|
uint224 amount;
|
|
19
19
|
uint32 referralChainId;
|
|
@@ -3,17 +3,17 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
4
|
import {JBCurrencyAmount} from "./JBCurrencyAmount.sol";
|
|
5
5
|
|
|
6
|
-
/// @notice Defines how much a project can withdraw from a specific terminal and token
|
|
6
|
+
/// @notice Defines how much a project can withdraw from a specific terminal and token during a ruleset.
|
|
7
7
|
/// @dev A ruleset configuration should include at most one group for each `(terminal, token)` pair.
|
|
8
8
|
/// @dev Example — payout limit of 5 USD in an ETH terminal: the project can distribute up to 5 USD worth of ETH to
|
|
9
|
-
/// its splits per cycle. Example — surplus allowance of 5 USD: the project owner can pull up to 5 USD
|
|
10
|
-
/// from the surplus (balance above payout limits).
|
|
11
|
-
/// @dev Multiple limits in different currencies are additive
|
|
9
|
+
/// its splits per ruleset cycle. Example — surplus allowance of 5 USD: the project owner can pull up to 5 USD
|
|
10
|
+
/// worth of ETH from the surplus (balance above payout limits) during the ruleset.
|
|
11
|
+
/// @dev Multiple limits in different currencies are additive within their respective reset windows.
|
|
12
12
|
/// @dev Amounts use the same decimal precision as the terminal token (e.g. 18 for ETH, 6 for USDC).
|
|
13
13
|
/// @custom:member terminal The terminal address these limits apply to.
|
|
14
14
|
/// @custom:member token The token address within that terminal these limits apply to.
|
|
15
|
-
/// @custom:member payoutLimits Maximum amounts distributable to splits per cycle, each in a specific currency.
|
|
16
|
-
/// @custom:member surplusAllowances Maximum amounts withdrawable from surplus per
|
|
15
|
+
/// @custom:member payoutLimits Maximum amounts distributable to splits per ruleset cycle, each in a specific currency.
|
|
16
|
+
/// @custom:member surplusAllowances Maximum amounts withdrawable from surplus per ruleset, each in a specific currency.
|
|
17
17
|
struct JBFundAccessLimitGroup {
|
|
18
18
|
address terminal;
|
|
19
19
|
address token;
|