@bananapus/core-v6 0.0.24 → 0.0.25
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 +16 -3
- package/ARCHITECTURE.md +28 -9
- package/AUDIT_INSTRUCTIONS.md +102 -17
- package/CHANGE_LOG.md +18 -8
- package/README.md +58 -2
- package/RISKS.md +13 -20
- package/SKILLS.md +158 -11
- package/STYLE_GUIDE.md +11 -6
- package/USER_JOURNEYS.md +53 -16
- package/foundry.toml +1 -1
- package/package.json +2 -2
- package/script/Deploy.s.sol +2 -2
- package/script/DeployPeriphery.s.sol +2 -5
- package/script/helpers/CoreDeploymentLib.sol +2 -2
- package/src/JBChainlinkV3PriceFeed.sol +1 -1
- package/src/JBChainlinkV3SequencerPriceFeed.sol +1 -1
- package/src/JBController.sol +14 -7
- package/src/JBDeadline.sol +1 -1
- package/src/JBDirectory.sol +1 -1
- package/src/JBERC20.sol +6 -2
- package/src/JBFeelessAddresses.sol +1 -1
- package/src/JBFundAccessLimits.sol +1 -1
- package/src/JBMultiTerminal.sol +53 -4
- package/src/JBPermissions.sol +6 -2
- package/src/JBPrices.sol +1 -1
- package/src/JBProjects.sol +1 -1
- package/src/JBRulesets.sol +1 -1
- package/src/JBSplits.sol +1 -1
- package/src/JBTerminalStore.sol +57 -53
- package/src/JBTokens.sol +5 -1
- package/src/interfaces/IJBController.sol +7 -1
- package/src/libraries/JBPayoutSplitGroupLib.sol +1 -1
- package/src/periphery/JBDeadline1Day.sol +1 -1
- package/src/periphery/JBDeadline3Days.sol +1 -1
- package/src/periphery/JBDeadline3Hours.sol +1 -1
- package/src/periphery/JBDeadline7Days.sol +1 -1
- package/src/periphery/JBMatchingPriceFeed.sol +1 -1
- package/test/TestAccessToFunds.sol +4 -4
- package/test/TestFeeFreeCashOutBypass.sol +332 -0
- package/test/TestJBERC20Inheritance.sol +1 -1
- package/test/TestMetadataOffsetOverflow.sol +1 -1
- package/test/TestMetadataParserLib.sol +1 -1
- package/test/TestMultiTerminalSurplus.sol +1 -1
- package/test/TestMultiTokenSurplus.sol +1 -1
- package/test/TestMultipleAccessLimits.sol +4 -4
- package/test/TestPermit2DataHook.t.sol +1 -1
- package/test/TestPermit2Terminal.sol +1 -1
- package/test/TestTerminalPreviewParity.sol +1 -1
- package/test/audit/CashOutReenterPay.t.sol +496 -0
- package/test/audit/FeeFreeSurplusLifecycle.t.sol +392 -0
- package/test/audit/FeeFreeSurplusStale.t.sol +242 -0
- package/test/audit/USDTVoidReturnCompat.t.sol +519 -0
- package/test/fork/TestChainlinkPriceFeedFork.sol +1 -1
- package/test/fork/TestSequencerPriceFeedFork.sol +1 -1
- package/test/fork/TestTerminalPreviewParityFork.sol +1 -1
- package/test/helpers/JBTest.sol +1 -1
- package/test/helpers/MetadataResolverHelper.sol +1 -1
- package/test/mock/MockERC20.sol +1 -1
- package/test/mock/MockMaliciousBeneficiary.sol +1 -1
- package/test/mock/MockMaliciousSplitHook.sol +1 -1
- package/test/mock/MockPriceFeed.sol +1 -1
- package/test/mock/MockUSDT.sol +80 -0
- package/test/regression/HoldFeesCashOutReserved.t.sol +2 -2
- package/test/units/static/JBChainlinkV3PriceFeed/TestPriceFeed.sol +1 -1
- package/test/units/static/JBController/JBControllerSetup.sol +1 -1
- package/test/units/static/JBController/TestBurnTokensOf.sol +1 -1
- package/test/units/static/JBController/TestClaimTokensFor.sol +1 -1
- package/test/units/static/JBController/TestDeployErc20For.sol +1 -1
- package/test/units/static/JBController/TestLaunchProjectFor.sol +1 -1
- package/test/units/static/JBController/TestLaunchRulesetsFor.sol +1 -1
- package/test/units/static/JBController/TestMigrateController.sol +1 -1
- package/test/units/static/JBController/TestMintTokensOfUnits.sol +1 -1
- package/test/units/static/JBController/TestOmnichainRulesetOperator.sol +324 -0
- package/test/units/static/JBController/TestPayReservedTokenToTerminal.sol +1 -1
- package/test/units/static/JBController/TestPreviewMintOf.sol +1 -1
- package/test/units/static/JBController/TestReceiveMigrationFrom.sol +1 -1
- package/test/units/static/JBController/TestRulesetViews.sol +1 -1
- package/test/units/static/JBController/TestSendReservedTokensToSplitsOf.sol +1 -1
- package/test/units/static/JBController/TestSetSplitGroupsOf.sol +1 -1
- package/test/units/static/JBController/TestSetTokenFor.sol +1 -1
- package/test/units/static/JBController/TestSetUriOf.sol +1 -1
- package/test/units/static/JBController/TestTransferCreditsFrom.sol +1 -1
- package/test/units/static/JBDeadline/TestDeadlineFuzz.sol +1 -1
- package/test/units/static/JBDirectory/JBDirectorySetup.sol +1 -1
- package/test/units/static/JBDirectory/TestPrimaryTerminalOf.sol +1 -1
- package/test/units/static/JBDirectory/TestSetControllerOf.sol +1 -1
- package/test/units/static/JBDirectory/TestSetControllerOfMigrationOrder.sol +1 -1
- package/test/units/static/JBDirectory/TestSetPrimaryTerminalOf.sol +1 -1
- package/test/units/static/JBDirectory/TestSetTerminalsOf.sol +1 -1
- package/test/units/static/JBERC20/JBERC20Setup.sol +11 -4
- package/test/units/static/JBERC20/SigUtils.sol +1 -1
- package/test/units/static/JBERC20/TestInitialize.sol +8 -1
- package/test/units/static/JBERC20/TestName.sol +1 -1
- package/test/units/static/JBERC20/TestNonces.sol +1 -1
- package/test/units/static/JBERC20/TestSymbol.sol +1 -1
- package/test/units/static/JBFeelessAdresses/JBFeelessSetup.sol +1 -1
- package/test/units/static/JBFeelessAdresses/TestInterfaces.sol +1 -1
- package/test/units/static/JBFeelessAdresses/TestSetFeelessAddress.sol +1 -1
- package/test/units/static/JBFees/TestFeesFuzz.sol +1 -1
- package/test/units/static/JBFixedPointNumber/TestAdjustDecimals.sol +1 -1
- package/test/units/static/JBFixedPointNumber/TestAdjustDecimalsFuzz.sol +1 -1
- package/test/units/static/JBFundAccessLimits/JBFundAccessSetup.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestFundAccessLimitsEdge.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestPayoutLimitOf.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestPayoutLimitsOf.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestSetFundAccessLimitsFor.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestSurplusAllowanceOf.sol +1 -1
- package/test/units/static/JBFundAccessLimits/TestSurplusAllowancesOf.sol +1 -1
- package/test/units/static/JBMetadataResolver/TestGetDataFor.sol +1 -1
- package/test/units/static/JBMetadataResolver/TestMetadataResolverEdgeCases.sol +1 -1
- package/test/units/static/JBMetadataResolver/TestMetadataResolverFuzz.sol +1 -1
- package/test/units/static/JBMultiTerminal/JBMultiTerminalSetup.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestAccountingContextsOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestAddAccountingContextsFor.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestAddToBalanceOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestMigrateBalanceOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestPay.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestPreviewCashOutFrom.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestPreviewPayFor.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +1 -1
- package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +1 -1
- package/test/units/static/JBPermissions/JBPermissionsSetup.sol +1 -1
- package/test/units/static/JBPermissions/TestHasPermission.sol +1 -1
- package/test/units/static/JBPermissions/TestHasPermissions.sol +1 -1
- package/test/units/static/JBPermissions/TestSetPermissionsFor.sol +1 -1
- package/test/units/static/JBPrices/JBPricesSetup.sol +1 -1
- package/test/units/static/JBPrices/TestAddPriceFeedFor.sol +1 -1
- package/test/units/static/JBPrices/TestPricePerUnitOf.sol +1 -1
- package/test/units/static/JBPrices/TestPrices.sol +1 -1
- package/test/units/static/JBProjects/JBProjectsSetup.sol +1 -1
- package/test/units/static/JBProjects/TestCreateFor.sol +1 -1
- package/test/units/static/JBProjects/TestInitialProject.sol +1 -1
- package/test/units/static/JBProjects/TestInterfaces.sol +1 -1
- package/test/units/static/JBProjects/TestSetResolver.sol +1 -1
- package/test/units/static/JBProjects/TestTokenUri.sol +1 -1
- package/test/units/static/JBRulesetMetadataResolver/TestSetCashOutTaxRateTo.sol +1 -1
- package/test/units/static/JBRulesets/JBRulesetsSetup.sol +1 -1
- package/test/units/static/JBRulesets/TestCurrentApprovalStatusForLatestRulesetOf.sol +1 -1
- package/test/units/static/JBRulesets/TestCurrentOf.sol +1 -1
- package/test/units/static/JBRulesets/TestGetRulesetOf.sol +1 -1
- package/test/units/static/JBRulesets/TestLatestQueuedRulesetOf.sol +1 -1
- package/test/units/static/JBRulesets/TestRulesets.sol +1 -1
- package/test/units/static/JBRulesets/TestRulesetsOf.sol +1 -1
- package/test/units/static/JBRulesets/TestUpcomingRulesetOf.sol +1 -1
- package/test/units/static/JBRulesets/TestUpdateRulesetWeightCache.sol +1 -1
- package/test/units/static/JBSplits/JBSplitsSetup.sol +1 -1
- package/test/units/static/JBSplits/TestSelfManagedSplitGroups.sol +1 -1
- package/test/units/static/JBSplits/TestSetSplitGroupsOf.sol +1 -1
- package/test/units/static/JBSplits/TestSplitsLockedEdge.sol +1 -1
- package/test/units/static/JBSplits/TestSplitsOf.sol +1 -1
- package/test/units/static/JBSplits/TestSplitsPacking.sol +1 -1
- package/test/units/static/JBSurplus/TestSurplusFuzz.sol +1 -1
- package/test/units/static/JBTerminalStore/JBTerminalStoreSetup.sol +1 -1
- package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +1 -1
- package/test/units/static/JBTerminalStore/TestCurrentSurplusOf.sol +1 -1
- package/test/units/static/JBTerminalStore/TestCurrentTotalSurplusOf.sol +1 -1
- package/test/units/static/JBTerminalStore/TestPreviewCashOutFrom.sol +1 -1
- package/test/units/static/JBTerminalStore/TestPreviewPayFrom.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordPaymentFrom.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordPayoutFor.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordTerminalMigration.sol +1 -1
- package/test/units/static/JBTerminalStore/TestRecordUsedAllowanceOf.sol +1 -1
- package/test/units/static/JBTerminalStore/TestUint224Overflow.sol +1 -1
- package/test/units/static/JBTokens/JBTokensSetup.sol +1 -1
- package/test/units/static/JBTokens/TestBurnFrom.sol +1 -1
- package/test/units/static/JBTokens/TestClaimTokensFor.sol +1 -1
- package/test/units/static/JBTokens/TestDeployERC20ForUnits.sol +1 -1
- package/test/units/static/JBTokens/TestMintFor.sol +1 -1
- package/test/units/static/JBTokens/TestSetTokenFor.sol +1 -1
- package/test/units/static/JBTokens/TestTotalBalanceOf.sol +1 -1
- package/test/units/static/JBTokens/TestTotalSupplyOf.sol +1 -1
- package/test/units/static/JBTokens/TestTransferCreditsFrom.sol +1 -1
package/SKILLS.md
CHANGED
|
@@ -46,7 +46,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
46
46
|
| `setUriOf(uint256 projectId, string uri)` | Sets the project's metadata URI. |
|
|
47
47
|
| `transferCreditsFrom(address holder, uint256 projectId, address recipient, uint256 creditCount)` | Transfers credits between addresses (reverts if `pauseCreditTransfers` is set in ruleset). |
|
|
48
48
|
| `addPriceFeedFor(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency, IJBPriceFeed feed)` | Registers a price feed (requires `allowAddPriceFeed` in ruleset). |
|
|
49
|
-
| `
|
|
49
|
+
| `migrate(uint256 projectId, IERC165 to)` | Migrates the project to a new controller. Calls `beforeReceiveMigrationFrom`, `migrate`, updates directory, then `afterReceiveMigrationFrom`. |
|
|
50
50
|
| `currentRulesetOf(uint256 projectId)` | Returns the current ruleset and unpacked metadata. |
|
|
51
51
|
| `upcomingRulesetOf(uint256 projectId)` | Returns the upcoming ruleset and unpacked metadata. |
|
|
52
52
|
| `allRulesetsOf(uint256 projectId, uint256 startingId, uint256 size)` | Returns an array of rulesets with metadata, paginated. |
|
|
@@ -59,7 +59,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
59
59
|
| Function | What it does |
|
|
60
60
|
|----------|--------------|
|
|
61
61
|
| `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. |
|
|
62
|
-
| `cashOutTokensOf(address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, uint256 minTokensReclaimed, address beneficiary, bytes metadata)` | Burns project tokens and reclaims surplus terminal tokens via bonding curve. |
|
|
62
|
+
| `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. |
|
|
63
63
|
| `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. |
|
|
64
64
|
| `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. |
|
|
65
65
|
| `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. |
|
|
@@ -70,7 +70,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
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`. |
|
|
73
|
-
| `previewCashOutFrom(address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, address beneficiary, bytes metadata)` | Simulates a full cash out including bonding curve and data hook effects. Returns `(ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications)`. Delegates to `STORE.previewCashOutFrom`. |
|
|
73
|
+
| `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. Returns `(ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications)`. Delegates to `STORE.previewCashOutFrom`. |
|
|
74
74
|
| `heldFeesOf(uint256 projectId, address token, uint256 count)` | Returns up to `count` held fees for a project/token. |
|
|
75
75
|
|
|
76
76
|
### JBTerminalStore
|
|
@@ -78,9 +78,9 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
78
78
|
| Function | What it does |
|
|
79
79
|
|----------|--------------|
|
|
80
80
|
| `recordPaymentFrom(address payer, JBTokenAmount amount, uint256 projectId, address beneficiary, bytes metadata)` | Records a payment. Applies data hook if enabled. Returns ruleset, token count, hook specifications. |
|
|
81
|
-
| `recordPayoutFor(uint256 projectId,
|
|
82
|
-
| `recordCashOutFor(address holder, uint256 projectId, uint256 cashOutCount,
|
|
83
|
-
| `recordUsedAllowanceOf(uint256 projectId,
|
|
81
|
+
| `recordPayoutFor(uint256 projectId, address token, uint256 amount, uint256 currency)` | Records a payout. Enforces payout limits. Returns ruleset and amount paid out. |
|
|
82
|
+
| `recordCashOutFor(address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, bool beneficiaryIsFeeless, bytes metadata)` | Records a cash out. Computes reclaim via bonding curve. Returns ruleset, reclaim amount, tax rate, and hook specifications. |
|
|
83
|
+
| `recordUsedAllowanceOf(uint256 projectId, address token, uint256 amount, uint256 currency)` | Records surplus allowance usage. Enforces allowance limits. Returns ruleset and used amount. |
|
|
84
84
|
| `recordAddedBalanceFor(uint256 projectId, address token, uint256 amount)` | Records funds added to a project's balance. |
|
|
85
85
|
| `recordTerminalMigration(uint256 projectId, address token)` | Records a terminal migration, returning the full balance. |
|
|
86
86
|
| `balanceOf(address terminal, uint256 projectId, address token)` | Returns the balance of a project at a terminal for a given token. |
|
|
@@ -101,7 +101,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
101
101
|
| `currentOf(uint256 projectId)` | Returns the currently active ruleset with decayed weight and correct cycle number. |
|
|
102
102
|
| `latestQueuedOf(uint256 projectId)` | Returns the latest queued ruleset and its approval status. |
|
|
103
103
|
| `queueFor(uint256 projectId, uint256 duration, uint256 weight, uint256 weightCutPercent, IJBRulesetApprovalHook approvalHook, uint256 metadata, uint256 mustStartAtOrAfter)` | Queues a new ruleset. Only callable by the project's controller. |
|
|
104
|
-
| `updateRulesetWeightCache(uint256 projectId)` | Updates the weight cache for long-running rulesets. Required when `weightCutMultiple > 20,000` to avoid gas limits. |
|
|
104
|
+
| `updateRulesetWeightCache(uint256 projectId, uint256 rulesetId)` | Updates the weight cache for long-running rulesets. Required when `weightCutMultiple > 20,000` to avoid gas limits. |
|
|
105
105
|
|
|
106
106
|
### JBPermissions
|
|
107
107
|
|
|
@@ -186,8 +186,8 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
186
186
|
| `JBBeforeCashOutRecordedContext` | `terminal`, `holder`, `projectId`, `rulesetId`, `cashOutCount`, `totalSupply`, `surplus (JBTokenAmount)`, `useTotalSurplus`, `cashOutTaxRate`, `beneficiaryIsFeeless`, `metadata` | `IJBRulesetDataHook.beforeCashOutRecordedWith()` input |
|
|
187
187
|
| `JBAfterPayRecordedContext` | `payer`, `projectId`, `rulesetId`, `amount (JBTokenAmount)`, `forwardedAmount (JBTokenAmount)`, `weight`, `newlyIssuedTokenCount`, `beneficiary`, `hookMetadata`, `payerMetadata` | `IJBPayHook.afterPayRecordedWith()` input |
|
|
188
188
|
| `JBAfterCashOutRecordedContext` | `holder`, `projectId`, `rulesetId`, `cashOutCount`, `reclaimedAmount (JBTokenAmount)`, `forwardedAmount (JBTokenAmount)`, `cashOutTaxRate`, `beneficiary`, `hookMetadata`, `cashOutMetadata` | `IJBCashOutHook.afterCashOutRecordedWith()` input |
|
|
189
|
-
| `JBPayHookSpecification` | `hook (IJBPayHook)`, `amount`, `metadata` | Returned by data hook; specifies which pay hooks to call and how much to forward |
|
|
190
|
-
| `JBCashOutHookSpecification` | `hook (IJBCashOutHook)`, `amount`, `metadata` | Returned by data hook; specifies which cash out hooks to call and how much to forward |
|
|
189
|
+
| `JBPayHookSpecification` | `hook (IJBPayHook)`, `noop (bool)`, `amount`, `metadata` | Returned by data hook; specifies which pay hooks to call and how much to forward. `noop = true` means informational-only (callback skipped, amount must be 0). |
|
|
190
|
+
| `JBCashOutHookSpecification` | `hook (IJBCashOutHook)`, `noop (bool)`, `amount`, `metadata` | Returned by data hook; specifies which cash out hooks to call and how much to forward. `noop = true` means informational-only (callback skipped, amount must be 0). |
|
|
191
191
|
| `JBSplitHookContext` | `token`, `amount`, `decimals`, `projectId`, `groupId`, `split (JBSplit)` | `IJBSplitHook.processSplitWith()` input |
|
|
192
192
|
|
|
193
193
|
### Constants (`JBConstants`)
|
|
@@ -244,7 +244,7 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
244
244
|
- `JBERC20` is cloned via `Clones.clone()` -- its constructor sets invalid name/symbol; real values set in `initialize()`
|
|
245
245
|
- Fee is 2.5% (`FEE = 25` out of `MAX_FEE = 1000`)
|
|
246
246
|
- Project #1 is the fee beneficiary project (receives all protocol fees)
|
|
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.
|
|
247
|
+
- **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.
|
|
248
248
|
- `JBProjects` constructor optionally mints project #1 to `feeProjectOwner` -- if `address(0)`, no fee project is created
|
|
249
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`
|
|
@@ -265,12 +265,159 @@ The core Juicebox V6 protocol on EVM: a modular system for launching treasury-ba
|
|
|
265
265
|
- **`JBAccountingContext.currency` is NOT `baseCurrency` — by design.** `baseCurrency` in ruleset metadata uses abstract real-world values (1 = ETH, 2 = USD) so rulesets are portable across chains — `baseCurrency=2` means "issue X tokens per USD" whether on Ethereum, Base, or Arbitrum. `JBAccountingContext.currency` uses token-derived values (`uint32(uint160(tokenAddress))`) because terminals track specific tokens at specific addresses — e.g. NATIVE_TOKEN = 61166, USDC on Ethereum = 909516616, USDC on Base = 3169378579. `JBPrices` mediates between the two: it converts token-derived currencies to/from abstract currencies (e.g. USDC token → USD concept, NATIVE_TOKEN → ETH concept) so that payout limits denominated in USD work correctly regardless of which token the terminal holds. The separation is what makes cross-chain consistency possible: same ruleset, different terminal accounting per chain.
|
|
266
266
|
- **Don't queue multiple identical rulesets.** A ruleset with a `duration` automatically cycles — no need to queue copies. Queue multiple rulesets only when configuration actually changes between periods (e.g. different weight, splits, or limits).
|
|
267
267
|
- **`NATIVE_TOKEN` represents a different token on each chain.** `NATIVE_TOKEN` (`0x000000000000000000000000000000000000EEEe`) is the token received via `msg.value` — ETH on Ethereum/Base/Optimism/Arbitrum, CELO on Celo, etc. Its currency is `uint32(uint160(NATIVE_TOKEN))` = 61166. A `JBMatchingPriceFeed` (returns 1:1) is deployed for `ETH:NATIVE_TOKEN` on ETH-native chains so that `baseCurrency=ETH` resolves correctly to the native token. On non-ETH-native chains, a different price feed would be needed.
|
|
268
|
+
- **Noop hook specifications are informational-only.** `noop = true` + `amount != 0` reverts with `JBTerminalStore_NoopHookSpecHasAmount`. Data hooks use noop specs to return diagnostics to preview clients without triggering a hook callback. The `noop` flag only suppresses the callback — parameter overrides (weight, tax rate, supply) from the data hook still apply.
|
|
269
|
+
|
|
270
|
+
## Permission IDs
|
|
271
|
+
|
|
272
|
+
Quick-reference for the most common `JBPermissionIds` values (from `@bananapus/permission-ids-v6`). Pass these to `JBPermissions.setPermissionsFor()`.
|
|
273
|
+
|
|
274
|
+
| ID | Name | Gates |
|
|
275
|
+
|----|------|-------|
|
|
276
|
+
| `1` | `ROOT` | All permissions for the scoped project. Cannot be combined with `projectId = 0`. |
|
|
277
|
+
| `2` | `QUEUE_RULESETS` | `JBController.queueRulesetsOf` |
|
|
278
|
+
| `3` | `LAUNCH_RULESETS` | `JBController.launchRulesetsFor` |
|
|
279
|
+
| `4` | `CASH_OUT_TOKENS` | `JBMultiTerminal.cashOutTokensOf` |
|
|
280
|
+
| `5` | `SEND_PAYOUTS` | `JBMultiTerminal.sendPayoutsOf` |
|
|
281
|
+
| `6` | `MIGRATE_TERMINAL` | `JBMultiTerminal.migrateBalanceOf` |
|
|
282
|
+
| `7` | `SET_PROJECT_URI` | `JBController.setUriOf` |
|
|
283
|
+
| `8` | `DEPLOY_ERC20` | `JBController.deployERC20For` |
|
|
284
|
+
| `9` | `SET_TOKEN` | `JBController.setTokenFor` |
|
|
285
|
+
| `10` | `MINT_TOKENS` | `JBController.mintTokensOf` |
|
|
286
|
+
| `11` | `BURN_TOKENS` | `JBController.burnTokensOf` |
|
|
287
|
+
| `12` | `CLAIM_TOKENS` | `JBController.claimTokensFor` |
|
|
288
|
+
| `13` | `TRANSFER_CREDITS` | `JBController.transferCreditsFrom` |
|
|
289
|
+
| `14` | `SET_CONTROLLER` | `JBDirectory.setControllerOf` |
|
|
290
|
+
| `15` | `SET_TERMINALS` | `JBDirectory.setTerminalsOf` (can remove primary terminal) |
|
|
291
|
+
| `16` | `SET_PRIMARY_TERMINAL` | `JBDirectory.setPrimaryTerminalOf` |
|
|
292
|
+
| `17` | `USE_ALLOWANCE` | `JBMultiTerminal.useAllowanceOf` |
|
|
293
|
+
| `18` | `SET_SPLIT_GROUPS` | `JBController.setSplitGroupsOf` |
|
|
294
|
+
| `19` | `ADD_PRICE_FEED` | `JBController.addPriceFeedFor` |
|
|
295
|
+
| `20` | `ADD_ACCOUNTING_CONTEXTS` | `JBMultiTerminal.addAccountingContextsFor` |
|
|
296
|
+
| `21` | `SET_TOKEN_METADATA` | `JBController.setTokenMetadataOf` |
|
|
297
|
+
|
|
298
|
+
IDs 22-33 are used by extension contracts (721 hook, buyback hook, router terminal, suckers).
|
|
299
|
+
|
|
300
|
+
## Common Errors
|
|
301
|
+
|
|
302
|
+
Errors an agent is most likely to encounter. All are custom errors (revert with selector).
|
|
303
|
+
|
|
304
|
+
| Error | Contract | When |
|
|
305
|
+
|-------|----------|------|
|
|
306
|
+
| `JBPermissioned_Unauthorized` | `JBPermissioned` | Caller lacks the required permission ID for the project. |
|
|
307
|
+
| `JBController_RulesetsArrayEmpty` | `JBController` | `launchProjectFor` / `queueRulesetsOf` called with empty rulesets array. |
|
|
308
|
+
| `JBController_RulesetsAlreadyLaunched` | `JBController` | `launchRulesetsFor` called on a project that already has rulesets. |
|
|
309
|
+
| `JBController_MintNotAllowedAndNotTerminalOrHook` | `JBController` | `mintTokensOf` called but `allowOwnerMinting` is false and caller is not a terminal/hook. |
|
|
310
|
+
| `JBController_ZeroTokensToMint` | `JBController` | `mintTokensOf` called with `tokenCount = 0`. |
|
|
311
|
+
| `JBController_ZeroTokensToBurn` | `JBController` | `burnTokensOf` called with `tokenCount = 0`. |
|
|
312
|
+
| `JBController_NoReservedTokens` | `JBController` | `sendReservedTokensToSplitsOf` called but no pending reserved tokens. |
|
|
313
|
+
| `JBController_CreditTransfersPaused` | `JBController` | `transferCreditsFrom` called but `pauseCreditTransfers` is set in ruleset. |
|
|
314
|
+
| `JBController_RulesetSetTokenNotAllowed` | `JBController` | `setTokenFor` called but `allowSetCustomToken` is false in ruleset. |
|
|
315
|
+
| `JBController_AddingPriceFeedNotAllowed` | `JBController` | `addPriceFeedFor` called but `allowAddPriceFeed` is false in ruleset. |
|
|
316
|
+
| `JBController_InvalidReservedPercent` | `JBController` | `reservedPercent` exceeds `MAX_RESERVED_PERCENT` (10,000). |
|
|
317
|
+
| `JBController_InvalidCashOutTaxRate` | `JBController` | `cashOutTaxRate` exceeds `MAX_CASH_OUT_TAX_RATE` (10,000). |
|
|
318
|
+
| `JBMultiTerminal_UnderMinReturnedTokens` | `JBMultiTerminal` | Payment minted fewer tokens than `minReturnedTokens`. |
|
|
319
|
+
| `JBMultiTerminal_UnderMinTokensReclaimed` | `JBMultiTerminal` | Cash out reclaimed less than `minTokensReclaimed`. |
|
|
320
|
+
| `JBMultiTerminal_UnderMinTokensPaidOut` | `JBMultiTerminal` | Payout distributed less than `minTokensPaidOut`. |
|
|
321
|
+
| `JBMultiTerminal_TokenNotAccepted` | `JBMultiTerminal` | Token has no accounting context for the project in this terminal. |
|
|
322
|
+
| `JBMultiTerminal_NoMsgValueAllowed` | `JBMultiTerminal` | `msg.value > 0` sent with an ERC-20 payment (not `NATIVE_TOKEN`). |
|
|
323
|
+
| `JBMultiTerminal_PermitAllowanceNotEnough` | `JBMultiTerminal` | Permit2 allowance insufficient for the payment amount. |
|
|
324
|
+
| `JBTerminalStore_RulesetPaymentPaused` | `JBTerminalStore` | `pausePay` is set in the current ruleset. |
|
|
325
|
+
| `JBTerminalStore_RulesetNotFound` | `JBTerminalStore` | No ruleset exists for the project (not launched). |
|
|
326
|
+
| `JBTerminalStore_InadequateControllerPayoutLimit` | `JBTerminalStore` | `sendPayoutsOf` amount exceeds the payout limit for this cycle. |
|
|
327
|
+
| `JBTerminalStore_InadequateControllerAllowance` | `JBTerminalStore` | `useAllowanceOf` amount exceeds the surplus allowance. |
|
|
328
|
+
| `JBTerminalStore_InadequateTerminalStoreBalance` | `JBTerminalStore` | Withdrawal exceeds the terminal's recorded balance. |
|
|
329
|
+
| `JBTerminalStore_InsufficientTokens` | `JBTerminalStore` | Cash out count exceeds the holder's token balance. |
|
|
330
|
+
| `JBTerminalStore_TerminalMigrationNotAllowed` | `JBTerminalStore` | `migrateBalanceOf` called but `allowTerminalMigration` is false. |
|
|
331
|
+
| `JBTerminalStore_NoopHookSpecHasAmount` | `JBTerminalStore` | Data hook returned a noop spec with `amount != 0`. |
|
|
332
|
+
| `JBTerminalStore_AccountingContextAlreadySet` | `JBTerminalStore` | Accounting context already exists for that token. |
|
|
333
|
+
| `JBDirectory_SetControllerNotAllowed` | `JBDirectory` | Controller change not allowed by the current ruleset. |
|
|
334
|
+
| `JBDirectory_SetTerminalsNotAllowed` | `JBDirectory` | Terminal change not allowed by the current ruleset. |
|
|
335
|
+
| `JBDirectory_DuplicateTerminals` | `JBDirectory` | Duplicate terminal in the terminals array. |
|
|
336
|
+
| `JBTokens_ProjectAlreadyHasToken` | `JBTokens` | `deployERC20For` / `setTokenFor` called but project already has an ERC-20. |
|
|
337
|
+
| `JBTokens_InsufficientCredits` | `JBTokens` | `claimTokensFor` count exceeds credit balance. |
|
|
338
|
+
| `JBTokens_TokensMustHave18Decimals` | `JBTokens` | Custom token does not use 18 decimals. |
|
|
339
|
+
| `JBSplits_TotalPercentExceeds100` | `JBSplits` | Split percentages sum exceeds `SPLITS_TOTAL_PERCENT`. |
|
|
340
|
+
| `JBPrices_PriceFeedAlreadyExists` | `JBPrices` | Feed already set for that currency pair (immutable). |
|
|
341
|
+
| `JBPrices_PriceFeedNotFound` | `JBPrices` | No feed found for the requested currency pair. |
|
|
342
|
+
| `JBPermissions_CantSetRootPermissionForWildcardProject` | `JBPermissions` | Tried to grant ROOT with `projectId = 0` (wildcard). |
|
|
343
|
+
| `JBRulesets_InvalidWeight` | `JBRulesets` | Weight exceeds `uint112.max`. |
|
|
344
|
+
| `JBRulesets_InvalidWeightCutPercent` | `JBRulesets` | `weightCutPercent` exceeds `MAX_WEIGHT_CUT_PERCENT`. |
|
|
345
|
+
| `JBFundAccessLimits_InvalidPayoutLimitCurrencyOrdering` | `JBFundAccessLimits` | Payout limit currencies not in strictly increasing order. |
|
|
346
|
+
|
|
347
|
+
## Key Events
|
|
348
|
+
|
|
349
|
+
The most important events for indexing and off-chain monitoring. Indexed params marked with `*`.
|
|
350
|
+
|
|
351
|
+
| Event | Interface | Key Params |
|
|
352
|
+
|-------|-----------|------------|
|
|
353
|
+
| `Pay` | `IJBTerminal` | `rulesetId*`, `rulesetCycleNumber*`, `projectId*`, `payer`, `beneficiary`, `amount`, `newlyIssuedTokenCount` |
|
|
354
|
+
| `CashOutTokens` | `IJBCashOutTerminal` | `rulesetId*`, `rulesetCycleNumber*`, `projectId*`, `holder`, `beneficiary`, `cashOutCount`, `cashOutTaxRate`, `reclaimAmount` |
|
|
355
|
+
| `SendPayouts` | `IJBPayoutTerminal` | `rulesetId*`, `rulesetCycleNumber*`, `projectId*`, `projectOwner`, `amount`, `amountPaidOut`, `fee`, `netLeftoverPayoutAmount` |
|
|
356
|
+
| `SendPayoutToSplit` | `IJBPayoutTerminal` | `projectId*`, `rulesetId*`, `group*`, `split`, `amount`, `netAmount` |
|
|
357
|
+
| `UseAllowance` | `IJBPayoutTerminal` | `rulesetId*`, `rulesetCycleNumber*`, `projectId*`, `beneficiary`, `amount`, `netAmountPaidOut` |
|
|
358
|
+
| `MintTokens` | `IJBController` | `beneficiary*`, `projectId*`, `tokenCount`, `beneficiaryTokenCount`, `reservedPercent` |
|
|
359
|
+
| `BurnTokens` | `IJBController` | `holder*`, `projectId*`, `tokenCount` |
|
|
360
|
+
| `SendReservedTokensToSplits` | `IJBController` | `rulesetId*`, `rulesetCycleNumber*`, `projectId*`, `owner`, `tokenCount`, `leftoverAmount` |
|
|
361
|
+
| `SendReservedTokensToSplit` | `IJBController` | `projectId*`, `rulesetId*`, `groupId*`, `split`, `tokenCount` |
|
|
362
|
+
| `LaunchProject` | `IJBController` | `rulesetId`, `projectId`, `projectUri` |
|
|
363
|
+
| `QueueRulesets` | `IJBController` | `rulesetId`, `projectId` |
|
|
364
|
+
| `DeployERC20` | `IJBController` | `projectId*`, `deployer*`, `salt`, `saltHash`, `caller` |
|
|
365
|
+
| `SetUri` | `IJBController` | `projectId*`, `uri` |
|
|
366
|
+
| `AddToBalance` | `IJBTerminal` | `projectId*`, `amount`, `returnedFees` |
|
|
367
|
+
| `MigrateTerminal` | `IJBTerminal` | `projectId*`, `token*`, `to*`, `amount` |
|
|
368
|
+
| `HoldFee` | `IJBFeeTerminal` | `projectId*`, `token*`, `amount*`, `fee`, `beneficiary` |
|
|
369
|
+
| `ProcessFee` | `IJBFeeTerminal` | `projectId*`, `token*`, `amount*`, `wasHeld`, `beneficiary` |
|
|
370
|
+
| `ReturnHeldFees` | `IJBFeeTerminal` | `projectId*`, `token*`, `amount*`, `returnedFees`, `leftoverAmount` |
|
|
371
|
+
| `Create` | `IJBProjects` | `projectId*`, `owner*` |
|
|
372
|
+
| `OperatorPermissionsSet` | `IJBPermissions` | (operator, account, projectId, permissionIds, packed, caller) |
|
|
373
|
+
| `RulesetQueued` | `IJBRulesets` | (rulesetId, projectId, duration, weight, weightCutPercent, approvalHook, metadata, mustStartAtOrAfter, caller) |
|
|
374
|
+
| `SetSplit` | `IJBSplits` | (projectId, rulesetId, groupId, split, caller) |
|
|
375
|
+
| `AddPriceFeed` | `IJBPrices` | (projectId, pricingCurrency, unitCurrency, feed, caller) |
|
|
376
|
+
|
|
377
|
+
## Hook Interface Return Types
|
|
378
|
+
|
|
379
|
+
### `IJBRulesetDataHook.beforePayRecordedWith()`
|
|
380
|
+
|
|
381
|
+
```solidity
|
|
382
|
+
function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
|
|
383
|
+
external view
|
|
384
|
+
returns (
|
|
385
|
+
uint256 weight, // Overrides the ruleset's weight for token issuance
|
|
386
|
+
JBPayHookSpecification[] memory hookSpecifications // Pay hooks to call + amounts to forward
|
|
387
|
+
);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
The data hook can override `weight` to change how many tokens the payer receives. Return `hookSpecifications` to redirect funds to pay hooks (each spec has `hook`, `amount`, `metadata`, and `noop`). An empty array means all funds stay in the terminal balance.
|
|
391
|
+
|
|
392
|
+
### `IJBRulesetDataHook.beforeCashOutRecordedWith()`
|
|
393
|
+
|
|
394
|
+
```solidity
|
|
395
|
+
function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
|
|
396
|
+
external view
|
|
397
|
+
returns (
|
|
398
|
+
uint256 cashOutTaxRate, // Overrides the ruleset's cash out tax rate
|
|
399
|
+
uint256 cashOutCount, // Overrides the number of tokens being cashed out
|
|
400
|
+
uint256 totalSupply, // Overrides total supply for bonding curve calc
|
|
401
|
+
JBCashOutHookSpecification[] memory hookSpecifications // Cash out hooks to call + amounts to forward
|
|
402
|
+
);
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
The data hook can override `cashOutTaxRate` (0 = proportional, 10000 = nothing reclaimable), `cashOutCount` and `totalSupply` (to shift the bonding curve), and return `hookSpecifications` to redirect reclaimed funds to cash out hooks.
|
|
406
|
+
|
|
407
|
+
### `IJBRulesetDataHook.hasMintPermissionFor()`
|
|
408
|
+
|
|
409
|
+
```solidity
|
|
410
|
+
function hasMintPermissionFor(uint256 projectId, JBRuleset memory ruleset, address addr)
|
|
411
|
+
external view returns (bool flag);
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Returns whether `addr` is allowed to mint tokens for the project. Called by `JBController.mintTokensOf` when the caller is not the owner and `allowOwnerMinting` is false -- the data hook can grant mint permission to specific addresses (e.g. suckers for omnichain bridging).
|
|
268
415
|
|
|
269
416
|
## Example Integration
|
|
270
417
|
|
|
271
418
|
```solidity
|
|
272
419
|
// SPDX-License-Identifier: MIT
|
|
273
|
-
pragma solidity 0.8.
|
|
420
|
+
pragma solidity 0.8.28;
|
|
274
421
|
|
|
275
422
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
276
423
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
package/STYLE_GUIDE.md
CHANGED
|
@@ -21,7 +21,7 @@ One contract/interface/struct/enum per file. Name the file after the type it con
|
|
|
21
21
|
|
|
22
22
|
```solidity
|
|
23
23
|
// Contracts — pin to exact version
|
|
24
|
-
pragma solidity 0.8.
|
|
24
|
+
pragma solidity 0.8.28;
|
|
25
25
|
|
|
26
26
|
// Interfaces, structs, enums — caret for forward compatibility
|
|
27
27
|
pragma solidity ^0.8.0;
|
|
@@ -112,6 +112,10 @@ contract JBExample is JBPermissioned, IJBExample {
|
|
|
112
112
|
// ----------------------- external views ---------------------------- //
|
|
113
113
|
//*********************************************************************//
|
|
114
114
|
|
|
115
|
+
//*********************************************************************//
|
|
116
|
+
// -------------------------- public views --------------------------- //
|
|
117
|
+
//*********************************************************************//
|
|
118
|
+
|
|
115
119
|
//*********************************************************************//
|
|
116
120
|
// ----------------------- public transactions ----------------------- //
|
|
117
121
|
//*********************************************************************//
|
|
@@ -141,10 +145,11 @@ contract JBExample is JBPermissioned, IJBExample {
|
|
|
141
145
|
8. Constructor
|
|
142
146
|
9. External transactions
|
|
143
147
|
10. External views
|
|
144
|
-
11. Public
|
|
145
|
-
12.
|
|
146
|
-
13. Internal
|
|
147
|
-
14.
|
|
148
|
+
11. Public views
|
|
149
|
+
12. Public transactions
|
|
150
|
+
13. Internal helpers
|
|
151
|
+
14. Internal views
|
|
152
|
+
15. Private helpers
|
|
148
153
|
|
|
149
154
|
Functions are alphabetized within each section.
|
|
150
155
|
|
|
@@ -326,7 +331,7 @@ Standard config across all repos:
|
|
|
326
331
|
|
|
327
332
|
```toml
|
|
328
333
|
[profile.default]
|
|
329
|
-
solc = '0.8.
|
|
334
|
+
solc = '0.8.28'
|
|
330
335
|
evm_version = 'cancun'
|
|
331
336
|
optimizer_runs = 200
|
|
332
337
|
libs = ["node_modules", "lib"]
|
package/USER_JOURNEYS.md
CHANGED
|
@@ -89,15 +89,15 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
89
89
|
- `metadata` -- Hook-specific data
|
|
90
90
|
|
|
91
91
|
**State changes**:
|
|
92
|
-
1. `JBTerminalStore.balanceOf[terminal][projectId][token]`
|
|
93
|
-
2. Project tokens burned via `JBController.burnTokensOf()` (credits first, then ERC-20)
|
|
92
|
+
1. `STORE.recordCashOutFor()` -- computes the reclaim amount via bonding curve (using current `totalSupply` which still includes the tokens being cashed out), then decrements `JBTerminalStore.balanceOf[terminal][projectId][token]` by `reclaimAmount + hookSpec amounts`
|
|
93
|
+
2. Project tokens burned via `JBController.burnTokensOf()` (credits first, then ERC-20) -- happens AFTER the reclaim calculation, so the bonding curve sees the pre-burn supply
|
|
94
94
|
3. Reclaimed tokens transferred to beneficiary
|
|
95
95
|
4. Cash out hooks execute (if data hook returns specifications)
|
|
96
|
-
5. Fee taken (2.5%) on total amount eligible for fees, unless beneficiary is feeless. When `cashOutTaxRate == 0`, the fee applies only up to the project's unconsumed fee-free surplus (`_feeFreeSurplusOf`) from intra-terminal payouts
|
|
96
|
+
5. Fee taken (2.5%) on total amount eligible for fees, unless beneficiary is feeless. When `cashOutTaxRate == 0`, the fee applies only up to the project's unconsumed fee-free surplus (`_feeFreeSurplusOf`) from intra-terminal payouts -- once depleted, cashouts are fee-free.
|
|
97
97
|
|
|
98
98
|
**Events**: `CashOutTokens(rulesetId, rulesetCycleNumber, projectId, holder, beneficiary, cashOutCount, cashOutTaxRate, reclaimAmount, metadata, caller)`
|
|
99
99
|
|
|
100
|
-
**Preview**: Call `JBMultiTerminal.previewCashOutFrom(holder, projectId, cashOutCount, tokenToReclaim, beneficiary, metadata)` to simulate the full cash out on-chain -- including data hook effects on tax rate, supply, and hook specifications. Returns `(ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications)`. This is a `view` function that does not modify state. The terminal delegates to `JBTerminalStore.previewCashOutFrom(holder, projectId, cashOutCount,
|
|
100
|
+
**Preview**: Call `JBMultiTerminal.previewCashOutFrom(holder, projectId, cashOutCount, tokenToReclaim, beneficiary, metadata)` to simulate the full cash out on-chain -- including data hook effects on tax rate, supply, and hook specifications. Returns `(ruleset, reclaimAmount, cashOutTaxRate, hookSpecifications)`. This is a `view` function that does not modify state. The terminal delegates to `JBTerminalStore.previewCashOutFrom(terminal, holder, projectId, cashOutCount, tokenToReclaim, beneficiaryIsFeeless, metadata)` for the bonding curve computation. For a simpler estimate without data hook effects, use `currentTotalReclaimableSurplusOf(projectId, cashOutCount, decimals, currency)` or the 6-param `currentReclaimableSurplusOf` overload.
|
|
101
101
|
|
|
102
102
|
**Edge cases**:
|
|
103
103
|
- `cashOutCount = 0` with `totalSupply = 0` -- returns entire surplus (C-5 known bug)
|
|
@@ -169,7 +169,7 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
169
169
|
- `usedAmount > surplus` -- reverts (`InadequateTerminalStoreBalance`)
|
|
170
170
|
- Surplus allowance resets each ruleset (keyed by `rulesetId`, not `cycleNumber`)
|
|
171
171
|
- Amount validated against surplus BEFORE checking allowance limit
|
|
172
|
-
- If
|
|
172
|
+
- If either the owner or the beneficiary is feeless, no fee is taken
|
|
173
173
|
|
|
174
174
|
---
|
|
175
175
|
|
|
@@ -277,7 +277,7 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
277
277
|
1. `JBSplits` stores the new split groups for the project/ruleset/group combination
|
|
278
278
|
2. Locked splits from existing configuration must be preserved (validated by `JBSplits`)
|
|
279
279
|
|
|
280
|
-
**Events**:
|
|
280
|
+
**Events**: `SetSplit(projectId, rulesetId, groupId, split, caller)` per split (emitted by `JBSplits`, not `JBController`)
|
|
281
281
|
|
|
282
282
|
**Edge cases**:
|
|
283
283
|
- Locked splits (`lockedUntil > block.timestamp`) cannot be removed or modified
|
|
@@ -321,15 +321,19 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
321
321
|
|
|
322
322
|
**Who can call**: Project owner, address with `SET_CONTROLLER` permission, or an address in `isAllowedToSetFirstController` (for first controller only). The current controller's ruleset must have `allowSetController` enabled.
|
|
323
323
|
|
|
324
|
+
**Parameters**:
|
|
325
|
+
- `projectId` -- The project being migrated
|
|
326
|
+
- `controller` -- The new controller (must support `IERC165`)
|
|
327
|
+
|
|
324
328
|
**Flow**:
|
|
325
329
|
1. `JBDirectory.setControllerOf(projectId, newController)` is called
|
|
326
|
-
2.
|
|
327
|
-
- Copies metadata URI from old controller
|
|
328
|
-
- Distributes pending reserved tokens from old controller
|
|
329
|
-
3.
|
|
330
|
+
2. If old controller exists AND new controller supports `IJBMigratable`: directory calls `newController.beforeReceiveMigrationFrom(oldController, projectId)`:
|
|
331
|
+
- Copies metadata URI from old controller (if it supports `IJBProjectUriRegistry`)
|
|
332
|
+
- Distributes pending reserved tokens from old controller (if it supports `IJBController` and has pending tokens)
|
|
333
|
+
3. If old controller exists AND old controller supports `IJBMigratable`: directory calls `oldController.migrate(projectId, newController)`:
|
|
330
334
|
- Reverts if pending reserved tokens > 0 (must distribute first)
|
|
331
335
|
4. Directory updates `controllerOf[projectId] = newController`
|
|
332
|
-
5.
|
|
336
|
+
5. If old controller exists AND new controller supports `IJBMigratable`: directory calls `newController.afterReceiveMigrationFrom(oldController, projectId)` (currently a no-op; verifies caller is the directory)
|
|
333
337
|
|
|
334
338
|
**Events**: `Migrate(projectId, to, caller)` from old controller; `SetController(projectId, controller, caller)` from directory
|
|
335
339
|
|
|
@@ -358,7 +362,7 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
358
362
|
2. Clone's `initialize(name, symbol, owner=JBTokens)` is called
|
|
359
363
|
3. `JBTokens.tokenOf[projectId]` set to the new token
|
|
360
364
|
|
|
361
|
-
**Events**: `DeployERC20(projectId, deployer, salt, saltHash, caller)`
|
|
365
|
+
**Events**: `DeployERC20(projectId, deployer, salt, saltHash, caller)` from `JBController`, `DeployERC20(projectId, token, name, symbol, salt, caller)` from `JBTokens`
|
|
362
366
|
|
|
363
367
|
**Edge cases**:
|
|
364
368
|
- Can only be called once per project. If a token is already set, `JBTokens` reverts.
|
|
@@ -383,6 +387,8 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
383
387
|
1. `JBTokens.creditBalanceOf[holder][projectId]` decreased by `tokenCount`
|
|
384
388
|
2. ERC-20 tokens minted to `beneficiary` for `tokenCount`
|
|
385
389
|
|
|
390
|
+
**Events**: `ClaimTokens(holder, projectId, creditBalance, count, beneficiary, caller)` (emitted by `JBTokens`)
|
|
391
|
+
|
|
386
392
|
**Edge cases**:
|
|
387
393
|
- Requires an ERC-20 token to be deployed for the project (reverts otherwise)
|
|
388
394
|
- Credits and ERC-20 tokens are fungible -- this is a one-way conversion from internal credits to on-chain ERC-20
|
|
@@ -421,7 +427,7 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
421
427
|
|
|
422
428
|
## 15. Add Price Feeds
|
|
423
429
|
|
|
424
|
-
**Entry point**: `JBController.
|
|
430
|
+
**Entry point**: `JBController.addPriceFeedFor(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency, IJBPriceFeed feed)`
|
|
425
431
|
|
|
426
432
|
**Who can call**: Project owner or address with `ADD_PRICE_FEED` permission.
|
|
427
433
|
|
|
@@ -435,7 +441,7 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
435
441
|
1. `JBPrices` stores the feed for the `(projectId, pricingCurrency, unitCurrency)` triple
|
|
436
442
|
2. Feed is **immutable** once set -- cannot be replaced or removed
|
|
437
443
|
|
|
438
|
-
**Events**:
|
|
444
|
+
**Events**: `AddPriceFeed(projectId, pricingCurrency, unitCurrency, feed, caller)` (emitted by `JBPrices`)
|
|
439
445
|
|
|
440
446
|
**Edge cases**:
|
|
441
447
|
- Requires `allowAddPriceFeed` in current ruleset
|
|
@@ -488,6 +494,8 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
488
494
|
2. All `PROJECTS.ownerOf(projectId)` calls now return the new owner
|
|
489
495
|
3. All permission checks that reference the owner now apply to the new owner
|
|
490
496
|
|
|
497
|
+
**Events**: `Transfer(from, to, tokenId)` (standard ERC-721 event)
|
|
498
|
+
|
|
491
499
|
**Edge cases**:
|
|
492
500
|
- This is a standard ERC-721 transfer. All ERC-721 rules apply (approval, operator, etc.)
|
|
493
501
|
- **Permissions are NOT transferred**. Existing operators retain their permissions scoped to the account that granted them (the old owner). The new owner must grant their own permissions.
|
|
@@ -539,7 +547,7 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
539
547
|
1. `JBTokens.creditBalanceOf[holder][projectId]` decreased
|
|
540
548
|
2. `JBTokens.creditBalanceOf[recipient][projectId]` increased
|
|
541
549
|
|
|
542
|
-
**Events**:
|
|
550
|
+
**Events**: `TransferCredits(holder, projectId, recipient, count, caller)` (emitted by `JBTokens`)
|
|
543
551
|
|
|
544
552
|
**Edge cases**:
|
|
545
553
|
- Reverts if `pauseCreditTransfers` is set in current ruleset
|
|
@@ -554,10 +562,18 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
554
562
|
|
|
555
563
|
**Who can call**: Project owner or address with `SET_PROJECT_URI` permission.
|
|
556
564
|
|
|
565
|
+
**Parameters**:
|
|
566
|
+
- `projectId` -- Target project
|
|
567
|
+
- `uri` -- Metadata URI (typically an IPFS hash)
|
|
568
|
+
|
|
557
569
|
**State changes**: `uriOf[projectId] = uri`
|
|
558
570
|
|
|
559
571
|
**Events**: `SetUri(projectId, uri, caller)`
|
|
560
572
|
|
|
573
|
+
**Edge cases**:
|
|
574
|
+
- Empty string is valid -- clears the metadata URI
|
|
575
|
+
- No ruleset flag required (always allowed with permission)
|
|
576
|
+
|
|
561
577
|
---
|
|
562
578
|
|
|
563
579
|
## 21. Set Custom Token
|
|
@@ -566,8 +582,14 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
566
582
|
|
|
567
583
|
**Who can call**: Project owner or address with `SET_TOKEN` permission.
|
|
568
584
|
|
|
585
|
+
**Parameters**:
|
|
586
|
+
- `projectId` -- Target project
|
|
587
|
+
- `token` -- The custom token contract (must implement `IJBToken`)
|
|
588
|
+
|
|
569
589
|
**State changes**: `JBTokens.tokenOf[projectId] = token`
|
|
570
590
|
|
|
591
|
+
**Events**: `SetToken(projectId, token, caller)` (emitted by `JBTokens`)
|
|
592
|
+
|
|
571
593
|
**Edge cases**:
|
|
572
594
|
- Requires `allowSetCustomToken` in current or upcoming ruleset
|
|
573
595
|
- Can only be called if no token is currently set for the project
|
|
@@ -581,7 +603,18 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
581
603
|
|
|
582
604
|
**Who can call**: Project owner or address with `SET_TOKEN_METADATA` permission.
|
|
583
605
|
|
|
584
|
-
**
|
|
606
|
+
**Parameters**:
|
|
607
|
+
- `projectId` -- Target project
|
|
608
|
+
- `name` -- New ERC-20 token name
|
|
609
|
+
- `symbol` -- New ERC-20 token symbol
|
|
610
|
+
|
|
611
|
+
**State changes**: Updates the ERC-20 token's name and symbol via `JBERC20.setMetadata()`
|
|
612
|
+
|
|
613
|
+
**Events**: `SetTokenMetadata(projectId, name, symbol, caller)` (emitted by `JBTokens`)
|
|
614
|
+
|
|
615
|
+
**Edge cases**:
|
|
616
|
+
- Requires an ERC-20 token to be deployed for the project (reverts if no token set)
|
|
617
|
+
- Only works with `JBERC20` clones (custom tokens that implement `IJBToken` may not support `setMetadata`)
|
|
585
618
|
|
|
586
619
|
---
|
|
587
620
|
|
|
@@ -615,6 +648,10 @@ All user paths through the Juicebox V6 core protocol. For each journey: entry po
|
|
|
615
648
|
|
|
616
649
|
**Who can call**: Project owner, address with `ADD_ACCOUNTING_CONTEXTS` permission, or the project's controller.
|
|
617
650
|
|
|
651
|
+
**Parameters**:
|
|
652
|
+
- `projectId` -- Target project
|
|
653
|
+
- `accountingContexts` -- Array of `JBAccountingContext` structs, each specifying a token address, decimals, and currency
|
|
654
|
+
|
|
618
655
|
**State changes**:
|
|
619
656
|
1. For each context: validates token decimals, stores `_accountingContextForTokenOf[projectId][token]`
|
|
620
657
|
2. Appends to `_accountingContextsOf[projectId]` array
|
package/foundry.toml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-core-v6'"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
29
|
+
"@bananapus/permission-ids-v6": "^0.0.11",
|
|
30
30
|
"@chainlink/contracts": "^1.3.0",
|
|
31
31
|
"@openzeppelin/contracts": "^5.6.1",
|
|
32
32
|
"@prb/math": "^4.1.1",
|
package/script/Deploy.s.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
5
5
|
import {Script} from "forge-std/Script.sol";
|
|
@@ -43,7 +43,7 @@ contract Deploy is Script, Sphinx {
|
|
|
43
43
|
uint256 private CORE_DEPLOYMENT_NONCE = 6;
|
|
44
44
|
|
|
45
45
|
function configureSphinx() public override {
|
|
46
|
-
sphinxConfig.projectName = "nana-core-
|
|
46
|
+
sphinxConfig.projectName = "nana-core-v6";
|
|
47
47
|
sphinxConfig.mainnets = ["ethereum", "optimism", "base", "arbitrum"];
|
|
48
48
|
sphinxConfig.testnets = ["ethereum_sepolia", "optimism_sepolia", "base_sepolia", "arbitrum_sepolia"];
|
|
49
49
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {Sphinx} from "@sphinx-labs/contracts/contracts/foundry/SphinxPlugin.sol";
|
|
5
5
|
import {Script} from "forge-std/Script.sol";
|
|
@@ -47,7 +47,7 @@ contract DeployPeriphery is Script, Sphinx {
|
|
|
47
47
|
address private OMNICHAIN_RULESET_OPERATOR = address(0x8f5DED85c40b50d223269C1F922A056E72101590);
|
|
48
48
|
|
|
49
49
|
function configureSphinx() public override {
|
|
50
|
-
sphinxConfig.projectName = "nana-core-
|
|
50
|
+
sphinxConfig.projectName = "nana-core-v6";
|
|
51
51
|
sphinxConfig.mainnets = ["ethereum", "optimism", "base", "arbitrum"];
|
|
52
52
|
sphinxConfig.testnets = ["ethereum_sepolia", "optimism_sepolia", "base_sepolia", "arbitrum_sepolia"];
|
|
53
53
|
}
|
|
@@ -254,9 +254,6 @@ contract DeployPeriphery is Script, Sphinx {
|
|
|
254
254
|
});
|
|
255
255
|
} else if (block.chainid == 84_532) {
|
|
256
256
|
usdc = address(0x036CbD53842c5426634e7929541eC2318f3dCF7e);
|
|
257
|
-
// TODO: Verify this feed address — 0xd30e2101... is the Arbitrum Sepolia ETH/USD feed and is likely
|
|
258
|
-
// incorrect for Base Sepolia USDC/USD. Replace with the correct Chainlink Base Sepolia USDC/USD address
|
|
259
|
-
// before deploying. Testnet-only; does not affect mainnet deployments.
|
|
260
257
|
usdcFeed = new JBChainlinkV3PriceFeed({
|
|
261
258
|
feed: AggregatorV3Interface(address(0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165)),
|
|
262
259
|
threshold: 86_400 seconds
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {stdJson} from "forge-std/Script.sol";
|
|
5
5
|
import {Vm} from "forge-std/Vm.sol";
|
|
@@ -40,7 +40,7 @@ library CoreDeploymentLib {
|
|
|
40
40
|
address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
|
|
41
41
|
// forge-lint: disable-next-line(screaming-snake-case-const)
|
|
42
42
|
Vm internal constant vm = Vm(VM_ADDRESS);
|
|
43
|
-
string constant PROJECT_NAME = "nana-core-
|
|
43
|
+
string constant PROJECT_NAME = "nana-core-v6";
|
|
44
44
|
|
|
45
45
|
function getDeployment(string memory path) internal returns (CoreDeployment memory deployment) {
|
|
46
46
|
// get chainId for which we need to get the deployment.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {AggregatorV2V3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol";
|
|
5
5
|
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
|
package/src/JBController.sol
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
4
|
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
5
5
|
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
|
|
@@ -95,11 +95,18 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
|
|
|
95
95
|
IJBTokens public immutable override TOKENS;
|
|
96
96
|
|
|
97
97
|
/// @notice The address of the contract that manages omnichain ruleset ops.
|
|
98
|
-
/// @dev This is a deterministic CREATE2 deployment; bytecode verification at runtime would add
|
|
99
|
-
///
|
|
100
|
-
///
|
|
101
|
-
///
|
|
102
|
-
///
|
|
98
|
+
/// @dev This is a deterministic CREATE2 deployment; bytecode verification at runtime would add gas cost for
|
|
99
|
+
/// marginal security benefit since the address is verified at deploy time.
|
|
100
|
+
/// @dev TRUST BOUNDARY: This hardcoded address can call `launchRulesetsFor` and `queueRulesetsOf` for ANY project,
|
|
101
|
+
/// bypassing normal `JBPermissions` checks. If this address is compromised or its implementation is malicious:
|
|
102
|
+
/// - Projects WITH approval hooks are partially protected: the attacker can queue rulesets, but they must pass
|
|
103
|
+
/// the project's approval hook before becoming active.
|
|
104
|
+
/// - Projects WITHOUT approval hooks (duration == 0 or no approval hook set) are IMMEDIATELY affected: queued
|
|
105
|
+
/// rulesets take effect as soon as the current ruleset expires (or immediately if duration == 0).
|
|
106
|
+
/// @dev The operator can also call `setTerminalsOf` during `launchRulesetsFor`, which could redirect a project's
|
|
107
|
+
/// payment routing. This is limited to the initial launch flow (reverts if rulesets already exist).
|
|
108
|
+
/// @dev Mitigation: verify the deployed bytecode at this address matches the expected `JBOmnichainDeployer`
|
|
109
|
+
/// contract from `nana-omnichain-deployers-v6` on every target chain before running deployment scripts.
|
|
103
110
|
address public immutable override OMNICHAIN_RULESET_OPERATOR;
|
|
104
111
|
|
|
105
112
|
//*********************************************************************//
|
|
@@ -164,7 +171,7 @@ contract JBController is JBPermissioned, ERC2771Context, IJBController, IJBMigra
|
|
|
164
171
|
/// @param pricingCurrency The currency the feed's output price is in terms of.
|
|
165
172
|
/// @param unitCurrency The currency being priced by the feed.
|
|
166
173
|
/// @param feed The address of the price feed to add.
|
|
167
|
-
function
|
|
174
|
+
function addPriceFeedFor(
|
|
168
175
|
uint256 projectId,
|
|
169
176
|
uint256 pricingCurrency,
|
|
170
177
|
uint256 unitCurrency,
|
package/src/JBDeadline.sol
CHANGED
package/src/JBDirectory.sol
CHANGED