@bananapus/core-v6 0.0.15 → 0.0.17
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 +5 -1
- package/ARCHITECTURE.md +2 -1
- package/AUDIT_INSTRUCTIONS.md +342 -0
- package/CHANGE_LOG.md +375 -0
- package/README.md +6 -6
- package/RISKS.md +171 -50
- package/SKILLS.md +11 -6
- package/STYLE_GUIDE.md +16 -2
- package/USER_JOURNEYS.md +622 -0
- package/package.json +2 -2
- package/script/Deploy.s.sol +22 -13
- package/script/DeployPeriphery.s.sol +76 -52
- package/script/helpers/CoreDeploymentLib.sol +83 -35
- package/src/JBChainlinkV3PriceFeed.sol +1 -0
- package/src/JBController.sol +23 -3
- package/src/JBDeadline.sol +3 -0
- package/src/JBDirectory.sol +2 -1
- package/src/JBERC20.sol +12 -3
- package/src/JBFundAccessLimits.sol +12 -2
- package/src/JBMultiTerminal.sol +53 -10
- package/src/JBPermissions.sol +3 -0
- package/src/JBPrices.sol +8 -2
- package/src/JBProjects.sol +1 -1
- package/src/JBRulesets.sol +14 -0
- package/src/JBSplits.sol +14 -5
- package/src/JBTerminalStore.sol +57 -47
- package/src/JBTokens.sol +43 -4
- package/src/interfaces/IJBController.sol +6 -0
- package/src/interfaces/IJBPermitTerminal.sol +1 -0
- package/src/interfaces/IJBTerminalStore.sol +3 -0
- package/src/interfaces/IJBToken.sol +5 -0
- package/src/interfaces/IJBTokens.sol +13 -0
- package/src/libraries/JBFees.sol +2 -0
- package/src/libraries/JBMetadataResolver.sol +24 -7
- package/src/libraries/JBRulesetMetadataResolver.sol +21 -21
- package/src/structs/JBAccountingContext.sol +1 -0
- package/src/structs/JBAfterCashOutRecordedContext.sol +1 -0
- package/src/structs/JBAfterPayRecordedContext.sol +1 -0
- package/src/structs/JBBeforeCashOutRecordedContext.sol +5 -0
- package/src/structs/JBBeforePayRecordedContext.sol +1 -0
- package/src/structs/JBCashOutHookSpecification.sol +1 -0
- package/src/structs/JBCurrencyAmount.sol +1 -0
- package/src/structs/JBFee.sol +1 -0
- package/src/structs/JBFundAccessLimitGroup.sol +1 -0
- package/src/structs/JBPayHookSpecification.sol +1 -0
- package/src/structs/JBPermissionsData.sol +1 -0
- package/src/structs/JBRuleset.sol +1 -0
- package/src/structs/JBRulesetConfig.sol +1 -0
- package/src/structs/JBRulesetMetadata.sol +1 -0
- package/src/structs/JBRulesetWeightCache.sol +1 -0
- package/src/structs/JBRulesetWithMetadata.sol +1 -0
- package/src/structs/JBSingleAllowance.sol +1 -0
- package/src/structs/JBSplit.sol +1 -0
- package/src/structs/JBSplitGroup.sol +1 -0
- package/src/structs/JBSplitHookContext.sol +1 -0
- package/src/structs/JBTerminalConfig.sol +1 -0
- package/src/structs/JBTokenAmount.sol +1 -0
- package/test/ComprehensiveInvariant.t.sol +15 -2
- package/test/CoreExploitTests.t.sol +34 -1
- package/test/EconomicSimulation.t.sol +10 -2
- package/test/EntryPointPermutations.t.sol +17 -3
- package/test/FlashLoanAttacks.t.sol +12 -1
- package/test/PermissionEscalation.t.sol +53 -10
- package/test/RulesetTransitions.t.sol +15 -1
- package/test/SplitLoopTests.t.sol +25 -2
- package/test/TestAccessToFunds.sol +17 -2
- package/test/TestAuditResponseDesignProofs.sol +434 -0
- package/test/TestCashOut.sol +15 -1
- package/test/TestCashOutCountFor.sol +1 -1
- package/test/TestCashOutHooks.sol +47 -25
- package/test/TestCashOutTimingEdge.sol +13 -1
- package/test/TestDataHookFuzzing.sol +520 -0
- package/test/TestDurationUnderflow.sol +13 -1
- package/test/TestFeeFreeCashOutBypass.sol +617 -0
- package/test/TestFeeProcessingFailure.sol +16 -1
- package/test/TestFees.sol +14 -1
- package/test/TestInterfaceSupport.sol +20 -1
- package/test/TestJBERC20Inheritance.sol +11 -1
- package/test/TestL2SequencerPriceFeed.sol +292 -0
- package/test/TestLaunchProject.sol +13 -1
- package/test/TestMetaTx.sol +15 -1
- package/test/TestMetadataOffsetOverflow.sol +179 -0
- package/test/TestMetadataParserLib.sol +37 -4
- package/test/TestMigrationHeldFees.sol +16 -1
- package/test/TestMintTokensOf.sol +14 -1
- package/test/TestMultiTerminalSurplus.sol +348 -0
- package/test/TestMultiTokenSurplus.sol +14 -1
- package/test/TestMultipleAccessLimits.sol +23 -1
- package/test/TestPayBurnRedeemFlow.sol +16 -1
- package/test/TestPayHooks.sol +33 -14
- package/test/TestPermissions.sol +20 -1
- package/test/TestPermissionsEdge.sol +5 -1
- package/test/TestPermit2DataHook.t.sol +360 -0
- package/test/TestPermit2Terminal.sol +36 -3
- package/test/TestRulesetQueueing.sol +23 -1
- package/test/TestRulesetQueuingStress.sol +20 -1
- package/test/TestRulesetWeightCaching.sol +127 -125
- package/test/TestSplits.sol +23 -1
- package/test/TestTerminalMigration.sol +11 -1
- package/test/TestTokenFlow.sol +18 -1
- package/test/TestWeightCacheStaleAfterRejection.sol +15 -1
- package/test/WeirdTokenTests.t.sol +54 -1
- package/test/fork/TestChainlinkPriceFeedFork.sol +6 -1
- package/test/formal/BondingCurveProperties.t.sol +8 -1
- package/test/formal/FeeProperties.t.sol +7 -1
- package/test/helpers/JBTest.sol +1 -1
- package/test/helpers/TestBaseWorkflow.sol +84 -1
- package/test/invariants/Phase3DeepInvariant.t.sol +13 -2
- package/test/invariants/RulesetsInvariant.t.sol +12 -2
- package/test/invariants/TerminalStoreInvariant.t.sol +11 -2
- package/test/invariants/TokensInvariant.t.sol +13 -2
- package/test/invariants/handlers/ComprehensiveHandler.sol +19 -1
- package/test/invariants/handlers/EconomicHandler.sol +31 -1
- package/test/invariants/handlers/Phase3Handler.sol +31 -1
- package/test/invariants/handlers/RulesetsHandler.sol +5 -1
- package/test/invariants/handlers/TerminalStoreHandler.sol +6 -1
- package/test/invariants/handlers/TokensHandler.sol +1 -1
- package/test/mock/MockERC20.sol +0 -2
- package/test/mock/MockMaliciousBeneficiary.sol +2 -1
- package/test/mock/MockMaliciousSplitHook.sol +2 -1
- package/test/mock/MockPriceFeed.sol +1 -1
- package/test/regression/HoldFeesCashOutReserved.t.sol +415 -0
- package/test/regression/WeightCacheBoundary.t.sol +291 -0
- package/test/units/static/JBChainlinkV3PriceFeed/TestPriceFeed.sol +0 -1
- package/test/units/static/JBController/JBControllerSetup.sol +10 -1
- package/test/units/static/JBController/TestBurnTokensOf.sol +8 -1
- package/test/units/static/JBController/TestClaimTokensFor.sol +4 -1
- package/test/units/static/JBController/TestDeployErc20For.sol +7 -1
- package/test/units/static/JBController/TestLaunchProjectFor.sol +21 -1
- package/test/units/static/JBController/TestLaunchRulesetsFor.sol +21 -1
- package/test/units/static/JBController/TestMigrateController.sol +10 -1
- package/test/units/static/JBController/TestMintTokensOfUnits.sol +10 -1
- package/test/units/static/JBController/TestPayReservedTokenToTerminal.sol +4 -1
- package/test/units/static/JBController/TestReceiveMigrationFrom.sol +5 -1
- package/test/units/static/JBController/TestRulesetViews.sol +7 -1
- package/test/units/static/JBController/TestSendReservedTokensToSplitsOf.sol +21 -1
- package/test/units/static/JBController/TestSetSplitGroupsOf.sol +6 -1
- package/test/units/static/JBController/TestSetTokenFor.sol +13 -1
- package/test/units/static/JBController/TestSetUriOf.sol +5 -1
- package/test/units/static/JBController/TestTransferCreditsFrom.sol +11 -1
- package/test/units/static/JBDeadline/TestDeadlineFuzz.sol +12 -1
- package/test/units/static/JBDirectory/JBDirectorySetup.sol +4 -1
- package/test/units/static/JBDirectory/TestPrimaryTerminalOf.sol +5 -1
- package/test/units/static/JBDirectory/TestSetControllerOf.sol +11 -1
- package/test/units/static/JBDirectory/TestSetControllerOfMigrationOrder.sol +7 -1
- package/test/units/static/JBDirectory/TestSetPrimaryTerminalOf.sol +11 -1
- package/test/units/static/JBDirectory/TestSetTerminalsOf.sol +10 -1
- package/test/units/static/JBERC20/JBERC20Setup.sol +2 -1
- package/test/units/static/JBERC20/SigUtils.sol +2 -0
- package/test/units/static/JBERC20/TestInitialize.sol +1 -1
- package/test/units/static/JBERC20/TestName.sol +1 -1
- package/test/units/static/JBERC20/TestNonces.sol +3 -1
- package/test/units/static/JBERC20/TestSymbol.sol +1 -1
- package/test/units/static/JBFeelessAdresses/JBFeelessSetup.sol +2 -1
- package/test/units/static/JBFeelessAdresses/TestInterfaces.sol +2 -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 +0 -1
- package/test/units/static/JBFixedPointNumber/TestAdjustDecimalsFuzz.sol +0 -1
- package/test/units/static/JBFundAccessLimits/JBFundAccessSetup.sol +3 -1
- package/test/units/static/JBFundAccessLimits/TestFundAccessLimitsEdge.sol +4 -1
- package/test/units/static/JBFundAccessLimits/TestPayoutLimitOf.sol +4 -1
- package/test/units/static/JBFundAccessLimits/TestPayoutLimitsOf.sol +8 -1
- package/test/units/static/JBFundAccessLimits/TestSetFundAccessLimitsFor.sol +8 -1
- package/test/units/static/JBFundAccessLimits/TestSurplusAllowanceOf.sol +4 -1
- package/test/units/static/JBFundAccessLimits/TestSurplusAllowancesOf.sol +7 -1
- package/test/units/static/JBMetadataResolver/TestGetDataFor.sol +1 -1
- package/test/units/static/JBMetadataResolver/TestMetadataResolverEdgeCases.sol +2 -1
- package/test/units/static/JBMetadataResolver/TestMetadataResolverFuzz.sol +2 -1
- package/test/units/static/JBMultiTerminal/JBMultiTerminalSetup.sol +12 -1
- package/test/units/static/JBMultiTerminal/TestAccountingContextsOf.sol +9 -1
- package/test/units/static/JBMultiTerminal/TestAddAccountingContextsFor.sol +18 -2
- package/test/units/static/JBMultiTerminal/TestAddToBalanceOf.sol +44 -9
- package/test/units/static/JBMultiTerminal/TestCashOutTokensOf.sol +48 -23
- package/test/units/static/JBMultiTerminal/TestExecutePayout.sol +18 -2
- package/test/units/static/JBMultiTerminal/TestExecuteProcessFee.sol +13 -3
- package/test/units/static/JBMultiTerminal/TestMigrateBalanceOf.sol +21 -4
- package/test/units/static/JBMultiTerminal/TestPay.sol +35 -7
- package/test/units/static/JBMultiTerminal/TestProcessHeldFeesOf.sol +206 -19
- package/test/units/static/JBMultiTerminal/TestSendPayoutsOf.sol +15 -1
- package/test/units/static/JBMultiTerminal/TestUseAllowanceOf.sol +297 -1
- package/test/units/static/JBPermissions/JBPermissionsSetup.sol +2 -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 +3 -1
- package/test/units/static/JBPrices/JBPricesSetup.sol +6 -1
- package/test/units/static/JBPrices/TestAddPriceFeedFor.sol +6 -1
- package/test/units/static/JBPrices/TestPricePerUnitOf.sol +4 -1
- package/test/units/static/JBPrices/TestPrices.sol +4 -1
- package/test/units/static/JBProjects/JBProjectsSetup.sol +2 -1
- package/test/units/static/JBProjects/TestCreateFor.sol +3 -1
- package/test/units/static/JBProjects/TestInitialProject.sol +2 -1
- package/test/units/static/JBProjects/TestInterfaces.sol +0 -1
- package/test/units/static/JBProjects/TestSetResolver.sol +2 -1
- package/test/units/static/JBProjects/TestTokenUri.sol +3 -1
- package/test/units/static/JBRulesetMetadataResolver/TestSetCashOutTaxRateTo.sol +9 -1
- package/test/units/static/JBRulesets/JBRulesetsSetup.sol +3 -1
- package/test/units/static/JBRulesets/TestCurrentApprovalStatusForLatestRulesetOf.sol +9 -1
- package/test/units/static/JBRulesets/TestCurrentOf.sol +10 -1
- package/test/units/static/JBRulesets/TestGetRulesetOf.sol +7 -1
- package/test/units/static/JBRulesets/TestLatestQueuedRulesetOf.sol +9 -1
- package/test/units/static/JBRulesets/TestRulesets.sol +12 -1
- package/test/units/static/JBRulesets/TestRulesetsOf.sol +1 -1
- package/test/units/static/JBRulesets/TestUpcomingRulesetOf.sol +10 -1
- package/test/units/static/JBRulesets/TestUpdateRulesetWeightCache.sol +6 -1
- package/test/units/static/JBSplits/JBSplitsSetup.sol +3 -1
- package/test/units/static/JBSplits/TestSelfManagedSplitGroups.sol +63 -13
- package/test/units/static/JBSplits/TestSetSplitGroupsOf.sol +8 -1
- package/test/units/static/JBSplits/TestSplitsLockedEdge.sol +6 -1
- package/test/units/static/JBSplits/TestSplitsOf.sol +1 -1
- package/test/units/static/JBSplits/TestSplitsPacking.sol +5 -2
- package/test/units/static/JBSurplus/TestSurplusFuzz.sol +3 -1
- package/test/units/static/JBTerminalStore/JBTerminalStoreSetup.sol +5 -1
- package/test/units/static/JBTerminalStore/TestCurrentReclaimableSurplusOf.sol +14 -1
- package/test/units/static/JBTerminalStore/TestCurrentSurplusOf.sol +14 -1
- package/test/units/static/JBTerminalStore/TestCurrentTotalSurplusOf.sol +3 -1
- package/test/units/static/JBTerminalStore/TestRecordCashOutsFor.sol +92 -1
- package/test/units/static/JBTerminalStore/TestRecordPaymentFrom.sol +15 -1
- package/test/units/static/JBTerminalStore/TestRecordPayoutFor.sol +13 -1
- package/test/units/static/JBTerminalStore/TestRecordTerminalMigration.sol +8 -1
- package/test/units/static/JBTerminalStore/TestRecordUsedAllowanceOf.sol +16 -1
- package/test/units/static/JBTerminalStore/TestUint224Overflow.sol +15 -1
- package/test/units/static/JBTokens/JBTokensSetup.sol +5 -1
- package/test/units/static/JBTokens/TestBurnFrom.sol +4 -1
- package/test/units/static/JBTokens/TestClaimTokensFor.sol +4 -1
- package/test/units/static/JBTokens/TestDeployERC20ForUnits.sol +4 -1
- package/test/units/static/JBTokens/TestMintFor.sol +4 -1
- package/test/units/static/JBTokens/TestSetTokenFor.sol +4 -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 +3 -1
package/USER_JOURNEYS.md
ADDED
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
# nana-core-v6 -- User Journeys
|
|
2
|
+
|
|
3
|
+
All user paths through the Juicebox V6 core protocol. For each journey: entry point, key parameters, state changes, events, and edge cases.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Launch Project
|
|
8
|
+
|
|
9
|
+
**Entry point**: `JBController.launchProjectFor(address owner, string projectUri, JBRulesetConfig[] rulesetConfigurations, JBTerminalConfig[] terminalConfigurations, string memo)`
|
|
10
|
+
|
|
11
|
+
**Who can call**: Anyone. The project ERC-721 is minted to the specified `owner`.
|
|
12
|
+
|
|
13
|
+
**Parameters**:
|
|
14
|
+
- `owner` -- Address that receives the project ERC-721 NFT
|
|
15
|
+
- `projectUri` -- Metadata URI (typically IPFS hash)
|
|
16
|
+
- `rulesetConfigurations` -- Array of `JBRulesetConfig` structs defining the project's economic rules
|
|
17
|
+
- `terminalConfigurations` -- Array of `JBTerminalConfig` structs specifying which terminals accept which tokens
|
|
18
|
+
- `memo` -- Arbitrary string emitted in the event
|
|
19
|
+
|
|
20
|
+
**State changes**:
|
|
21
|
+
1. `JBProjects.createFor(owner)` -- Mints ERC-721, increments project count, returns `projectId`
|
|
22
|
+
2. `uriOf[projectId] = projectUri` -- Stores metadata URI (if non-empty)
|
|
23
|
+
3. `JBDirectory.setControllerOf(projectId, controller)` -- Sets this controller as the project's controller
|
|
24
|
+
4. For each terminal config: `terminal.addAccountingContextsFor(projectId, contexts)` -- Registers accepted tokens
|
|
25
|
+
5. `JBDirectory.setTerminalsOf(projectId, terminals)` -- Registers terminals
|
|
26
|
+
6. For each ruleset config:
|
|
27
|
+
- `JBRulesets.queueFor(...)` -- Creates ruleset with packed intrinsic/user properties and metadata
|
|
28
|
+
- `JBSplits.setSplitGroupsOf(...)` -- Stores split groups for the ruleset
|
|
29
|
+
- `JBFundAccessLimits.setFundAccessLimitsFor(...)` -- Stores payout limits and surplus allowances
|
|
30
|
+
|
|
31
|
+
**Events**: `LaunchProject(rulesetId, projectId, projectUri, memo, caller)`
|
|
32
|
+
|
|
33
|
+
**Edge cases**:
|
|
34
|
+
- Empty `rulesetConfigurations` array is valid -- project launches with no rulesets (cannot receive payments until rulesets are launched via `launchRulesetsFor`)
|
|
35
|
+
- Multiple rulesets can be queued in a single launch -- they form a linked list
|
|
36
|
+
- If `block.timestamp` collision occurs for rulesetId, the ID is incremented by 1
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 2. Pay a Project
|
|
41
|
+
|
|
42
|
+
**Entry point**: `JBMultiTerminal.pay(uint256 projectId, address token, uint256 amount, address beneficiary, uint256 minReturnedTokens, string memo, bytes metadata)`
|
|
43
|
+
|
|
44
|
+
**Who can call**: Anyone.
|
|
45
|
+
|
|
46
|
+
**Parameters**:
|
|
47
|
+
- `projectId` -- The project to pay
|
|
48
|
+
- `token` -- Token address (`JBConstants.NATIVE_TOKEN` for ETH)
|
|
49
|
+
- `amount` -- Amount of tokens (ignored for native token; uses `msg.value`)
|
|
50
|
+
- `beneficiary` -- Address to receive minted project tokens
|
|
51
|
+
- `minReturnedTokens` -- Slippage protection; reverts if fewer tokens minted
|
|
52
|
+
- `memo` -- Arbitrary string
|
|
53
|
+
- `metadata` -- Bytes; may contain Permit2 data (keyed by `"permit2"` ID) and/or hook-specific data
|
|
54
|
+
|
|
55
|
+
**State changes**:
|
|
56
|
+
1. Tokens transferred to terminal (or `msg.value` accepted)
|
|
57
|
+
2. `JBTerminalStore.balanceOf[terminal][projectId][token]` incremented (minus any hook-diverted amounts)
|
|
58
|
+
3. `JBTokens` mints project tokens to beneficiary (credits or ERC-20)
|
|
59
|
+
4. `JBController.pendingReservedTokenBalanceOf[projectId]` incremented by reserved portion
|
|
60
|
+
5. Pay hooks execute (if data hook returns specifications)
|
|
61
|
+
|
|
62
|
+
**Events**: `Pay(rulesetId, rulesetCycleNumber, projectId, payer, beneficiary, amount, newlyIssuedTokenCount, memo, metadata, caller)`
|
|
63
|
+
|
|
64
|
+
**Edge cases**:
|
|
65
|
+
- `amount = 0` is valid -- records a zero payment, mints 0 tokens
|
|
66
|
+
- `beneficiary = address(0)` -- tokens minted to zero address (effectively burned on mint)
|
|
67
|
+
- If `pausePay` is set in ruleset metadata, `recordPaymentFrom` reverts
|
|
68
|
+
- Token count = `mulDiv(amount.value, weight, weightRatio)` -- if weight is 0, no tokens minted
|
|
69
|
+
- Data hook can return empty weight (0) to suppress minting while still recording payment
|
|
70
|
+
- Fee-on-transfer tokens: actual amount received is `_balanceOf(token) - balanceBefore` (measured via balance diff)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 3. Cash Out Tokens
|
|
75
|
+
|
|
76
|
+
**Entry point**: `JBMultiTerminal.cashOutTokensOf(address holder, uint256 projectId, uint256 cashOutCount, address tokenToReclaim, uint256 minTokensReclaimed, address payable beneficiary, bytes metadata)`
|
|
77
|
+
|
|
78
|
+
**Who can call**: The token holder, or an address with the holder's `CASH_OUT_TOKENS` permission.
|
|
79
|
+
|
|
80
|
+
**Parameters**:
|
|
81
|
+
- `holder` -- Address whose tokens are being cashed out
|
|
82
|
+
- `projectId` -- The project to cash out from
|
|
83
|
+
- `cashOutCount` -- Number of project tokens to burn (18 decimals)
|
|
84
|
+
- `tokenToReclaim` -- Terminal token to receive back
|
|
85
|
+
- `minTokensReclaimed` -- Slippage protection
|
|
86
|
+
- `beneficiary` -- Address to receive reclaimed tokens
|
|
87
|
+
- `metadata` -- Hook-specific data
|
|
88
|
+
|
|
89
|
+
**State changes**:
|
|
90
|
+
1. `JBTerminalStore.balanceOf[terminal][projectId][token]` decremented by `reclaimAmount + hookSpec amounts`
|
|
91
|
+
2. Project tokens burned via `JBController.burnTokensOf()` (credits first, then ERC-20)
|
|
92
|
+
3. Reclaimed tokens transferred to beneficiary
|
|
93
|
+
4. Cash out hooks execute (if data hook returns specifications)
|
|
94
|
+
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.
|
|
95
|
+
|
|
96
|
+
**Events**: `CashOutTokens(rulesetId, rulesetCycleNumber, projectId, holder, beneficiary, cashOutCount, cashOutTaxRate, reclaimAmount, metadata, caller)`
|
|
97
|
+
|
|
98
|
+
**Edge cases**:
|
|
99
|
+
- `cashOutCount = 0` with `totalSupply = 0` -- returns entire surplus (C-5 known bug)
|
|
100
|
+
- `cashOutTaxRate = MAX (10,000)` -- returns 0 (all surplus locked)
|
|
101
|
+
- `cashOutTaxRate = 0` -- proportional (1:1 against supply) with no discount
|
|
102
|
+
- `cashOutCount >= totalSupply` -- returns entire surplus regardless of tax rate
|
|
103
|
+
- Data hook can override `cashOutTaxRate`, `cashOutCount`, `totalSupply` to arbitrary values
|
|
104
|
+
- Fee is NOT taken when `cashOutTaxRate == 0` UNLESS the project has unconsumed fee-free surplus from intra-terminal payouts (`_feeFreeSurplusOf`). The fee applies only up to the surplus amount and depletes it — preventing round-trip fee bypass while scoping fees precisely to the fee-free inflow.
|
|
105
|
+
- Pending reserved tokens inflate `totalSupply`, reducing individual cash out value (H-4)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 4. Send Payouts to Splits
|
|
110
|
+
|
|
111
|
+
**Entry point**: `JBMultiTerminal.sendPayoutsOf(uint256 projectId, address token, uint256 amount, uint256 currency, uint256 minTokensPaidOut)`
|
|
112
|
+
|
|
113
|
+
**Who can call**: Anyone (unless `ownerMustSendPayouts` is set, then requires `SEND_PAYOUTS` permission from owner).
|
|
114
|
+
|
|
115
|
+
**Parameters**:
|
|
116
|
+
- `projectId` -- The project distributing payouts
|
|
117
|
+
- `token` -- Token being distributed
|
|
118
|
+
- `amount` -- Amount to distribute (in terms of `currency`)
|
|
119
|
+
- `currency` -- Currency denomination of the amount; must match a configured payout limit's currency
|
|
120
|
+
- `minTokensPaidOut` -- Slippage protection on the actual token amount paid out
|
|
121
|
+
|
|
122
|
+
**State changes**:
|
|
123
|
+
1. `JBTerminalStore.balanceOf` decremented by `amountPaidOut` (currency-converted)
|
|
124
|
+
2. `JBTerminalStore.usedPayoutLimitOf` incremented
|
|
125
|
+
3. For each split: funds transferred (split hooks, project terminals, or addresses)
|
|
126
|
+
4. Failed splits: amount returned to project balance via `recordAddedBalanceFor`
|
|
127
|
+
5. Leftover (if splits < 100%): sent to project owner
|
|
128
|
+
6. Fee taken on all non-feeless payouts
|
|
129
|
+
|
|
130
|
+
**Events**: `SendPayouts(rulesetId, rulesetCycleNumber, projectId, projectOwner, amount, amountPaidOut, fee, netLeftoverPayoutAmount, caller)`, `SendPayoutToSplit(...)` per split
|
|
131
|
+
|
|
132
|
+
**Edge cases**:
|
|
133
|
+
- `amount > payout limit` -- reverts with `InadequateControllerPayoutLimit`. Does NOT auto-cap.
|
|
134
|
+
- Empty `fundAccessLimitGroups` for the terminal/token = zero payout limit = always reverts
|
|
135
|
+
- Currency conversion uses `JBPrices` if `currency != accountingContext.currency`
|
|
136
|
+
- Payout limit resets each ruleset cycle (`cycleNumber`)
|
|
137
|
+
- `ownerMustSendPayouts` flag gates who can trigger payouts
|
|
138
|
+
- Individual split failures are caught by try-catch; the payout continues to remaining splits
|
|
139
|
+
- Split percentage uses `mulDiv(amount, split.percent, leftoverPercentage)` -- each split gets its proportion of the remaining amount, not of the original total
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 5. Use Surplus Allowance
|
|
144
|
+
|
|
145
|
+
**Entry point**: `JBMultiTerminal.useAllowanceOf(uint256 projectId, address token, uint256 amount, uint256 currency, uint256 minTokensPaidOut, address payable beneficiary, address payable feeBeneficiary, string memo)`
|
|
146
|
+
|
|
147
|
+
**Who can call**: Project owner or address with `USE_ALLOWANCE` permission.
|
|
148
|
+
|
|
149
|
+
**Parameters**:
|
|
150
|
+
- `projectId`, `token`, `amount`, `currency` -- What to withdraw and in what denomination
|
|
151
|
+
- `minTokensPaidOut` -- Slippage protection on net amount after fees
|
|
152
|
+
- `beneficiary` -- Receives the withdrawn surplus
|
|
153
|
+
- `feeBeneficiary` -- Receives project #1 tokens minted from the fee payment
|
|
154
|
+
- `memo` -- Arbitrary string
|
|
155
|
+
|
|
156
|
+
**State changes**:
|
|
157
|
+
1. `JBTerminalStore.balanceOf` decremented by `usedAmount`
|
|
158
|
+
2. `JBTerminalStore.usedSurplusAllowanceOf` incremented
|
|
159
|
+
3. Fee taken (unless owner or beneficiary is feeless)
|
|
160
|
+
4. Net amount transferred to beneficiary
|
|
161
|
+
|
|
162
|
+
**Events**: `UseAllowance(rulesetId, rulesetCycleNumber, projectId, beneficiary, feeBeneficiary, amount, amountPaidOut, netAmountPaidOut, memo, caller)`
|
|
163
|
+
|
|
164
|
+
**Edge cases**:
|
|
165
|
+
- `usedAmount > surplus` -- reverts (`InadequateTerminalStoreBalance`)
|
|
166
|
+
- Surplus allowance resets each ruleset (keyed by `rulesetId`, not `cycleNumber`)
|
|
167
|
+
- Amount validated against surplus BEFORE checking allowance limit
|
|
168
|
+
- If both owner and beneficiary are feeless, no fee is taken
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 6. Mint Tokens (Owner)
|
|
173
|
+
|
|
174
|
+
**Entry point**: `JBController.mintTokensOf(uint256 projectId, uint256 tokenCount, address beneficiary, string memo, bool useReservedPercent)`
|
|
175
|
+
|
|
176
|
+
**Who can call**: Project owner, address with `MINT_TOKENS` permission, a project terminal, the data hook, or an address with `hasMintPermissionFor` from the data hook.
|
|
177
|
+
|
|
178
|
+
**Parameters**:
|
|
179
|
+
- `projectId` -- Target project
|
|
180
|
+
- `tokenCount` -- Total tokens to mint (including reserved portion)
|
|
181
|
+
- `beneficiary` -- Receives the non-reserved tokens
|
|
182
|
+
- `memo` -- Arbitrary string
|
|
183
|
+
- `useReservedPercent` -- If true, applies the ruleset's `reservedPercent`; if false, all tokens go to beneficiary
|
|
184
|
+
|
|
185
|
+
**State changes**:
|
|
186
|
+
1. `JBTokens.mintFor(beneficiary, beneficiaryTokenCount)` -- Mints non-reserved portion
|
|
187
|
+
2. `pendingReservedTokenBalanceOf[projectId]` incremented by reserved portion
|
|
188
|
+
|
|
189
|
+
**Events**: `MintTokens(beneficiary, projectId, tokenCount, beneficiaryTokenCount, memo, reservedPercent, caller)`
|
|
190
|
+
|
|
191
|
+
**Edge cases**:
|
|
192
|
+
- `tokenCount = 0` -- reverts (`ZeroTokensToMint`)
|
|
193
|
+
- If `allowOwnerMinting` is false in ruleset, only terminals and data hooks can mint
|
|
194
|
+
- If `reservedPercent = 10,000` (100%), all tokens go to pending reserved balance, `beneficiaryTokenCount = 0`
|
|
195
|
+
- Terminal calls this with `useReservedPercent = true` during payments
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 7. Burn Tokens
|
|
200
|
+
|
|
201
|
+
**Entry point**: `JBController.burnTokensOf(address holder, uint256 projectId, uint256 tokenCount, string memo)`
|
|
202
|
+
|
|
203
|
+
**Who can call**: The token holder, an address with the holder's `BURN_TOKENS` permission, or a project terminal.
|
|
204
|
+
|
|
205
|
+
**Parameters**:
|
|
206
|
+
- `holder` -- Address whose tokens to burn
|
|
207
|
+
- `projectId` -- Project whose tokens are being burned
|
|
208
|
+
- `tokenCount` -- Number of tokens to burn
|
|
209
|
+
- `memo` -- Arbitrary string
|
|
210
|
+
|
|
211
|
+
**State changes**:
|
|
212
|
+
1. Credits burned first (up to credit balance)
|
|
213
|
+
2. Remaining amount burned from ERC-20 balance (if any)
|
|
214
|
+
3. `JBTokens` reduces credit and/or ERC-20 supply
|
|
215
|
+
|
|
216
|
+
**Events**: `BurnTokens(holder, projectId, tokenCount, memo, caller)`
|
|
217
|
+
|
|
218
|
+
**Edge cases**:
|
|
219
|
+
- `tokenCount = 0` -- reverts (`ZeroTokensToBurn`)
|
|
220
|
+
- Credits are always burned first. If holder has 100 credits and 50 ERC-20, burning 120 burns all 100 credits + 20 ERC-20.
|
|
221
|
+
- Terminal calls this during cash outs
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 8. Queue New Ruleset
|
|
226
|
+
|
|
227
|
+
**Entry point**: `JBController.queueRulesetsOf(uint256 projectId, JBRulesetConfig[] rulesetConfigurations, string memo)`
|
|
228
|
+
|
|
229
|
+
**Who can call**: Project owner, address with `QUEUE_RULESETS` permission, or the `OMNICHAIN_RULESET_OPERATOR`.
|
|
230
|
+
|
|
231
|
+
**Parameters**:
|
|
232
|
+
- `projectId` -- Target project
|
|
233
|
+
- `rulesetConfigurations` -- Array of ruleset configs to queue
|
|
234
|
+
- `memo` -- Arbitrary string
|
|
235
|
+
|
|
236
|
+
**State changes**:
|
|
237
|
+
1. For each config:
|
|
238
|
+
- `JBRulesets.queueFor(...)` -- Creates new ruleset in linked list
|
|
239
|
+
- `JBSplits.setSplitGroupsOf(...)` -- Sets splits for the new ruleset
|
|
240
|
+
- `JBFundAccessLimits.setFundAccessLimitsFor(...)` -- Sets limits for the new ruleset
|
|
241
|
+
2. `latestRulesetIdOf[projectId]` updated
|
|
242
|
+
|
|
243
|
+
**Events**: `QueueRulesets(rulesetId, projectId, memo, caller)`, `RulesetQueued(rulesetId, projectId, ...)`
|
|
244
|
+
|
|
245
|
+
**Edge cases**:
|
|
246
|
+
- Empty array reverts (`RulesetsArrayEmpty`)
|
|
247
|
+
- `reservedPercent > 10,000` reverts
|
|
248
|
+
- `cashOutTaxRate > 10,000` reverts
|
|
249
|
+
- `weight > type(uint112).max` reverts
|
|
250
|
+
- `duration > type(uint32).max` reverts
|
|
251
|
+
- `mustStartAtOrAfter + duration > type(uint48).max` reverts
|
|
252
|
+
- If `mustStartAtOrAfter = 0`, it defaults to `block.timestamp`
|
|
253
|
+
- If `rulesetId` collides with current timestamp, it is incremented by 1
|
|
254
|
+
- Approval hook address is validated: must have code, must support `IJBRulesetApprovalHook` interface
|
|
255
|
+
- Queued rulesets take effect after the current ruleset expires (or immediately for `duration = 0`)
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 9. Set Splits
|
|
260
|
+
|
|
261
|
+
**Entry point**: `JBController.setSplitGroupsOf(uint256 projectId, uint256 rulesetId, JBSplitGroup[] splitGroups)`
|
|
262
|
+
|
|
263
|
+
**Who can call**: Project owner or address with `SET_SPLIT_GROUPS` permission. Alternatively, a contract whose address matches the lower 160 bits of a `groupId` can set that group directly -- but only if the upper 96 bits of the `groupId` are non-zero (bare-address groupIds are protocol-reserved for terminal payout groups).
|
|
264
|
+
|
|
265
|
+
**Parameters**:
|
|
266
|
+
- `projectId` -- Target project
|
|
267
|
+
- `rulesetId` -- The ruleset ID the splits apply to. Use `0` for default/fallback splits.
|
|
268
|
+
- `splitGroups` -- Array of `JBSplitGroup` structs, each containing a `groupId` and `JBSplit[]`
|
|
269
|
+
|
|
270
|
+
**State changes**:
|
|
271
|
+
1. `JBSplits` stores the new split groups for the project/ruleset/group combination
|
|
272
|
+
2. Locked splits from existing configuration must be preserved (validated by `JBSplits`)
|
|
273
|
+
|
|
274
|
+
**Events**: Emitted by `JBSplits` (not `JBController`)
|
|
275
|
+
|
|
276
|
+
**Edge cases**:
|
|
277
|
+
- Locked splits (`lockedUntil > block.timestamp`) cannot be removed or modified
|
|
278
|
+
- If no splits set for a rulesetId, `splitsOf()` falls back to `rulesetId = 0` (default splits)
|
|
279
|
+
- If no default splits either, all payouts/reserved tokens go to project owner
|
|
280
|
+
- Split `percent` values are out of `SPLITS_TOTAL_PERCENT` (1,000,000,000)
|
|
281
|
+
- Payout splits use `groupId = uint256(uint160(token))`, reserved token splits use `groupId = 1` (`JBSplitGroupIds.RESERVED_TOKENS`)
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 10. Migrate Terminal
|
|
286
|
+
|
|
287
|
+
**Entry point**: `JBMultiTerminal.migrateBalanceOf(uint256 projectId, address token, IJBTerminal to)`
|
|
288
|
+
|
|
289
|
+
**Who can call**: Project owner or address with `MIGRATE_TERMINAL` permission.
|
|
290
|
+
|
|
291
|
+
**Parameters**:
|
|
292
|
+
- `projectId` -- Project being migrated
|
|
293
|
+
- `token` -- Token balance to migrate
|
|
294
|
+
- `to` -- Destination terminal
|
|
295
|
+
|
|
296
|
+
**State changes**:
|
|
297
|
+
1. `JBTerminalStore.balanceOf[oldTerminal][projectId][token]` set to 0
|
|
298
|
+
2. Funds transferred to destination terminal via `to.addToBalanceOf()`
|
|
299
|
+
3. Destination terminal records the added balance
|
|
300
|
+
|
|
301
|
+
**Events**: `MigrateTerminal(projectId, token, to, amount, caller)`
|
|
302
|
+
|
|
303
|
+
**Edge cases**:
|
|
304
|
+
- Requires `allowTerminalMigration` in current ruleset
|
|
305
|
+
- Destination terminal must have accounting context for the token (validated via `accountingContextForTokenOf`)
|
|
306
|
+
- **Held fees are NOT transferred** -- they remain in the old terminal. Held fees belong to the fee beneficiary (project #1), not the migrating project.
|
|
307
|
+
- If balance is 0, no transfer occurs
|
|
308
|
+
- This only migrates one token's balance. Must be called once per token.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 11. Migrate Controller
|
|
313
|
+
|
|
314
|
+
**Entry point**: `JBDirectory.setControllerOf(uint256 projectId, IERC165 controller)`
|
|
315
|
+
|
|
316
|
+
**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.
|
|
317
|
+
|
|
318
|
+
**Flow**:
|
|
319
|
+
1. `JBDirectory.setControllerOf(projectId, newController)` is called
|
|
320
|
+
2. Directory calls `newController.beforeReceiveMigrationFrom(oldController, projectId)`:
|
|
321
|
+
- Copies metadata URI from old controller
|
|
322
|
+
- Distributes pending reserved tokens from old controller
|
|
323
|
+
3. Directory calls `oldController.migrate(projectId, newController)`:
|
|
324
|
+
- Reverts if pending reserved tokens > 0 (must distribute first)
|
|
325
|
+
4. Directory updates `controllerOf[projectId] = newController`
|
|
326
|
+
5. Directory calls `newController.afterReceiveMigrationFrom(oldController, projectId)`
|
|
327
|
+
|
|
328
|
+
**Events**: `Migrate(projectId, to, caller)` from old controller; `SetController(projectId, controller, caller)` from directory
|
|
329
|
+
|
|
330
|
+
**Edge cases**:
|
|
331
|
+
- `pendingReservedTokenBalanceOf[projectId] != 0` causes revert in `migrate()` -- reserved tokens must be distributed first
|
|
332
|
+
- `beforeReceiveMigrationFrom` automatically distributes pending reserved tokens from the old controller
|
|
333
|
+
- The old controller's `migrate()` runs while the directory still points to it (prevents reentrancy window)
|
|
334
|
+
- First controller can be set by addresses in `isAllowedToSetFirstController` without owner permission
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 12. Deploy ERC-20 for Project Tokens
|
|
339
|
+
|
|
340
|
+
**Entry point**: `JBController.deployERC20For(uint256 projectId, string name, string symbol, bytes32 salt)`
|
|
341
|
+
|
|
342
|
+
**Who can call**: Project owner or address with `DEPLOY_ERC20` permission.
|
|
343
|
+
|
|
344
|
+
**Parameters**:
|
|
345
|
+
- `projectId` -- Target project
|
|
346
|
+
- `name` -- ERC-20 token name
|
|
347
|
+
- `symbol` -- ERC-20 token symbol
|
|
348
|
+
- `salt` -- For deterministic deployment (CREATE2). Pass `bytes32(0)` for non-deterministic.
|
|
349
|
+
|
|
350
|
+
**State changes**:
|
|
351
|
+
1. `JBTokens.deployERC20For()` clones `JBERC20` implementation via `Clones.clone()` (or `Clones.cloneDeterministic()` if salt provided)
|
|
352
|
+
2. Clone's `initialize(name, symbol, owner=JBTokens)` is called
|
|
353
|
+
3. `JBTokens.tokenOf[projectId]` set to the new token
|
|
354
|
+
|
|
355
|
+
**Events**: `DeployERC20(projectId, deployer, salt, saltHash, caller)`
|
|
356
|
+
|
|
357
|
+
**Edge cases**:
|
|
358
|
+
- Can only be called once per project. If a token is already set, `JBTokens` reverts.
|
|
359
|
+
- `salt` is hashed with `_msgSender()` to prevent front-running deterministic deployments
|
|
360
|
+
- The clone's constructor sets invalid name/symbol; real values come from `initialize()`
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 13. Claim Credits as ERC-20
|
|
365
|
+
|
|
366
|
+
**Entry point**: `JBController.claimTokensFor(address holder, uint256 projectId, uint256 tokenCount, address beneficiary)`
|
|
367
|
+
|
|
368
|
+
**Who can call**: The credit holder or address with `CLAIM_TOKENS` permission.
|
|
369
|
+
|
|
370
|
+
**Parameters**:
|
|
371
|
+
- `holder` -- Address whose credits to convert
|
|
372
|
+
- `projectId` -- Target project
|
|
373
|
+
- `tokenCount` -- Number of credits to convert to ERC-20
|
|
374
|
+
- `beneficiary` -- Address to receive the ERC-20 tokens
|
|
375
|
+
|
|
376
|
+
**State changes**:
|
|
377
|
+
1. `JBTokens.creditBalanceOf[holder][projectId]` decreased by `tokenCount`
|
|
378
|
+
2. ERC-20 tokens minted to `beneficiary` for `tokenCount`
|
|
379
|
+
|
|
380
|
+
**Edge cases**:
|
|
381
|
+
- Requires an ERC-20 token to be deployed for the project (reverts otherwise)
|
|
382
|
+
- Credits and ERC-20 tokens are fungible -- this is a one-way conversion from internal credits to on-chain ERC-20
|
|
383
|
+
- Does not require any ruleset flag (always allowed if ERC-20 exists)
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 14. Process Held Fees
|
|
388
|
+
|
|
389
|
+
**Entry point**: `JBMultiTerminal.processHeldFeesOf(uint256 projectId, address token, uint256 count)`
|
|
390
|
+
|
|
391
|
+
**Who can call**: Anyone.
|
|
392
|
+
|
|
393
|
+
**Parameters**:
|
|
394
|
+
- `projectId` -- Project whose held fees to process
|
|
395
|
+
- `token` -- Token the fees are denominated in
|
|
396
|
+
- `count` -- Maximum number of held fees to process
|
|
397
|
+
|
|
398
|
+
**State changes**:
|
|
399
|
+
1. For each processable fee (unlocked, i.e., `unlockTimestamp <= block.timestamp`):
|
|
400
|
+
- Fee entry deleted from `_heldFeesOf` array
|
|
401
|
+
- `_nextHeldFeeIndexOf` incremented
|
|
402
|
+
- Fee amount sent to project #1's terminal via `_processFee` (try-catch)
|
|
403
|
+
- On failure: fee amount returned to project's balance
|
|
404
|
+
2. If all fees processed: array and index are reset to 0
|
|
405
|
+
|
|
406
|
+
**Events**: `ProcessFee(projectId, token, amount, wasHeld, beneficiary, caller)` per fee, or `FeeReverted(...)` on failure
|
|
407
|
+
|
|
408
|
+
**Edge cases**:
|
|
409
|
+
- Fees unlock after 28 days from when they were held
|
|
410
|
+
- Processing stops at the first locked fee (fees are sequential)
|
|
411
|
+
- Re-reads storage index each iteration (reentrancy-safe)
|
|
412
|
+
- If fee terminal for project #1 does not exist for the token, `_processFee` reverts (caught by try-catch, returns to project balance)
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## 15. Add Price Feeds
|
|
417
|
+
|
|
418
|
+
**Entry point**: `JBController.addPriceFeed(uint256 projectId, uint256 pricingCurrency, uint256 unitCurrency, IJBPriceFeed feed)`
|
|
419
|
+
|
|
420
|
+
**Who can call**: Project owner or address with `ADD_PRICE_FEED` permission.
|
|
421
|
+
|
|
422
|
+
**Parameters**:
|
|
423
|
+
- `projectId` -- Project the feed applies to (0 for protocol-wide default, owner-only)
|
|
424
|
+
- `pricingCurrency` -- Currency the feed's output is in
|
|
425
|
+
- `unitCurrency` -- Currency being priced
|
|
426
|
+
- `feed` -- The price feed contract address
|
|
427
|
+
|
|
428
|
+
**State changes**:
|
|
429
|
+
1. `JBPrices` stores the feed for the `(projectId, pricingCurrency, unitCurrency)` triple
|
|
430
|
+
2. Feed is **immutable** once set -- cannot be replaced or removed
|
|
431
|
+
|
|
432
|
+
**Events**: Emitted by `JBPrices`
|
|
433
|
+
|
|
434
|
+
**Edge cases**:
|
|
435
|
+
- Requires `allowAddPriceFeed` in current ruleset
|
|
436
|
+
- Protocol-wide defaults (`projectId = 0`) can only be set by the `JBPrices` owner
|
|
437
|
+
- `JBPrices` auto-calculates inverse: if A->B exists, B->A is derived. If both explicit and inverse exist, explicit takes priority.
|
|
438
|
+
- Lookup order: project-specific -> project-specific inverse -> default (projectId=0) -> default inverse
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## 16. Set Permissions
|
|
443
|
+
|
|
444
|
+
**Entry point**: `JBPermissions.setPermissionsFor(address account, JBPermissionsData permissionsData)`
|
|
445
|
+
|
|
446
|
+
**Who can call**: The `account` itself, or a ROOT operator for the project (with restrictions).
|
|
447
|
+
|
|
448
|
+
**Parameters**:
|
|
449
|
+
- `account` -- The account granting permissions
|
|
450
|
+
- `permissionsData.operator` -- Address receiving the permissions
|
|
451
|
+
- `permissionsData.projectId` -- Project scope (0 = wildcard, all projects)
|
|
452
|
+
- `permissionsData.permissionIds` -- Array of `uint8` permission IDs to grant
|
|
453
|
+
|
|
454
|
+
**State changes**:
|
|
455
|
+
1. `permissionsOf[operator][account][projectId]` set to packed `uint256` bitmap
|
|
456
|
+
|
|
457
|
+
**Events**: `OperatorPermissionsSet(operator, account, projectId, permissionIds, packed, caller)`
|
|
458
|
+
|
|
459
|
+
**Edge cases**:
|
|
460
|
+
- Permission ID 0 cannot be set (reserved, always `NoZeroPermission` revert)
|
|
461
|
+
- ROOT (ID 1) cannot be set for wildcard `projectId = 0` (`CantSetRootPermissionForWildcardProject`)
|
|
462
|
+
- ROOT operators can set non-ROOT permissions for others on their scoped project
|
|
463
|
+
- ROOT operators CANNOT grant ROOT to other addresses
|
|
464
|
+
- ROOT operators CANNOT set permissions for wildcard `projectId = 0`
|
|
465
|
+
- Setting permissions replaces the entire bitmap (not additive) -- passing an empty array clears all permissions
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## 17. Transfer Project Ownership
|
|
470
|
+
|
|
471
|
+
**Entry point**: `JBProjects.transferFrom(address from, address to, uint256 tokenId)` (standard ERC-721)
|
|
472
|
+
|
|
473
|
+
**Who can call**: The current owner, an approved address, or an operator approved for all.
|
|
474
|
+
|
|
475
|
+
**Parameters**:
|
|
476
|
+
- `from` -- Current owner
|
|
477
|
+
- `to` -- New owner
|
|
478
|
+
- `tokenId` -- The project ID (same as the ERC-721 token ID)
|
|
479
|
+
|
|
480
|
+
**State changes**:
|
|
481
|
+
1. ERC-721 ownership transferred
|
|
482
|
+
2. All `PROJECTS.ownerOf(projectId)` calls now return the new owner
|
|
483
|
+
3. All permission checks that reference the owner now apply to the new owner
|
|
484
|
+
|
|
485
|
+
**Edge cases**:
|
|
486
|
+
- This is a standard ERC-721 transfer. All ERC-721 rules apply (approval, operator, etc.)
|
|
487
|
+
- **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.
|
|
488
|
+
- The new owner immediately gets full control: queue rulesets, set terminals, set splits, etc.
|
|
489
|
+
- Transferring to `address(0)` is prevented by OpenZeppelin's ERC-721 implementation
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## 18. Add to Balance (Without Minting)
|
|
494
|
+
|
|
495
|
+
**Entry point**: `JBMultiTerminal.addToBalanceOf(uint256 projectId, address token, uint256 amount, bool shouldReturnHeldFees, string memo, bytes metadata)`
|
|
496
|
+
|
|
497
|
+
**Who can call**: Anyone.
|
|
498
|
+
|
|
499
|
+
**Parameters**:
|
|
500
|
+
- `projectId` -- Project to add funds to
|
|
501
|
+
- `token` -- Token to add
|
|
502
|
+
- `amount` -- Amount to add (uses `msg.value` for native token)
|
|
503
|
+
- `shouldReturnHeldFees` -- If true, uses the added amount to return held fees (reducing the fee burden)
|
|
504
|
+
- `memo`, `metadata` -- Arbitrary data
|
|
505
|
+
|
|
506
|
+
**State changes**:
|
|
507
|
+
1. Tokens transferred to terminal
|
|
508
|
+
2. If `shouldReturnHeldFees`: iterates held fees, returns fees proportional to amount added
|
|
509
|
+
3. `JBTerminalStore.balanceOf[terminal][projectId][token]` incremented by `amount + returnedFees`
|
|
510
|
+
|
|
511
|
+
**Events**: `AddToBalance(projectId, amount, returnedFees, memo, metadata, caller)`
|
|
512
|
+
|
|
513
|
+
**Edge cases**:
|
|
514
|
+
- Does NOT mint tokens -- purely adds to balance. This increases surplus, which increases cash out value for existing holders.
|
|
515
|
+
- `shouldReturnHeldFees = true` partially or fully returns held fees. The returned fee amount is added to the project balance on top of the deposited amount.
|
|
516
|
+
- Used by terminal migration (`migrateBalanceOf`) and split payouts (when `preferAddToBalance = true`)
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## 19. Transfer Credits
|
|
521
|
+
|
|
522
|
+
**Entry point**: `JBController.transferCreditsFrom(address holder, uint256 projectId, address recipient, uint256 creditCount)`
|
|
523
|
+
|
|
524
|
+
**Who can call**: The credit holder or address with `TRANSFER_CREDITS` permission.
|
|
525
|
+
|
|
526
|
+
**Parameters**:
|
|
527
|
+
- `holder` -- Address transferring credits
|
|
528
|
+
- `projectId` -- Project whose credits are being transferred
|
|
529
|
+
- `recipient` -- Address to receive credits
|
|
530
|
+
- `creditCount` -- Number of credits to transfer
|
|
531
|
+
|
|
532
|
+
**State changes**:
|
|
533
|
+
1. `JBTokens.creditBalanceOf[holder][projectId]` decreased
|
|
534
|
+
2. `JBTokens.creditBalanceOf[recipient][projectId]` increased
|
|
535
|
+
|
|
536
|
+
**Events**: Emitted by `JBTokens`
|
|
537
|
+
|
|
538
|
+
**Edge cases**:
|
|
539
|
+
- Reverts if `pauseCreditTransfers` is set in current ruleset
|
|
540
|
+
- Credits are internal -- they are NOT ERC-20 tokens. Use `claimTokensFor` to convert credits to ERC-20.
|
|
541
|
+
- This only transfers credits, not ERC-20 tokens. ERC-20 tokens are transferred via standard ERC-20 `transfer`/`transferFrom`.
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## 20. Set Project URI
|
|
546
|
+
|
|
547
|
+
**Entry point**: `JBController.setUriOf(uint256 projectId, string uri)`
|
|
548
|
+
|
|
549
|
+
**Who can call**: Project owner or address with `SET_PROJECT_URI` permission.
|
|
550
|
+
|
|
551
|
+
**State changes**: `uriOf[projectId] = uri`
|
|
552
|
+
|
|
553
|
+
**Events**: `SetUri(projectId, uri, caller)`
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## 21. Set Custom Token
|
|
558
|
+
|
|
559
|
+
**Entry point**: `JBController.setTokenFor(uint256 projectId, IJBToken token)`
|
|
560
|
+
|
|
561
|
+
**Who can call**: Project owner or address with `SET_TOKEN` permission.
|
|
562
|
+
|
|
563
|
+
**State changes**: `JBTokens.tokenOf[projectId] = token`
|
|
564
|
+
|
|
565
|
+
**Edge cases**:
|
|
566
|
+
- Requires `allowSetCustomToken` in current or upcoming ruleset
|
|
567
|
+
- Can only be called if no token is currently set for the project
|
|
568
|
+
- The token must conform to `IJBToken` interface (18 decimals required)
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## 22. Set Token Metadata
|
|
573
|
+
|
|
574
|
+
**Entry point**: `JBController.setTokenMetadataOf(uint256 projectId, string name, string symbol)`
|
|
575
|
+
|
|
576
|
+
**Who can call**: Project owner or address with `SET_TOKEN_METADATA` permission.
|
|
577
|
+
|
|
578
|
+
**State changes**: Updates the ERC-20 token's name and symbol via `JBERC20.setNameAndSymbol()`
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## 23. Update Ruleset Weight Cache
|
|
583
|
+
|
|
584
|
+
**Entry point**: `JBRulesets.updateRulesetWeightCache(uint256 projectId, uint256 rulesetId)`
|
|
585
|
+
|
|
586
|
+
**Who can call**: Anyone.
|
|
587
|
+
|
|
588
|
+
**Parameters**:
|
|
589
|
+
- `projectId` -- Target project
|
|
590
|
+
- `rulesetId` -- The specific ruleset to update cache for
|
|
591
|
+
|
|
592
|
+
**State changes**:
|
|
593
|
+
1. `_weightCacheOf[projectId][rulesetId].weight` updated to current decayed weight
|
|
594
|
+
2. `_weightCacheOf[projectId][rulesetId].weightCutMultiple` updated
|
|
595
|
+
|
|
596
|
+
**Events**: `WeightCacheUpdated(projectId, weight, weightCutMultiple, caller)`
|
|
597
|
+
|
|
598
|
+
**Edge cases**:
|
|
599
|
+
- Required for projects with >20,000 cycles (otherwise `currentOf()` reverts with `WeightCacheRequired`)
|
|
600
|
+
- Advances the cache by at most `_WEIGHT_CUT_MULTIPLE_CACHE_LOOKUP_THRESHOLD` (20,000) cycles per call
|
|
601
|
+
- Multiple calls needed to fully catch up for very large cycle gaps
|
|
602
|
+
- No-op if `duration == 0` or `weightCutPercent == 0`
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
## 24. Add Accounting Contexts
|
|
607
|
+
|
|
608
|
+
**Entry point**: `JBMultiTerminal.addAccountingContextsFor(uint256 projectId, JBAccountingContext[] accountingContexts)`
|
|
609
|
+
|
|
610
|
+
**Who can call**: Project owner, address with `ADD_ACCOUNTING_CONTEXTS` permission, or the project's controller.
|
|
611
|
+
|
|
612
|
+
**State changes**:
|
|
613
|
+
1. For each context: validates token decimals, stores `_accountingContextForTokenOf[projectId][token]`
|
|
614
|
+
2. Appends to `_accountingContextsOf[projectId]` array
|
|
615
|
+
|
|
616
|
+
**Events**: `SetAccountingContext(projectId, context, caller)` per token
|
|
617
|
+
|
|
618
|
+
**Edge cases**:
|
|
619
|
+
- Requires `allowAddAccountingContext` in current ruleset (if ruleset exists)
|
|
620
|
+
- Token cannot be added twice (`AccountingContextAlreadySet`)
|
|
621
|
+
- Currency must be non-zero (`ZeroAccountingContextCurrency`)
|
|
622
|
+
- For non-native tokens: decimals are validated against the token's `decimals()` function. Tokens that revert on `decimals()` bypass validation (caller responsible).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
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.10",
|
|
30
30
|
"@chainlink/contracts": "^1.3.0",
|
|
31
31
|
"@openzeppelin/contracts": "^5.6.1",
|
|
32
32
|
"@prb/math": "^4.1.1",
|