@rev-net/core-v6 0.0.23 → 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 +27 -0
- package/ARCHITECTURE.md +53 -129
- package/AUDIT_INSTRUCTIONS.md +108 -375
- package/CHANGELOG.md +65 -0
- package/README.md +76 -175
- package/RISKS.md +16 -4
- package/SKILLS.md +30 -391
- package/STYLE_GUIDE.md +59 -20
- package/USER_JOURNEYS.md +55 -478
- package/package.json +3 -3
- package/references/operations.md +311 -0
- package/references/runtime.md +98 -0
- package/script/Deploy.s.sol +12 -12
- package/src/REVLoans.sol +10 -10
- package/src/interfaces/IREVDeployer.sol +1 -1
- package/test/TestSplitWeightE2E.t.sol +10 -6
- package/test/TestSplitWeightFork.t.sol +10 -6
- package/test/fork/ForkTestBase.sol +10 -6
- package/CHANGE_LOG.md +0 -420
package/SKILLS.md
CHANGED
|
@@ -1,404 +1,43 @@
|
|
|
1
1
|
# Revnet Core
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
Deploy and manage Revnets -- autonomous, unowned Juicebox projects with staged issuance schedules, built-in Uniswap buyback pools, cross-chain suckers, and token-collateralized lending.
|
|
6
|
-
|
|
7
|
-
## Contracts
|
|
8
|
-
|
|
9
|
-
| Contract | Role |
|
|
10
|
-
|----------|------|
|
|
11
|
-
| `REVDeployer` | Deploys revnets, permanently owns the project NFT. Manages stages, splits, auto-issuance, buyback hooks, suckers, split operators, and configuration state storage. Exposes `OWNER()` view returning the REVOwner address. Calls DEPLOYER-restricted setters on REVOwner during deployment to store `cashOutDelayOf` and `tiered721HookOf`. |
|
|
12
|
-
| `REVOwner` | Runtime hook contract for all revnets. Implements `IJBRulesetDataHook` + `IJBCashOutHook`. Set as the `dataHook` in each revnet's ruleset metadata. Handles pay hooks, cash-out hooks, mint permissions, and sucker verification. Stores `cashOutDelayOf` and `tiered721HookOf` mappings (set by REVDeployer via DEPLOYER-restricted setters `setCashOutDelayOf()` and `setTiered721HookOf()`). (~310 lines) |
|
|
13
|
-
| `REVLoans` | Issues token-collateralized loans from revnet treasuries. Each loan is an ERC-721 NFT. Burns collateral on borrow, re-mints on repay. Charges tiered fees (REV protocol fee + source fee + prepaid fee). (~1,359 lines) |
|
|
14
|
-
|
|
15
|
-
## Key Functions
|
|
16
|
-
|
|
17
|
-
### Deployment
|
|
18
|
-
|
|
19
|
-
| Function | Permissions | What it does |
|
|
20
|
-
|----------|------------|-------------|
|
|
21
|
-
| `REVDeployer.deployFor(revnetId, config, terminals, suckerConfig)` | Permissionless | Deploy a new revnet (`revnetId=0`) or convert an existing Juicebox project. Encodes stage configs into rulesets, deploys ERC-20 token, initializes buyback pool at 1:1 price, sets up split operator, suckers, loans permissions, and deploys a default empty tiered ERC-721 hook. |
|
|
22
|
-
| `REVDeployer.deployFor(revnetId, config, terminals, suckerConfig, hookConfig, allowedPosts)` | Permissionless | Same as `deployFor` but deploys a tiered ERC-721 hook with pre-configured tiers. Optionally configures Croptop posting criteria and grants publisher permission to add tiers. |
|
|
23
|
-
| `REVDeployer.deploySuckersFor(revnetId, suckerConfig)` | Split operator | Deploy new cross-chain suckers post-launch. Validates ruleset allows sucker deployment (bit 2 of `extraMetadata`). Uses stored config hash for cross-chain matching. |
|
|
24
|
-
|
|
25
|
-
### Data Hooks (REVOwner)
|
|
26
|
-
|
|
27
|
-
| Function | Permissions | What it does |
|
|
28
|
-
|----------|------------|-------------|
|
|
29
|
-
| `REVOwner.beforePayRecordedWith(context)` | Terminal callback | Calls the 721 hook first for split specs, then calls the buyback hook with a reduced amount context (payment minus split amount). Adjusts the returned weight proportionally for splits (`weight = mulDiv(weight, amount - splitAmount, amount)`) so the terminal only mints tokens for the amount entering the project. Assembles pay hook specs (721 hook specs first, then buyback spec). Reads `tiered721HookOf` from REVOwner storage. |
|
|
30
|
-
| `REVOwner.beforeCashOutRecordedWith(context)` | Terminal callback | If sucker: returns full amount with 0 tax (fee exempt). Otherwise: calculates 2.5% fee, enforces 30-day cash-out delay (reads `cashOutDelayOf` from REVOwner storage), returns modified count + fee hook spec. |
|
|
31
|
-
| `REVOwner.afterCashOutRecordedWith(context)` | Permissionless | Cash-out hook callback. Receives fee amount and pays it to the fee revnet's terminal. Falls back to returning funds if fee payment fails. |
|
|
32
|
-
| `REVOwner.hasMintPermissionFor(revnetId, ruleset, addr)` | View | Returns `true` for: loans contract, buyback hook, buyback hook delegates, or suckers. |
|
|
33
|
-
| `REVOwner.cashOutDelayOf(revnetId)` | View | Returns the cash-out delay timestamp from REVOwner storage. Exposed for REVLoans compatibility (REVLoans imports IREVOwner for this). |
|
|
34
|
-
|
|
35
|
-
### Split Operator
|
|
36
|
-
|
|
37
|
-
| Function | Permissions | What it does |
|
|
38
|
-
|----------|------------|-------------|
|
|
39
|
-
| `REVDeployer.setSplitOperatorOf(revnetId, newOperator)` | Split operator | Replace the current split operator. Revokes old permissions, grants new ones. |
|
|
40
|
-
|
|
41
|
-
### Auto-Issuance
|
|
42
|
-
|
|
43
|
-
| Function | Permissions | What it does |
|
|
44
|
-
|----------|------------|-------------|
|
|
45
|
-
| `REVDeployer.autoIssueFor(revnetId, stageId, beneficiary)` | Permissionless | Mint pre-configured auto-issuance tokens for a beneficiary once a stage has started. One-time per stage per beneficiary. |
|
|
46
|
-
| `REVDeployer.burnHeldTokensOf(revnetId)` | Permissionless | Burn any reserved tokens held by the deployer (when splits < 100%). |
|
|
47
|
-
|
|
48
|
-
### Loans -- Borrowing
|
|
49
|
-
|
|
50
|
-
| Function | Permissions | What it does |
|
|
51
|
-
|----------|------------|-------------|
|
|
52
|
-
| `REVLoans.borrowFrom(revnetId, source, minBorrowAmount, collateralCount, beneficiary, prepaidFeePercent)` | Permissionless (caller must grant BURN_TOKENS to REVLoans) | Open a loan: enforce cash-out delay if set (cross-chain deployment protection), burn collateral tokens, pull funds from revnet via `useAllowanceOf`, pay REV fee (1%) + terminal fee (2.5%), transfer remainder to beneficiary, mint loan NFT. |
|
|
53
|
-
| `REVLoans.repayLoan(loanId, maxRepayBorrowAmount, collateralCountToReturn, beneficiary, allowance)` | Loan NFT owner | Repay fully or partially. Returns funds to revnet via `addToBalanceOf`, re-mints collateral tokens, burns/replaces the loan NFT. Supports permit2 signatures. |
|
|
54
|
-
| `REVLoans.reallocateCollateralFromLoan(loanId, collateralCountToTransfer, source, minBorrowAmount, collateralCountToAdd, beneficiary, prepaidFeePercent)` | Loan NFT owner | Refinance: remove excess collateral from an existing loan and open a new loan with the freed collateral. Burns original, mints two replacements. |
|
|
55
|
-
| `REVLoans.liquidateExpiredLoansFrom(revnetId, startingLoanId, count)` | Permissionless | Clean up loans past the 10-year liquidation duration. Burns NFTs and decrements accounting totals. Collateral is permanently lost. |
|
|
56
|
-
| `REVLoans.setTokenUriResolver(resolver)` | Contract owner (`onlyOwner`) | Set the `IJBTokenUriResolver` used for loan NFT token URIs. |
|
|
57
|
-
|
|
58
|
-
### Loans -- Views
|
|
59
|
-
|
|
60
|
-
| Function | What it does |
|
|
61
|
-
|----------|-------------|
|
|
62
|
-
| `REVLoans.borrowableAmountFrom(revnetId, collateralCount, decimals, currency)` | Calculate how much can be borrowed for a given collateral amount. Returns 0 during the cash-out delay period. Aggregates surplus from all terminals, applies bonding curve. |
|
|
63
|
-
| `REVLoans.determineSourceFeeAmount(loan, amount)` | Calculate the time-proportional source fee for a loan repayment. Zero during prepaid window, linear accrual after. |
|
|
64
|
-
| `REVLoans.loanOf(loanId)` | Returns the full `REVLoan` struct for a loan. |
|
|
65
|
-
| `REVLoans.loanSourcesOf(revnetId)` | Returns all `(terminal, token)` pairs used for loans by a revnet. |
|
|
66
|
-
| `REVLoans.revnetIdOfLoanWith(loanId)` | Decode the revnet ID from a loan ID (`loanId / 1_000_000_000_000`). |
|
|
67
|
-
|
|
68
|
-
## Integration Points
|
|
69
|
-
|
|
70
|
-
| Dependency | Import | Used For |
|
|
71
|
-
|------------|--------|----------|
|
|
72
|
-
| `@bananapus/core-v6` | `IJBController`, `IJBDirectory`, `IJBPermissions`, `IJBProjects`, `IJBTerminal`, `IJBPrices`, `JBConstants`, `JBCashOuts`, `JBSurplus` | Project lifecycle, rulesets, token minting/burning, fund access, terminal payments, price feeds, bonding curve |
|
|
73
|
-
| `@bananapus/721-hook-v6` | `IJB721TiersHook`, `IJB721TiersHookDeployer` | Deploying and registering tiered ERC-721 pay hooks |
|
|
74
|
-
| `@bananapus/buyback-hook-v6` | `IJBBuybackHookRegistry` | Configuring Uniswap buyback pools per revnet |
|
|
75
|
-
| `@bananapus/suckers-v6` | `IJBSuckerRegistry` | Deploying cross-chain suckers, checking sucker status for fee exemption |
|
|
76
|
-
| `@croptop/core-v6` | `CTPublisher` | Configuring Croptop posting criteria for 721 tiers |
|
|
77
|
-
| `@bananapus/permission-ids-v6` | `JBPermissionIds` | Permission ID constants (SET_SPLIT_GROUPS, USE_ALLOWANCE, etc.) |
|
|
78
|
-
| `@openzeppelin/contracts` | `ERC721`, `ERC2771Context`, `Ownable`, `SafeERC20` | Loan NFTs, meta-transactions, ownership, safe token transfers |
|
|
79
|
-
| `@uniswap/permit2` | `IPermit2`, `IAllowanceTransfer` | Gasless token approvals for loan repayments |
|
|
80
|
-
| `@prb/math` | `mulDiv` | Precise fixed-point multiplication and division |
|
|
81
|
-
|
|
82
|
-
## Key Types
|
|
83
|
-
|
|
84
|
-
| Struct | Key Fields | Used In |
|
|
85
|
-
|--------|------------|---------|
|
|
86
|
-
| `REVConfig` | `description` (REVDescription), `baseCurrency`, `splitOperator`, `stageConfigurations[]` | `deployFor` |
|
|
87
|
-
| `REVStageConfig` | `startsAtOrAfter` (uint48), `initialIssuance` (uint112), `issuanceCutFrequency` (uint32), `issuanceCutPercent` (uint32), `cashOutTaxRate` (uint16), `splitPercent` (uint16), `splits[]`, `autoIssuances[]`, `extraMetadata` (uint16) | Translated into `JBRulesetConfig` |
|
|
88
|
-
| `REVDescription` | `name`, `ticker`, `uri`, `salt` | ERC-20 token deployment and project metadata |
|
|
89
|
-
| `REVAutoIssuance` | `chainId` (uint32), `count` (uint104), `beneficiary` | Per-stage cross-chain token auto-minting |
|
|
90
|
-
| `REVLoan` | `amount` (uint112), `collateral` (uint112), `createdAt` (uint48), `prepaidFeePercent` (uint16), `prepaidDuration` (uint32), `source` (REVLoanSource) | Per-loan state in `REVLoans` |
|
|
91
|
-
| `REVLoanSource` | `token`, `terminal` (IJBPayoutTerminal) | Identifies which terminal and token a loan draws from |
|
|
92
|
-
| `REVDeploy721TiersHookConfig` | `baseline721HookConfiguration` (REVBaseline721HookConfig), `salt`, `preventSplitOperatorAdjustingTiers`, `preventSplitOperatorUpdatingMetadata`, `preventSplitOperatorMinting`, `preventSplitOperatorIncreasingDiscountPercent` | 721 hook deployment with operator permissions (preventive flags — `false` = allowed). Uses `REVBaseline721HookConfig` (not `JBDeploy721TiersHookConfig`) to omit `issueTokensForSplits` — revnets always force it to `false`. |
|
|
93
|
-
| `REVBaseline721HookConfig` | `name`, `symbol`, `baseUri`, `tokenUriResolver`, `contractUri`, `tiersConfig`, `reserveBeneficiary`, `flags` (REV721TiersHookFlags) | Same as `JBDeploy721TiersHookConfig` but uses `REV721TiersHookFlags` which omits `issueTokensForSplits`. |
|
|
94
|
-
| `REV721TiersHookFlags` | `noNewTiersWithReserves`, `noNewTiersWithVotes`, `noNewTiersWithOwnerMinting`, `preventOverspending` | Same as `JB721TiersHookFlags` minus `issueTokensForSplits`. Revnets do their own weight adjustment for splits. |
|
|
95
|
-
| `REVCroptopAllowedPost` | `category` (uint24), `minimumPrice` (uint104), `minimumTotalSupply` (uint32), `maximumTotalSupply` (uint32), `allowedAddresses[]` | Croptop posting criteria |
|
|
96
|
-
| `REVSuckerDeploymentConfig` | `deployerConfigurations[]`, `salt` | Cross-chain sucker deployment |
|
|
97
|
-
|
|
98
|
-
## Events
|
|
99
|
-
|
|
100
|
-
### REVDeployer
|
|
101
|
-
|
|
102
|
-
| Event | When It Fires |
|
|
103
|
-
|-------|---------------|
|
|
104
|
-
| `AutoIssue(revnetId, stageId, beneficiary, count, caller)` | When tokens are auto-issued for a beneficiary during a stage via `autoIssueFor`. |
|
|
105
|
-
| `BurnHeldTokens(revnetId, count, caller)` | When held tokens are burned from the deployer contract via `burnHeldTokensOf`. |
|
|
106
|
-
| `DeployRevnet(revnetId, configuration, terminalConfigurations, suckerDeploymentConfiguration, rulesetConfigurations, encodedConfigurationHash, caller)` | When a new revnet is deployed via `deployFor`. |
|
|
107
|
-
| `DeploySuckers(revnetId, encodedConfigurationHash, suckerDeploymentConfiguration, caller)` | When suckers are deployed for a revnet via `deploySuckersFor`. |
|
|
108
|
-
| `ReplaceSplitOperator(revnetId, newSplitOperator, caller)` | When the split operator of a revnet is replaced via `setSplitOperatorOf`. |
|
|
109
|
-
| `SetCashOutDelay(revnetId, cashOutDelay, caller)` | When the cash out delay is set for a revnet during deployment to a new chain. |
|
|
110
|
-
| `StoreAutoIssuanceAmount(revnetId, stageId, beneficiary, count, caller)` | When an auto-issuance amount is stored for a beneficiary during deployment. |
|
|
111
|
-
|
|
112
|
-
### REVLoans
|
|
113
|
-
|
|
114
|
-
| Event | When It Fires |
|
|
115
|
-
|-------|---------------|
|
|
116
|
-
| `Borrow(loanId, revnetId, loan, source, borrowAmount, collateralCount, sourceFeeAmount, beneficiary, caller)` | When a loan is created by borrowing from a revnet via `borrowFrom`. |
|
|
117
|
-
| `Liquidate(loanId, revnetId, loan, caller)` | When a loan is liquidated after exceeding the 10-year liquidation duration via `liquidateExpiredLoansFrom`. |
|
|
118
|
-
| `ReallocateCollateral(loanId, revnetId, reallocatedLoanId, reallocatedLoan, removedCollateralCount, caller)` | When collateral is reallocated from one loan to a new loan via `reallocateCollateralFromLoan`. |
|
|
119
|
-
| `RepayLoan(loanId, revnetId, paidOffLoanId, loan, paidOffLoan, repayBorrowAmount, sourceFeeAmount, collateralCountToReturn, beneficiary, caller)` | When a loan is repaid via `repayLoan`. |
|
|
120
|
-
| `SetTokenUriResolver(resolver, caller)` | When the token URI resolver is changed via `setTokenUriResolver`. |
|
|
121
|
-
|
|
122
|
-
## Errors
|
|
123
|
-
|
|
124
|
-
### REVDeployer
|
|
125
|
-
|
|
126
|
-
| Error | When It Fires |
|
|
127
|
-
|-------|---------------|
|
|
128
|
-
| `REVDeployer_AutoIssuanceBeneficiaryZeroAddress()` | When an auto-issuance config has a zero-address beneficiary. |
|
|
129
|
-
| `REVDeployer_CashOutDelayNotFinished(cashOutDelay, blockTimestamp)` | When a cash out is attempted before the 30-day delay has elapsed. |
|
|
130
|
-
| `REVDeployer_CashOutsCantBeTurnedOffCompletely(cashOutTaxRate, maxCashOutTaxRate)` | When `cashOutTaxRate` equals `MAX_CASH_OUT_TAX_RATE` (10,000). Must be strictly less. |
|
|
131
|
-
| `REVDeployer_MustHaveSplits()` | When a stage with `splitPercent > 0` has no splits configured. |
|
|
132
|
-
| `REVDeployer_NothingToAutoIssue()` | When `autoIssueFor` is called but no tokens are available for auto-issuance. |
|
|
133
|
-
| `REVDeployer_NothingToBurn()` | When `burnHeldTokensOf` is called but the deployer holds no tokens. |
|
|
134
|
-
| `REVDeployer_RulesetDoesNotAllowDeployingSuckers()` | When `deploySuckersFor` is called but the current ruleset's `extraMetadata` bit 2 is not set. |
|
|
135
|
-
| `REVDeployer_StageNotStarted(stageId)` | When `autoIssueFor` is called for a stage that hasn't started yet. |
|
|
136
|
-
| `REVDeployer_StagesRequired()` | When `deployFor` is called with zero stage configurations. |
|
|
137
|
-
| `REVDeployer_StageTimesMustIncrease()` | When stage `startsAtOrAfter` values are not strictly increasing. |
|
|
138
|
-
| `REVDeployer_Unauthorized(revnetId, caller)` | When a non-split-operator calls a split-operator-only function. |
|
|
139
|
-
|
|
140
|
-
### REVLoans
|
|
141
|
-
|
|
142
|
-
| Error | When It Fires |
|
|
143
|
-
|-------|---------------|
|
|
144
|
-
| `REVLoans_CashOutDelayNotFinished(cashOutDelay, blockTimestamp)` | When borrowing during the 30-day cash-out delay period (cross-chain deployment protection). |
|
|
145
|
-
| `REVLoans_CollateralExceedsLoan(collateralToReturn, loanCollateral)` | When trying to return more collateral than the loan holds. |
|
|
146
|
-
| `REVLoans_InvalidPrepaidFeePercent(prepaidFeePercent, min, max)` | When `prepaidFeePercent` is outside the allowed range (2.5%--50%). |
|
|
147
|
-
| `REVLoans_InvalidTerminal(terminal, revnetId)` | When the specified terminal is not registered for the revnet. |
|
|
148
|
-
| `REVLoans_LoanExpired(timeSinceLoanCreated, loanLiquidationDuration)` | When trying to repay or reallocate an expired loan. |
|
|
149
|
-
| `REVLoans_LoanIdOverflow()` | When the loan ID counter exceeds the per-revnet trillion-ID namespace. |
|
|
150
|
-
| `REVLoans_NewBorrowAmountGreaterThanLoanAmount(newBorrowAmount, loanAmount)` | When a reallocation would produce a reduced loan with a larger borrow amount than the original. |
|
|
151
|
-
| `REVLoans_NoMsgValueAllowed()` | When `msg.value > 0` on a non-native-token repayment. |
|
|
152
|
-
| `REVLoans_NotEnoughCollateral()` | When the caller does not have enough tokens for the requested collateral. |
|
|
153
|
-
| `REVLoans_NothingToRepay()` | When `repayLoan` is called with zero repay amount and zero collateral to return. |
|
|
154
|
-
| `REVLoans_OverMaxRepayBorrowAmount(maxRepayBorrowAmount, repayBorrowAmount)` | When the actual repay cost exceeds the caller's `maxRepayBorrowAmount`. |
|
|
155
|
-
| `REVLoans_OverflowAlert(value, limit)` | When a value would overflow `uint112` storage. |
|
|
156
|
-
| `REVLoans_PermitAllowanceNotEnough(allowanceAmount, requiredAmount)` | When the permit2 allowance is insufficient for the repayment. |
|
|
157
|
-
| `REVLoans_ReallocatingMoreCollateralThanBorrowedAmountAllows(newBorrowAmount, loanAmount)` | When the collateral being transferred out would leave the original loan undercollateralized. |
|
|
158
|
-
| `REVLoans_SourceMismatch()` | When `reallocateCollateralFromLoan` specifies a source that doesn't match the existing loan's source. |
|
|
159
|
-
| `REVLoans_Unauthorized(caller, owner)` | When a non-owner tries to repay or reallocate someone else's loan. |
|
|
160
|
-
| `REVLoans_UnderMinBorrowAmount(minBorrowAmount, borrowAmount)` | When the actual borrow amount is less than the caller's `minBorrowAmount`. |
|
|
161
|
-
| `REVLoans_ZeroBorrowAmount()` | When a borrow or reallocation would result in zero borrowed funds. |
|
|
162
|
-
| `REVLoans_ZeroCollateralLoanIsInvalid()` | When a loan would end up with zero collateral. |
|
|
163
|
-
|
|
164
|
-
## Constants
|
|
165
|
-
|
|
166
|
-
### REVDeployer
|
|
3
|
+
## Use This File For
|
|
167
4
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
| `CASH_OUT_DELAY` | 2,592,000 (30 days) | Prevents cross-chain liquidity arbitrage on new chain deployments |
|
|
171
|
-
| `FEE` | 25 (of MAX_FEE=1000) | 2.5% cash-out fee paid to fee revnet |
|
|
172
|
-
| `DEFAULT_BUYBACK_POOL_FEE` | 10,000 | 1% Uniswap fee tier for default buyback pools |
|
|
173
|
-
| `DEFAULT_BUYBACK_TWAP_WINDOW` | 2 days | TWAP observation window for buyback price |
|
|
174
|
-
| `DEFAULT_BUYBACK_TICK_SPACING` | 200 | Tick spacing for default buyback V4 pools |
|
|
5
|
+
- Use this file when the task involves revnet deployment, staged issuance, split operator logic, auto-issuance, or the revnet loan system built on top of Juicebox core.
|
|
6
|
+
- Start here, then open the deployer, owner, or loans contract depending on whether the issue is launch-time config, runtime hook behavior, or debt accounting.
|
|
175
7
|
|
|
176
|
-
|
|
8
|
+
## Read This Next
|
|
177
9
|
|
|
178
|
-
|
|
|
179
|
-
|
|
180
|
-
|
|
|
181
|
-
|
|
|
182
|
-
|
|
|
183
|
-
|
|
|
184
|
-
|
|
|
10
|
+
| If you need... | Open this next |
|
|
11
|
+
|---|---|
|
|
12
|
+
| Repo overview and operator flow | [`README.md`](./README.md) |
|
|
13
|
+
| Deployment and stage config | [`src/REVDeployer.sol`](./src/REVDeployer.sol), [`script/Deploy.s.sol`](./script/Deploy.s.sol) |
|
|
14
|
+
| Runtime owner and data-hook behavior | [`src/REVOwner.sol`](./src/REVOwner.sol) |
|
|
15
|
+
| Loan accounting and liquidation behavior | [`src/REVLoans.sol`](./src/REVLoans.sol) |
|
|
16
|
+
| Types and helpers | [`src/structs/`](./src/structs/), [`src/interfaces/`](./src/interfaces/), [`test/helpers/`](./test/helpers/) |
|
|
17
|
+
| Loan regressions, economics, and forks | [`test/REVLoansRegressions.t.sol`](./test/REVLoansRegressions.t.sol), [`test/TestLongTailEconomics.t.sol`](./test/TestLongTailEconomics.t.sol), [`test/fork/`](./test/fork/), [`test/regression/`](./test/regression/) |
|
|
185
18
|
|
|
186
|
-
##
|
|
19
|
+
## Repo Map
|
|
187
20
|
|
|
188
|
-
|
|
21
|
+
| Area | Where to look |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Main contracts | [`src/`](./src/) |
|
|
24
|
+
| Scripts | [`script/`](./script/) |
|
|
25
|
+
| Types | [`src/structs/`](./src/structs/), [`src/interfaces/`](./src/interfaces/) |
|
|
26
|
+
| Tests | [`test/`](./test/) |
|
|
189
27
|
|
|
190
|
-
|
|
191
|
-
|---------|-----------|------|---------|
|
|
192
|
-
| `amountToAutoIssue` | `public` | `revnetId => stageId => beneficiary => uint256` | Premint tokens per stage per beneficiary |
|
|
193
|
-
| `hashedEncodedConfigurationOf` | `public` | `revnetId => bytes32` | Config hash for cross-chain sucker validation |
|
|
194
|
-
| `_extraOperatorPermissions` | `internal` | `revnetId => uint256[]` | Custom permissions for split operator (no auto-getter) |
|
|
195
|
-
|
|
196
|
-
### REVOwner
|
|
197
|
-
|
|
198
|
-
| Mapping | Visibility | Type | Purpose |
|
|
199
|
-
|---------|-----------|------|---------|
|
|
200
|
-
| `DEPLOYER` | `public` | `address` | REVDeployer address (storage variable, set once via `setDeployer()` called from REVDeployer's constructor) |
|
|
201
|
-
| `cashOutDelayOf` | `public` | `revnetId => uint256` | Timestamp when cash outs unlock (0 = no delay). Set by REVDeployer via `setCashOutDelayOf()`. |
|
|
202
|
-
| `tiered721HookOf` | `public` | `revnetId => address` | Deployed 721 hook address (if any). Set by REVDeployer via `setTiered721HookOf()`. |
|
|
203
|
-
|
|
204
|
-
### REVLoans
|
|
205
|
-
|
|
206
|
-
| Mapping | Visibility | Type | Purpose |
|
|
207
|
-
|---------|-----------|------|---------|
|
|
208
|
-
| `isLoanSourceOf` | `public` | `revnetId => terminal => token => bool` | Is this (terminal, token) pair used for loans? |
|
|
209
|
-
| `totalLoansBorrowedFor` | `public` | `revnetId => uint256` | Counter for loan numbering |
|
|
210
|
-
| `totalBorrowedFrom` | `public` | `revnetId => terminal => token => uint256` | Tracks debt per loan source |
|
|
211
|
-
| `totalCollateralOf` | `public` | `revnetId => uint256` | Sum of all burned collateral |
|
|
212
|
-
| `_loanOf` | `internal` | `loanId => REVLoan` | Per-loan state (use `loanOf(loanId)` view) |
|
|
213
|
-
| `_loanSourcesOf` | `internal` | `revnetId => REVLoanSource[]` | Array of all loan sources used (use `loanSourcesOf(revnetId)` view) |
|
|
214
|
-
| `tokenUriResolver` | `public` | `IJBTokenUriResolver` | Resolver for loan NFT token URIs |
|
|
215
|
-
|
|
216
|
-
## Gotchas
|
|
217
|
-
|
|
218
|
-
1. **Revnets are permanently ownerless.** `REVDeployer` holds the project NFT forever. There is no function to release it. Stage parameters cannot be changed after deployment.
|
|
219
|
-
2. **Collateral is burned, not held.** Unlike traditional lending, collateral tokens are destroyed at borrow time and re-minted on repay. If a loan liquidates after 10 years, the collateral is permanently lost.
|
|
220
|
-
3. **100% LTV by design.** Borrowable amount equals the pro-rata cash-out value. No safety margin unless the stage has `cashOutTaxRate > 0`. A tax of 20% creates ~20% effective collateral buffer.
|
|
221
|
-
4. **Loan ID encoding.** `loanId = revnetId * 1_000_000_000_000 + loanNumber`. Each revnet supports ~1 trillion loans. Use `revnetIdOfLoanWith(loanId)` to decode.
|
|
222
|
-
5. **uint112 truncation risk.** `REVLoan.amount` and `REVLoan.collateral` are `uint112`. Values above ~5.19e33 truncate silently.
|
|
223
|
-
6. **Auto-issuance stage IDs.** Computed as `block.timestamp + i` during deployment. These match the Juicebox ruleset IDs because `JBRulesets` assigns IDs the same way (`latestId >= block.timestamp ? latestId + 1 : block.timestamp`), producing identical sequential IDs when all stages are queued in a single `deployFor()` call.
|
|
224
|
-
7. **Cash-out fee stacking.** Cash outs incur both the Juicebox terminal fee (2.5%) and the revnet cash-out fee (2.5% to fee revnet). These compound. The 2.5% fee is deducted from the TOKEN AMOUNT being cashed out, not from the reclaim value. 2.5% of the tokens are redirected to the fee revnet, which then redeems them at the bonding curve independently. The net reclaim to the caller is based on 97.5% of the tokens, not 97.5% of the computed ETH value. This is by design.
|
|
225
|
-
8. **30-day cash-out delay.** Applied when deploying an existing revnet to a new chain where the first stage has already started. Prevents cross-chain liquidity arbitrage. Enforced in both `beforeCashOutRecordedWith` (direct cash outs) and `REVLoans.borrowFrom` / `borrowableAmountFrom` (loans). The delay is stored on REVOwner (`cashOutDelayOf(revnetId)`) and set by REVDeployer during deployment via `setCashOutDelayOf()`. REVLoans imports IREVOwner (not IREVDeployer) to read it.
|
|
226
|
-
9. **`cashOutTaxRate` cannot be MAX.** Must be strictly less than `MAX_CASH_OUT_TAX_RATE` (10,000). Revnets cannot fully disable cash outs.
|
|
227
|
-
10. **Split operator is singular.** Only ONE address can be split operator at a time. The operator can replace itself via `setSplitOperatorOf` but cannot delegate or multi-sig.
|
|
228
|
-
11. **NATIVE_TOKEN on non-ETH chains.** `JBConstants.NATIVE_TOKEN` on Celo means CELO, on Polygon means MATIC -- not ETH. Use ERC-20 WETH instead. The config matching hash does NOT catch terminal configuration differences.
|
|
229
|
-
12. **Loan source array is unbounded.** `_loanSourcesOf[revnetId]` grows without limit. No validation that a terminal is actually registered for the project.
|
|
230
|
-
13. **Flash-loan surplus exposure.** `borrowableAmountFrom` reads live surplus. A flash loan can temporarily inflate the treasury to borrow more than the sustained value supports.
|
|
231
|
-
14. **Fee revnet must have terminals.** Cash-out fees and loan protocol fees are paid to `FEE_REVNET_ID`. If that project has no terminal for the token, the fee silently fails (try-catch).
|
|
232
|
-
15. **Buyback hook is immutable per deployer.** `BUYBACK_HOOK` is set at construction time on both REVDeployer and REVOwner. All revnets deployed by the same deployer share the same buyback hook.
|
|
233
|
-
16. **Cross-chain config matching.** `hashedEncodedConfigurationOf` covers economic parameters (baseCurrency, stages, auto-issuances) but NOT terminal configurations, accounting contexts, or sucker token mappings. Two deployments with identical hashes can have different terminal setups.
|
|
234
|
-
17. **Loan fee model has three layers.** See Constants table for exact values: REV protocol fee, terminal fee, and prepaid source fee (borrower-chosen, buys interest-free window). After the prepaid window, source fee accrues linearly over the remaining loan duration.
|
|
235
|
-
18. **Permit2 fallback.** `REVLoans` uses permit2 for ERC-20 transfers as a fallback when standard allowance is insufficient. Wrapped in try-catch.
|
|
236
|
-
19. **39.16% cash-out tax crossover.** Below ~39% cash-out tax, cashing out is more capital-efficient than borrowing. Above ~39%, loans become more efficient because they preserve upside while providing liquidity. Based on CryptoEconLab academic research. Design implication: revnets intended for active token trading should consider this threshold when setting `cashOutTaxRate`.
|
|
237
|
-
20. **REVDeployer always deploys a 721 hook** via `HOOK_DEPLOYER.deployHookFor` — even if `baseline721HookConfiguration` has empty tiers. This is correct by design: it lets the split operator add and sell NFTs later without migration. Non-revnet projects should follow the same pattern by using `JB721TiersHookProjectDeployer.launchProjectFor` (or `JBOmnichainDeployer.launchProjectFor`) instead of bare `launchProjectFor`.
|
|
238
|
-
21. **REVOwner circular dependency.** REVOwner and REVDeployer have a circular dependency broken by `setDeployer()`, called atomically from REVDeployer's constructor. `REVOwner.DEPLOYER` is a storage variable (not immutable). `setDeployer()` sets `msg.sender` as `DEPLOYER` if not already set -- reverts if already set. If `setDeployer()` is never called, all runtime hook behavior breaks. Deploy order: REVOwner first, then REVDeployer(owner=REVOwner) -- the constructor calls `REVOwner.setDeployer()` atomically. REVOwner stores `cashOutDelayOf` and `tiered721HookOf` mappings, which are set by REVDeployer via DEPLOYER-restricted setters (`setCashOutDelayOf()`, `setTiered721HookOf()`).
|
|
239
|
-
|
|
240
|
-
### NATIVE_TOKEN Accounting on Non-ETH Chains
|
|
241
|
-
|
|
242
|
-
When deploying to a chain where the native token is NOT ETH (Celo, Polygon), the terminal must NOT use `JBConstants.NATIVE_TOKEN` as its accounting context. `NATIVE_TOKEN` represents whatever is native on that chain, but `baseCurrency=1` (ETH) assumes ETH-denominated value.
|
|
243
|
-
|
|
244
|
-
**Correct (Celo):**
|
|
245
|
-
```solidity
|
|
246
|
-
JBAccountingContext({
|
|
247
|
-
token: WETH_CELO, // ERC-20 WETH, not native CELO
|
|
248
|
-
decimals: 18,
|
|
249
|
-
currency: uint32(uint160(WETH_CELO))
|
|
250
|
-
})
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
**Wrong (Celo):**
|
|
254
|
-
```solidity
|
|
255
|
-
JBAccountingContext({
|
|
256
|
-
token: JBConstants.NATIVE_TOKEN, // This is CELO, not ETH!
|
|
257
|
-
decimals: 18,
|
|
258
|
-
currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
259
|
-
})
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
## Reading Revnet State
|
|
263
|
-
|
|
264
|
-
Quick-reference for common read operations. All functions are `view`/`pure` and permissionless.
|
|
265
|
-
|
|
266
|
-
### Current Stage & Ruleset
|
|
267
|
-
|
|
268
|
-
| What | Call | Returns |
|
|
269
|
-
|------|------|---------|
|
|
270
|
-
| Current ruleset (stage) | `IJBController(CONTROLLER).currentRulesetOf(revnetId)` | `(JBRuleset, JBRulesetMetadata)` -- the active stage's parameters |
|
|
271
|
-
| All queued rulesets | `IJBController(CONTROLLER).allRulesetsOf(revnetId, startingId, size)` | `JBRulesetWithMetadata[]` -- paginated list of stages |
|
|
272
|
-
| Specific stage by ID | `IJBController(CONTROLLER).getRulesetOf(revnetId, stageId)` | `(JBRuleset, JBRulesetMetadata)` for that stage |
|
|
273
|
-
|
|
274
|
-
### Split Operator
|
|
275
|
-
|
|
276
|
-
| What | Call | Returns |
|
|
277
|
-
|------|------|---------|
|
|
278
|
-
| Check if address is split operator | `REVDeployer.isSplitOperatorOf(revnetId, addr)` | `bool` |
|
|
279
|
-
|
|
280
|
-
### Token Supply & Surplus
|
|
281
|
-
|
|
282
|
-
| What | Call | Returns |
|
|
283
|
-
|------|------|---------|
|
|
284
|
-
| Total supply (incl. pending reserved) | `IJBController(CONTROLLER).totalTokenSupplyWithReservedTokensOf(revnetId)` | `uint256` |
|
|
285
|
-
| Pending reserved tokens | `IJBController(CONTROLLER).pendingReservedTokenBalanceOf(revnetId)` | `uint256` |
|
|
286
|
-
| Current surplus (single terminal) | `IJBTerminalStore(STORE).currentSurplusOf(terminal, revnetId, configs, decimals, currency)` | `uint256` |
|
|
287
|
-
|
|
288
|
-
### Auto-Issuance
|
|
289
|
-
|
|
290
|
-
| What | Call | Returns |
|
|
291
|
-
|------|------|---------|
|
|
292
|
-
| Remaining auto-issuance for beneficiary | `REVDeployer.amountToAutoIssue(revnetId, stageId, beneficiary)` | `uint256` (0 if already claimed) |
|
|
293
|
-
|
|
294
|
-
### Loans
|
|
295
|
-
|
|
296
|
-
| What | Call | Returns |
|
|
297
|
-
|------|------|---------|
|
|
298
|
-
| Borrowable amount for collateral | `REVLoans.borrowableAmountFrom(revnetId, collateralCount, decimals, currency)` | `uint256` |
|
|
299
|
-
| Total borrowed (per source) | `REVLoans.totalBorrowedFrom(revnetId, terminal, token)` | `uint256` |
|
|
300
|
-
| Total collateral locked | `REVLoans.totalCollateralOf(revnetId)` | `uint256` |
|
|
301
|
-
| Loan details | `REVLoans.loanOf(loanId)` | `REVLoan` struct |
|
|
302
|
-
| All loan sources | `REVLoans.loanSourcesOf(revnetId)` | `REVLoanSource[]` |
|
|
303
|
-
| Loan count | `REVLoans.totalLoansBorrowedFor(revnetId)` | `uint256` |
|
|
304
|
-
| Source fee for repayment | `REVLoans.determineSourceFeeAmount(loan, amount)` | `uint256` |
|
|
305
|
-
| Revnet ID from loan ID | `REVLoans.revnetIdOfLoanWith(loanId)` | `uint256` (pure) |
|
|
306
|
-
| Loan NFT owner | `REVLoans.ownerOf(loanId)` | `address` (ERC-721) |
|
|
307
|
-
|
|
308
|
-
### Deployer Config
|
|
309
|
-
|
|
310
|
-
| What | Call | Returns |
|
|
311
|
-
|------|------|---------|
|
|
312
|
-
| Config hash (cross-chain matching) | `REVDeployer.hashedEncodedConfigurationOf(revnetId)` | `bytes32` |
|
|
313
|
-
| REVOwner address | `REVDeployer.OWNER()` | `address` |
|
|
314
|
-
|
|
315
|
-
### REVOwner State
|
|
316
|
-
|
|
317
|
-
| What | Call | Returns |
|
|
318
|
-
|------|------|---------|
|
|
319
|
-
| 721 hook address | `REVOwner.tiered721HookOf(revnetId)` | `IJB721TiersHook` |
|
|
320
|
-
| Cash-out delay timestamp | `REVOwner.cashOutDelayOf(revnetId)` | `uint256` (0 = no delay) |
|
|
321
|
-
|
|
322
|
-
## Example Integration
|
|
323
|
-
|
|
324
|
-
```solidity
|
|
325
|
-
import {REVConfig} from "@rev-net/core-v6/src/structs/REVConfig.sol";
|
|
326
|
-
import {REVStageConfig} from "@rev-net/core-v6/src/structs/REVStageConfig.sol";
|
|
327
|
-
import {REVDescription} from "@rev-net/core-v6/src/structs/REVDescription.sol";
|
|
328
|
-
import {REVSuckerDeploymentConfig} from "@rev-net/core-v6/src/structs/REVSuckerDeploymentConfig.sol";
|
|
329
|
-
import {IREVDeployer} from "@rev-net/core-v6/src/interfaces/IREVDeployer.sol";
|
|
330
|
-
|
|
331
|
-
// --- Deploy a simple revnet with one stage ---
|
|
332
|
-
|
|
333
|
-
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
334
|
-
stages[0] = REVStageConfig({
|
|
335
|
-
startsAtOrAfter: 0, // Start immediately (uses block.timestamp)
|
|
336
|
-
autoIssuances: new REVAutoIssuance[](0),
|
|
337
|
-
splitPercent: 2000, // 20% of new tokens go to splits
|
|
338
|
-
splits: splits, // Reserved token split destinations
|
|
339
|
-
initialIssuance: 1_000_000e18, // 1M tokens per unit of base currency
|
|
340
|
-
issuanceCutFrequency: 30 days, // Decay period
|
|
341
|
-
issuanceCutPercent: 100_000_000, // 10% cut per period (out of 1e9)
|
|
342
|
-
cashOutTaxRate: 2000, // 20% tax on cash outs
|
|
343
|
-
extraMetadata: 0
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
REVConfig memory config = REVConfig({
|
|
347
|
-
description: REVDescription({
|
|
348
|
-
name: "My Revnet Token",
|
|
349
|
-
ticker: "MYREV",
|
|
350
|
-
uri: "ipfs://...",
|
|
351
|
-
salt: bytes32(0)
|
|
352
|
-
}),
|
|
353
|
-
baseCurrency: 1, // ETH
|
|
354
|
-
splitOperator: msg.sender,
|
|
355
|
-
stageConfigurations: stages
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
deployer.deployFor({
|
|
359
|
-
revnetId: 0, // 0 = deploy new
|
|
360
|
-
configuration: config,
|
|
361
|
-
terminalConfigurations: terminals,
|
|
362
|
-
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
363
|
-
deployerConfigurations: new JBSuckerDeployerConfig[](0),
|
|
364
|
-
salt: bytes32(0)
|
|
365
|
-
})
|
|
366
|
-
});
|
|
28
|
+
## Purpose
|
|
367
29
|
|
|
368
|
-
|
|
30
|
+
Deploy and manage Revnets -- autonomous, unowned Juicebox projects with staged issuance schedules, built-in Uniswap buyback pools, cross-chain suckers, and token-collateralized lending.
|
|
369
31
|
|
|
370
|
-
|
|
371
|
-
revnetId: revnetId,
|
|
372
|
-
source: REVLoanSource({ token: JBConstants.NATIVE_TOKEN, terminal: terminal }),
|
|
373
|
-
minBorrowAmount: 0,
|
|
374
|
-
collateralCount: 1000e18, // Burn 1000 tokens as collateral
|
|
375
|
-
beneficiary: msg.sender, // Receive borrowed funds
|
|
376
|
-
prepaidFeePercent: 25 // 2.5% prepaid fee (minimum)
|
|
377
|
-
});
|
|
32
|
+
## Reference Files
|
|
378
33
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
(uint256 reallocatedLoanId, uint256 newLoanId, , ) = loans.reallocateCollateralFromLoan({
|
|
384
|
-
loanId: loanId,
|
|
385
|
-
collateralCountToTransfer: 500e18, // Move 500 tokens out of existing loan
|
|
386
|
-
source: REVLoanSource({ token: JBConstants.NATIVE_TOKEN, terminal: terminal }),
|
|
387
|
-
minBorrowAmount: 0,
|
|
388
|
-
collateralCountToAdd: 200e18, // Add 200 fresh tokens on top
|
|
389
|
-
beneficiary: payable(msg.sender), // Receive new loan proceeds
|
|
390
|
-
prepaidFeePercent: 25 // 2.5% prepaid fee on new loan
|
|
391
|
-
});
|
|
392
|
-
// Result: original loan now has 500 fewer collateral tokens (reallocatedLoanId),
|
|
393
|
-
// new loan has 700 tokens of collateral (newLoanId).
|
|
34
|
+
| If you need... | Open this next |
|
|
35
|
+
|---|---|
|
|
36
|
+
| Contract roles, deploy/runtime entrypoints, integration points, or key structs | [`references/runtime.md`](./references/runtime.md) |
|
|
37
|
+
| Events, errors, constants, storage, gotchas, state-reading recipes, or examples | [`references/operations.md`](./references/operations.md) |
|
|
394
38
|
|
|
395
|
-
|
|
39
|
+
## Working Rules
|
|
396
40
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
collateralCountToReturn: loan.collateral, // Return all collateral
|
|
401
|
-
beneficiary: msg.sender, // Receive re-minted tokens
|
|
402
|
-
allowance: JBSingleAllowance({ ... }) // Optional permit2
|
|
403
|
-
});
|
|
404
|
-
```
|
|
41
|
+
- Start in `REVDeployer` for launch-time behavior, `REVOwner` for runtime hook behavior, and `REVLoans` for collateral and debt accounting.
|
|
42
|
+
- Verify any economic assumption in code or tests before relying on it. Revnet docs carry more economic interpretation than most repos.
|
|
43
|
+
- For anything cross-chain or stage-related, check both the deployer path and the reading-state reference before editing.
|
package/STYLE_GUIDE.md
CHANGED
|
@@ -26,8 +26,8 @@ pragma solidity 0.8.28;
|
|
|
26
26
|
// Interfaces, structs, enums — caret for forward compatibility
|
|
27
27
|
pragma solidity ^0.8.0;
|
|
28
28
|
|
|
29
|
-
// Libraries —
|
|
30
|
-
pragma solidity
|
|
29
|
+
// Libraries — pin to exact version like contracts
|
|
30
|
+
pragma solidity 0.8.28;
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
## Imports
|
|
@@ -86,12 +86,20 @@ contract JBExample is JBPermissioned, IJBExample {
|
|
|
86
86
|
|
|
87
87
|
uint256 internal constant _FEE_BENEFICIARY_PROJECT_ID = 1;
|
|
88
88
|
|
|
89
|
+
//*********************************************************************//
|
|
90
|
+
// ------------------------ private constants ------------------------ //
|
|
91
|
+
//*********************************************************************//
|
|
92
|
+
|
|
89
93
|
//*********************************************************************//
|
|
90
94
|
// --------------- public immutable stored properties ---------------- //
|
|
91
95
|
//*********************************************************************//
|
|
92
96
|
|
|
93
97
|
IJBDirectory public immutable override DIRECTORY;
|
|
94
98
|
|
|
99
|
+
//*********************************************************************//
|
|
100
|
+
// -------------- internal immutable stored properties -------------- //
|
|
101
|
+
//*********************************************************************//
|
|
102
|
+
|
|
95
103
|
//*********************************************************************//
|
|
96
104
|
// --------------------- public stored properties -------------------- //
|
|
97
105
|
//*********************************************************************//
|
|
@@ -100,10 +108,26 @@ contract JBExample is JBPermissioned, IJBExample {
|
|
|
100
108
|
// -------------------- internal stored properties ------------------- //
|
|
101
109
|
//*********************************************************************//
|
|
102
110
|
|
|
111
|
+
//*********************************************************************//
|
|
112
|
+
// -------------------- private stored properties -------------------- //
|
|
113
|
+
//*********************************************************************//
|
|
114
|
+
|
|
115
|
+
//*********************************************************************//
|
|
116
|
+
// ------------------- transient stored properties ------------------- //
|
|
117
|
+
//*********************************************************************//
|
|
118
|
+
|
|
103
119
|
//*********************************************************************//
|
|
104
120
|
// -------------------------- constructor ---------------------------- //
|
|
105
121
|
//*********************************************************************//
|
|
106
122
|
|
|
123
|
+
//*********************************************************************//
|
|
124
|
+
// ---------------------------- modifiers ---------------------------- //
|
|
125
|
+
//*********************************************************************//
|
|
126
|
+
|
|
127
|
+
//*********************************************************************//
|
|
128
|
+
// ------------------------- receive / fallback ---------------------- //
|
|
129
|
+
//*********************************************************************//
|
|
130
|
+
|
|
107
131
|
//*********************************************************************//
|
|
108
132
|
// ---------------------- external transactions ---------------------- //
|
|
109
133
|
//*********************************************************************//
|
|
@@ -112,10 +136,18 @@ contract JBExample is JBPermissioned, IJBExample {
|
|
|
112
136
|
// ----------------------- external views ---------------------------- //
|
|
113
137
|
//*********************************************************************//
|
|
114
138
|
|
|
139
|
+
//*********************************************************************//
|
|
140
|
+
// -------------------------- public views --------------------------- //
|
|
141
|
+
//*********************************************************************//
|
|
142
|
+
|
|
115
143
|
//*********************************************************************//
|
|
116
144
|
// ----------------------- public transactions ----------------------- //
|
|
117
145
|
//*********************************************************************//
|
|
118
146
|
|
|
147
|
+
//*********************************************************************//
|
|
148
|
+
// ---------------------- internal transactions ---------------------- //
|
|
149
|
+
//*********************************************************************//
|
|
150
|
+
|
|
119
151
|
//*********************************************************************//
|
|
120
152
|
// ----------------------- internal helpers -------------------------- //
|
|
121
153
|
//*********************************************************************//
|
|
@@ -134,17 +166,28 @@ contract JBExample is JBPermissioned, IJBExample {
|
|
|
134
166
|
1. Custom errors
|
|
135
167
|
2. Public constants
|
|
136
168
|
3. Internal constants
|
|
137
|
-
4.
|
|
138
|
-
5.
|
|
139
|
-
6.
|
|
140
|
-
7.
|
|
141
|
-
8.
|
|
142
|
-
9.
|
|
143
|
-
10.
|
|
144
|
-
11.
|
|
145
|
-
12.
|
|
146
|
-
13.
|
|
147
|
-
14.
|
|
169
|
+
4. Private constants
|
|
170
|
+
5. Public immutable stored properties
|
|
171
|
+
6. Internal immutable stored properties
|
|
172
|
+
7. Public stored properties
|
|
173
|
+
8. Internal stored properties
|
|
174
|
+
9. Private stored properties
|
|
175
|
+
10. Transient stored properties
|
|
176
|
+
11. Constructor
|
|
177
|
+
12. Modifiers
|
|
178
|
+
13. Receive / fallback
|
|
179
|
+
14. External transactions
|
|
180
|
+
15. External views
|
|
181
|
+
16. Public views
|
|
182
|
+
17. Public transactions
|
|
183
|
+
18. Internal transactions
|
|
184
|
+
19. Internal helpers
|
|
185
|
+
20. Internal views
|
|
186
|
+
21. Private helpers
|
|
187
|
+
|
|
188
|
+
Use these additional section labels where they better match the contents of the block:
|
|
189
|
+
- `internal functions` is accepted as equivalent to `internal helpers`
|
|
190
|
+
- `events` and `structs` are acceptable in specialized contracts that define them explicitly
|
|
148
191
|
|
|
149
192
|
Functions are alphabetized within each section.
|
|
150
193
|
|
|
@@ -197,7 +240,7 @@ interface IJBExample is IJBBase {
|
|
|
197
240
|
| Public/external function | `camelCase` | `cashOutTokensOf` |
|
|
198
241
|
| Internal/private function | `_camelCase` | `_processFee` |
|
|
199
242
|
| Internal storage | `_camelCase` | `_accountingContextForTokenOf` |
|
|
200
|
-
| Function parameter | `camelCase` | `projectId`, `cashOutCount` |
|
|
243
|
+
| Function parameter | `camelCase` (no underscores) | `projectId`, `cashOutCount` |
|
|
201
244
|
|
|
202
245
|
## NatSpec
|
|
203
246
|
|
|
@@ -348,6 +391,7 @@ wrap_comments = true
|
|
|
348
391
|
|
|
349
392
|
**Optional sections (add only when needed):**
|
|
350
393
|
- `[rpc_endpoints]` — repos with fork tests. Maps named endpoints to env vars (e.g. `ethereum = "${RPC_ETHEREUM_MAINNET}"`).
|
|
394
|
+
- `[profile.ci_sizes]` — only when CI needs different optimizer settings than defaults for the size check step (e.g. `optimizer_runs = 200` when the default profile uses a lower value).
|
|
351
395
|
|
|
352
396
|
**Common variations:**
|
|
353
397
|
- `via_ir = true` when hitting stack-too-deep
|
|
@@ -563,9 +607,4 @@ CI checks formatting via `forge fmt --check`.
|
|
|
563
607
|
|
|
564
608
|
### Contract Size Checks
|
|
565
609
|
|
|
566
|
-
CI runs `forge build --sizes` to catch contracts approaching the 24KB limit.
|
|
567
|
-
|
|
568
|
-
## Repo-Specific Deviations
|
|
569
|
-
|
|
570
|
-
- **`optimizer_runs = 100`** — reduced for contract size compliance
|
|
571
|
-
- **`npm install --omit=dev && npm install @sphinx-labs/contracts` in CI** — test files import deployment helpers from dependencies (`SuckerDeploymentLib`, `CroptopDeploymentLib`) that use `@sphinx-labs` contracts. Installing just the contracts package avoids the full `@sphinx-labs/plugins` tree (445+ Solidity files) that would bloat compilation
|
|
610
|
+
CI runs `forge build --sizes` to catch contracts approaching the 24KB limit. When the repo's default `optimizer_runs` differs from what you want for size checking, use `FOUNDRY_PROFILE=ci_sizes forge build --sizes` with a `[profile.ci_sizes]` section in `foundry.toml`.
|