@rev-net/core-v6 0.0.19 → 0.0.21

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.
Files changed (59) hide show
  1. package/ADMINISTRATION.md +2 -2
  2. package/ARCHITECTURE.md +1 -1
  3. package/AUDIT_INSTRUCTIONS.md +7 -7
  4. package/CHANGE_LOG.md +3 -4
  5. package/README.md +2 -2
  6. package/RISKS.md +1 -1
  7. package/SKILLS.md +2 -2
  8. package/package.json +1 -1
  9. package/script/Deploy.s.sol +0 -5
  10. package/script/helpers/RevnetCoreDeploymentLib.sol +8 -0
  11. package/src/REVDeployer.sol +3 -0
  12. package/src/REVOwner.sol +5 -6
  13. package/test/REV.integrations.t.sol +0 -2
  14. package/test/REVAutoIssuanceFuzz.t.sol +0 -2
  15. package/test/REVDeployerRegressions.t.sol +0 -2
  16. package/test/REVInvincibility.t.sol +0 -4
  17. package/test/REVLifecycle.t.sol +0 -2
  18. package/test/REVLoans.invariants.t.sol +0 -2
  19. package/test/REVLoansAttacks.t.sol +0 -2
  20. package/test/REVLoansFeeRecovery.t.sol +0 -2
  21. package/test/REVLoansFindings.t.sol +0 -2
  22. package/test/REVLoansRegressions.t.sol +0 -2
  23. package/test/REVLoansSourceFeeRecovery.t.sol +0 -2
  24. package/test/REVLoansSourced.t.sol +0 -2
  25. package/test/REVLoansUnSourced.t.sol +0 -2
  26. package/test/TestBurnHeldTokens.t.sol +0 -2
  27. package/test/TestCEIPattern.t.sol +0 -1
  28. package/test/TestCashOutCallerValidation.t.sol +0 -2
  29. package/test/TestConversionDocumentation.t.sol +0 -2
  30. package/test/TestCrossCurrencyReclaim.t.sol +0 -2
  31. package/test/TestCrossSourceReallocation.t.sol +0 -1
  32. package/test/TestERC2771MetaTx.t.sol +0 -2
  33. package/test/TestEmptyBuybackSpecs.t.sol +0 -1
  34. package/test/TestFlashLoanSurplus.t.sol +0 -1
  35. package/test/TestHookArrayOOB.t.sol +0 -1
  36. package/test/TestLiquidationBehavior.t.sol +0 -1
  37. package/test/TestLoanSourceRotation.t.sol +0 -2
  38. package/test/TestLoansCashOutDelay.t.sol +0 -2
  39. package/test/TestLongTailEconomics.t.sol +0 -2
  40. package/test/TestLowFindings.t.sol +0 -2
  41. package/test/TestMixedFixes.t.sol +0 -1
  42. package/test/TestPermit2Signatures.t.sol +0 -2
  43. package/test/TestReallocationSandwich.t.sol +0 -1
  44. package/test/TestRevnetRegressions.t.sol +0 -2
  45. package/test/TestSplitWeightAdjustment.t.sol +0 -2
  46. package/test/TestSplitWeightE2E.t.sol +0 -3
  47. package/test/TestSplitWeightFork.t.sol +0 -2
  48. package/test/TestStageTransitionBorrowable.t.sol +0 -1
  49. package/test/TestSwapTerminalPermission.t.sol +0 -1
  50. package/test/TestUint112Overflow.t.sol +0 -2
  51. package/test/TestZeroRepayment.t.sol +0 -1
  52. package/test/audit/LoanIdOverflowGuard.t.sol +0 -2
  53. package/test/fork/ForkTestBase.sol +0 -2
  54. package/test/regression/TestBurnPermissionRequired.t.sol +0 -1
  55. package/test/regression/TestCashOutBuybackFeeLeak.t.sol +0 -2
  56. package/test/regression/TestCrossRevnetLiquidation.t.sol +0 -1
  57. package/test/regression/TestCumulativeLoanCounter.t.sol +0 -1
  58. package/test/regression/TestLiquidateGapHandling.t.sol +0 -1
  59. package/test/regression/TestZeroPriceFeed.t.sol +0 -2
package/ADMINISTRATION.md CHANGED
@@ -140,14 +140,14 @@ The following parameters are set at deployment and can never be changed:
140
140
  - `DEFAULT_BUYBACK_TWAP_WINDOW` -- 2 days
141
141
  - `OWNER()` -- view returning the REVOwner address
142
142
 
143
- ### REVOwner (global, set at contract deployment + initialize)
143
+ ### REVOwner (global, set at contract deployment)
144
144
  - `BUYBACK_HOOK` -- the buyback hook (shared immutable with REVDeployer)
145
145
  - `DIRECTORY` -- the Juicebox directory (shared immutable with REVDeployer)
146
146
  - `FEE_REVNET_ID` -- the project ID that receives cash-out fees (shared immutable)
147
147
  - `SUCKER_REGISTRY` -- the sucker registry (shared immutable)
148
148
  - `LOANS` -- the loans contract address (shared immutable)
149
149
  - `FEE` -- the cash-out fee constant (2.5%)
150
- - `DEPLOYER` -- the REVDeployer address (storage variable, set once via `initialize()`)
150
+ - `DEPLOYER` -- the REVDeployer address (storage variable, set once via `setDeployer()` called from REVDeployer's constructor)
151
151
 
152
152
  ### REVLoans (global, set at contract deployment)
153
153
  - `CONTROLLER`, `DIRECTORY`, `PRICES`, `PROJECTS` -- protocol infrastructure
package/ARCHITECTURE.md CHANGED
@@ -143,7 +143,7 @@ Fields set automatically by the deployer (not configurable per stage):
143
143
  ## Key Design Decisions
144
144
  - Stages are immutable after deployment — no owner can change ruleset parameters
145
145
  - Matching hash ensures cross-chain deployments have identical economic parameters. It covers all economic fields (issuance, decay, tax rates, auto-issuances) but intentionally excludes split recipient addresses, which may differ by chain. The hash is used as a CREATE2 salt component for sucker deployment, so mismatched configs produce different sucker addresses that cannot peer with each other.
146
- - REVOwner is the data hook for all revnets — centralizes runtime behavioral control (pay hooks, cash-out hooks, mint permissions) and stores `cashOutDelayOf` and `tiered721HookOf` per revnet. REVDeployer handles deployment and configuration state storage. The split was necessary to stay under the EIP-170 contract size limit (24,576 bytes). Deploy order: REVOwner first, then REVDeployer(owner=REVOwner), then REVOwner.initialize(deployer). REVDeployer calls DEPLOYER-restricted setters on REVOwner (`setCashOutDelayOf`, `setTiered721HookOf`) during deployment.
146
+ - REVOwner is the data hook for all revnets — centralizes runtime behavioral control (pay hooks, cash-out hooks, mint permissions) and stores `cashOutDelayOf` and `tiered721HookOf` per revnet. REVDeployer handles deployment and configuration state storage. The split was necessary to stay under the EIP-170 contract size limit (24,576 bytes). Deploy order: REVOwner first, then REVDeployer(owner=REVOwner) -- the constructor calls `REVOwner.setDeployer()` atomically. REVDeployer calls DEPLOYER-restricted setters on REVOwner (`setCashOutDelayOf`, `setTiered721HookOf`) during deployment.
147
147
  - Loans use bonding curve value, not market price — independent of external DEX pricing
148
148
  - Auto-issuance is deferred, not instant — token amounts are recorded at deploy time but minted via a separate `autoIssueFor` call after the stage starts. This separates deployment from issuance, allows anyone to trigger the mint permissionlessly, and ensures tokens are not minted before their stage is active.
149
149
  - No approval hook — revnet rulesets set `approvalHook` to `address(0)` because stages are configured immutably at deployment. There is no governance or owner who could queue a change that would need approval.
@@ -11,7 +11,7 @@ Read [RISKS.md](./RISKS.md) for the trust model and known risks. Read [ARCHITECT
11
11
  | Contract | Lines | Role |
12
12
  |----------|-------|------|
13
13
  | `src/REVDeployer.sol` | ~19,746 bytes | Deploys revnets. Manages stages, splits, auto-issuance, buyback hook delegation, 721 hook deployment, suckers, split operator permissions, and all state storage. Split from original monolith to stay under EIP-170 (24,576 bytes). |
14
- | `src/REVOwner.sol` | ~8,353 bytes (~310 lines) | Runtime hook contract. Implements `IJBRulesetDataHook` + `IJBCashOutHook`. Set as the `dataHook` in each revnet's ruleset metadata. Handles `beforePayRecordedWith`, `beforeCashOutRecordedWith`, `afterCashOutRecordedWith`, `hasMintPermissionFor`, and sucker verification. Stores `cashOutDelayOf` and `tiered721HookOf` mappings (set by REVDeployer via DEPLOYER-restricted setters). **Key audit focus: the `initialize()` one-shot pattern, DEPLOYER-restricted setter access control, and circular dependency with REVDeployer.** |
14
+ | `src/REVOwner.sol` | ~8,434 bytes (~310 lines) | Runtime hook contract. Implements `IJBRulesetDataHook` + `IJBCashOutHook`. Set as the `dataHook` in each revnet's ruleset metadata. Handles `beforePayRecordedWith`, `beforeCashOutRecordedWith`, `afterCashOutRecordedWith`, `hasMintPermissionFor`, and sucker verification. Stores `cashOutDelayOf` and `tiered721HookOf` mappings (set by REVDeployer via DEPLOYER-restricted setters). **Key audit focus: the `setDeployer()` one-shot pattern, DEPLOYER-restricted setter access control, and circular dependency with REVDeployer.** |
15
15
  | `src/REVLoans.sol` | ~1,359 lines | Token-collateralized lending. Burns collateral on borrow, re-mints on repay. ERC-721 loan NFTs. Three-layer fee model. Permit2 integration. |
16
16
  | `src/interfaces/` | ~525 | Interface definitions for both contracts |
17
17
  | `src/structs/` | ~212 | All struct definitions |
@@ -128,7 +128,7 @@ Borrower calls REVLoans.borrowFrom()
128
128
 
129
129
  | Variable | Purpose | Audit Focus |
130
130
  |----------|---------|-------------|
131
- | `DEPLOYER` | REVDeployer address | Set once via `initialize()`. **Not immutable** -- stored as a regular storage variable to break circular dependency. Verify `initialize()` can only be called once and with the correct address. Used to restrict access to `setCashOutDelayOf()` and `setTiered721HookOf()`. |
131
+ | `DEPLOYER` | REVDeployer address | Set once via `setDeployer()` called from REVDeployer's constructor. **Not immutable** -- stored as a regular storage variable to break circular dependency. `setDeployer()` sets `msg.sender` as `DEPLOYER` and reverts if already set (`REVOwner_AlreadyInitialized`). Used to restrict access to `setCashOutDelayOf()` and `setTiered721HookOf()`. |
132
132
  | `cashOutDelayOf[revnetId]` | Timestamp when cash-outs unlock | Set by REVDeployer via `setCashOutDelayOf()` (DEPLOYER-restricted). Applied only for existing revnets deployed to new chains. **Read by REVLoans via IREVOwner.** Verify only DEPLOYER can call the setter. |
133
133
  | `tiered721HookOf[revnetId]` | 721 hook address | Set by REVDeployer via `setTiered721HookOf()` (DEPLOYER-restricted). Set once during deploy, never changed. **Read by REVOwner internally during pay hooks.** Verify only DEPLOYER can call the setter. |
134
134
 
@@ -274,12 +274,12 @@ Verify:
274
274
 
275
275
  ### 8. REVOwner initialization and circular dependency
276
276
 
277
- REVOwner and REVDeployer have a circular dependency broken by a one-shot `initialize()` call. Deploy order: REVOwner first, then REVDeployer(owner=REVOwner), then REVOwner.initialize(deployer). Verify:
277
+ REVOwner and REVDeployer have a circular dependency broken by `setDeployer()`, called atomically from REVDeployer's constructor. Deploy order: REVOwner first, then REVDeployer(owner=REVOwner) -- the constructor calls `REVOwner.setDeployer()` atomically. Verify:
278
278
 
279
- - `initialize()` can only be called once (subsequent calls revert)
279
+ - `setDeployer()` sets `msg.sender` as `DEPLOYER` and reverts if already set (`REVOwner_AlreadyInitialized`)
280
280
  - `DEPLOYER` is a storage variable, not immutable, to break the circular dependency
281
- - Before `initialize()` is called, the DEPLOYER-restricted setters (`setCashOutDelayOf`, `setTiered721HookOf`) would reject calls, leaving `cashOutDelayOf` and `tiered721HookOf` unpopulated
282
- - No path allows `initialize()` to be called with the wrong deployer address after the correct one is set
281
+ - Before `setDeployer()` is called, the DEPLOYER-restricted setters (`setCashOutDelayOf`, `setTiered721HookOf`) would reject calls, leaving `cashOutDelayOf` and `tiered721HookOf` unpopulated
282
+ - After `setDeployer()` has been called once, no subsequent call can change the `DEPLOYER` address
283
283
  - Only DEPLOYER can call `setCashOutDelayOf()` and `setTiered721HookOf()` -- verify access control on these setters
284
284
  - `cashOutDelayOf` and `tiered721HookOf` are stored on REVOwner (not REVDeployer) -- verify REVOwner reads from its own storage and the setters cannot be called by unauthorized addresses
285
285
  - Both contracts define `FEE = 25` independently -- verify they stay in sync
@@ -293,7 +293,7 @@ Fuzzable properties that should hold for all valid inputs:
293
293
  3. **Loan NFT ownership**: The ERC-721 owner of a loan NFT is the only address authorized to repay, reallocate, or manage that loan (absent ROOT or explicit permission grants).
294
294
  4. **No flash-loan profit**: Borrowing and repaying in the same block (zero time elapsed) should never yield a net profit to the borrower after all fees.
295
295
  5. **Stage monotonicity**: Stage transitions are monotonically increasing in time -- a later stage's `startsAtOrAfter` is always strictly greater than the previous stage's.
296
- 6. **REVOwner initialization**: `DEPLOYER` is set exactly once via `initialize()` and matches the REVDeployer that references this REVOwner via `OWNER()`. Only the initialized `DEPLOYER` can call `setCashOutDelayOf()` and `setTiered721HookOf()`.
296
+ 6. **REVOwner initialization**: `DEPLOYER` is set exactly once via `setDeployer()` (called from REVDeployer's constructor) and matches the REVDeployer that references this REVOwner via `OWNER()`. Only the initialized `DEPLOYER` can call `setCashOutDelayOf()` and `setTiered721HookOf()`.
297
297
 
298
298
  ## How to Run Tests
299
299
 
package/CHANGE_LOG.md CHANGED
@@ -11,7 +11,7 @@ REVDeployer exceeded the EIP-170 contract size limit (24,576 bytes) at 26,397 by
11
11
  | Contract | Size | Role |
12
12
  |----------|------|------|
13
13
  | `REVDeployer` | 19,746 bytes | Deployment, configuration, state storage, split operator management |
14
- | `REVOwner` | 8,353 bytes (~310 lines) | Runtime hook behavior: `IJBRulesetDataHook` + `IJBCashOutHook` |
14
+ | `REVOwner` | 8,434 bytes (~310 lines) | Runtime hook behavior: `IJBRulesetDataHook` + `IJBCashOutHook` |
15
15
 
16
16
  ### What moved to REVOwner
17
17
 
@@ -51,10 +51,9 @@ The `cashOutDelayOf` and `tiered721HookOf` storage mappings were moved from REVD
51
51
  REVDeployer needs REVOwner (as the `dataHook` address and to set `cashOutDelayOf`/`tiered721HookOf` via restricted setters), and REVOwner references REVDeployer (to restrict setter access). This circular dependency is broken by:
52
52
 
53
53
  1. Deploy REVOwner first
54
- 2. Deploy REVDeployer with `owner=REVOwner`
55
- 3. Call `REVOwner.initialize(deployer)` to set the `DEPLOYER` storage variable
54
+ 2. Deploy REVDeployer with `owner=REVOwner` -- the constructor calls `REVOwner.setDeployer()` atomically
56
55
 
57
- `REVOwner.DEPLOYER` is a **storage variable** (not immutable) because the deployer address is not known at REVOwner construction time. The `initialize()` function can only be called once. After initialization, `DEPLOYER` is used to restrict access to `setCashOutDelayOf()` and `setTiered721HookOf()`.
56
+ `REVOwner.DEPLOYER` is a **storage variable** (not immutable) because the deployer address is not known at REVOwner construction time. `setDeployer()` sets `msg.sender` as `DEPLOYER` and reverts if already set (`REVOwner_AlreadyInitialized`). After initialization, `DEPLOYER` is used to restrict access to `setCashOutDelayOf()` and `setTiered721HookOf()`.
58
57
 
59
58
  ### Shared immutables
60
59
 
package/README.md CHANGED
@@ -96,7 +96,7 @@ Every revnet gets a tiered ERC-721 hook deployed automatically — even if no ti
96
96
 
97
97
  ### How They Relate
98
98
 
99
- `REVDeployer` owns every revnet's Juicebox project NFT and holds all administrative permissions. `REVOwner` is set as the `dataHook` for every revnet's rulesets, handling all runtime hook behavior (pay hooks, cash-out hooks, mint permissions) and storing `cashOutDelayOf` and `tiered721HookOf` per revnet (set by REVDeployer via DEPLOYER-restricted setters during deployment). Deploy order: REVOwner is deployed first, then REVDeployer (with `owner=REVOwner`), then `REVOwner.initialize(deployer)` is called to link them. Both contracts share immutables: `BUYBACK_HOOK`, `DIRECTORY`, `FEE_REVNET_ID`, `SUCKER_REGISTRY`, `LOANS`. During deployment, REVDeployer grants `REVLoans` the `USE_ALLOWANCE` permission so loans can pull funds from the revnet's terminal. `REVLoans` imports `IREVOwner` (not `IREVDeployer`) for `cashOutDelayOf` calls and verifies that a revnet was deployed by its expected `REVDeployer` before issuing any loan.
99
+ `REVDeployer` owns every revnet's Juicebox project NFT and holds all administrative permissions. `REVOwner` is set as the `dataHook` for every revnet's rulesets, handling all runtime hook behavior (pay hooks, cash-out hooks, mint permissions) and storing `cashOutDelayOf` and `tiered721HookOf` per revnet (set by REVDeployer via DEPLOYER-restricted setters during deployment). Deploy order: REVOwner first, then REVDeployer(owner=REVOwner) -- the constructor calls `REVOwner.setDeployer()` atomically. Both contracts share immutables: `BUYBACK_HOOK`, `DIRECTORY`, `FEE_REVNET_ID`, `SUCKER_REGISTRY`, `LOANS`. During deployment, REVDeployer grants `REVLoans` the `USE_ALLOWANCE` permission so loans can pull funds from the revnet's terminal. `REVLoans` imports `IREVOwner` (not `IREVDeployer`) for `cashOutDelayOf` calls and verifies that a revnet was deployed by its expected `REVDeployer` before issuing any loan.
100
100
 
101
101
  ### Interfaces
102
102
 
@@ -209,7 +209,7 @@ Plus optional from 721 hook config: `ADJUST_721_TIERS`, `SET_721_METADATA`, `MIN
209
209
  ## Risks
210
210
 
211
211
  - **No human owner.** `REVDeployer` permanently holds the project NFT. There is no function to release it. This is by design -- revnets are ownerless. But it means bugs in stage configurations cannot be fixed after deployment.
212
- - **REVOwner circular dependency.** REVOwner and REVDeployer have a circular dependency broken by a one-shot `initialize()` call. `REVOwner.DEPLOYER` is a storage variable (not immutable) set via `initialize()`. If `initialize()` is never called or called with the wrong address, the DEPLOYER-restricted setters (`setCashOutDelayOf`, `setTiered721HookOf`) cannot be called correctly, and all runtime hook behavior breaks.
212
+ - **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` and reverts if already set. If `setDeployer()` is never called, the DEPLOYER-restricted setters (`setCashOutDelayOf`, `setTiered721HookOf`) cannot be called correctly, and all runtime hook behavior breaks.
213
213
  - **Loan flash-loan exposure.** `borrowableAmountFrom` reads live surplus, which can be inflated via flash loans. A borrower could temporarily inflate the treasury to borrow more than the sustained value would support.
214
214
  - **uint112 truncation.** `REVLoan.amount` and `REVLoan.collateral` are `uint112` -- values above ~5.19e33 truncate silently.
215
215
  - **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.
package/RISKS.md CHANGED
@@ -11,7 +11,7 @@ Read [ARCHITECTURE.md](./ARCHITECTURE.md) and [SKILLS.md](./SKILLS.md) for proto
11
11
  ### What the system assumes to be correct
12
12
 
13
13
  - **REVOwner is a singleton data hook.** Every revnet shares one `beforePayRecordedWith` and `beforeCashOutRecordedWith` implementation in REVOwner. A bug in either function affects ALL revnets deployed by that deployer simultaneously. There is no per-project isolation and no circuit breaker.
14
- - **REVOwner circular dependency.** REVOwner and REVDeployer have a circular dependency broken by a one-shot `initialize()` call. `REVOwner.DEPLOYER` is a storage variable (not immutable) set via `initialize()`. If `initialize()` is never called, REVDeployer cannot call the DEPLOYER-restricted setters (`setCashOutDelayOf`, `setTiered721HookOf`) on REVOwner, and `cashOutDelayOf`/`tiered721HookOf` will never be populated, breaking all runtime hook behavior. If called with the wrong deployer address, an unauthorized address could set incorrect state. The `initialize()` function must be called exactly once with the correct address immediately after deploying both contracts.
14
+ - **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` and reverts if already set. If `setDeployer()` is never called, REVDeployer cannot call the DEPLOYER-restricted setters (`setCashOutDelayOf`, `setTiered721HookOf`) on REVOwner, and `cashOutDelayOf`/`tiered721HookOf` will never be populated, breaking all runtime hook behavior. Because the call is made atomically from REVDeployer's constructor, the correct deployer address is guaranteed to be set during deployment.
15
15
  - **Stage immutability is the trust model.** Once `deployFor()` completes, stage parameters (issuance, `cashOutTaxRate`, splits, auto-issuances) are locked forever. No owner, no governance, no upgrade path. A misconfigured deployment is permanent. This is intentional -- the absence of admin keys IS the security property.
16
16
  - **Bonding curve is the sole collateral oracle.** `REVLoans` uses `JBCashOuts.cashOutFrom` to value collateral. There is no external price oracle, no liquidation margin, and no health factor. The borrowable amount equals the cash-out value at the moment of borrowing.
17
17
  - **Juicebox core contracts are correct.** `JBController`, `JBMultiTerminal`, `JBTerminalStore`, `JBTokens`, `JBPrices` -- a bug in any of these is a bug in every revnet.
package/SKILLS.md CHANGED
@@ -197,7 +197,7 @@ Deploy and manage Revnets -- autonomous, unowned Juicebox projects with staged i
197
197
 
198
198
  | Mapping | Visibility | Type | Purpose |
199
199
  |---------|-----------|------|---------|
200
- | `DEPLOYER` | `public` | `address` | REVDeployer address (storage variable, set once via `initialize()`) |
200
+ | `DEPLOYER` | `public` | `address` | REVDeployer address (storage variable, set once via `setDeployer()` called from REVDeployer's constructor) |
201
201
  | `cashOutDelayOf` | `public` | `revnetId => uint256` | Timestamp when cash outs unlock (0 = no delay). Set by REVDeployer via `setCashOutDelayOf()`. |
202
202
  | `tiered721HookOf` | `public` | `revnetId => address` | Deployed 721 hook address (if any). Set by REVDeployer via `setTiered721HookOf()`. |
203
203
 
@@ -235,7 +235,7 @@ Deploy and manage Revnets -- autonomous, unowned Juicebox projects with staged i
235
235
  18. **Permit2 fallback.** `REVLoans` uses permit2 for ERC-20 transfers as a fallback when standard allowance is insufficient. Wrapped in try-catch.
236
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
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 a one-shot `initialize()` call. `REVOwner.DEPLOYER` is a storage variable (not immutable) set via `initialize()`. If `initialize()` is never called or called with the wrong address, all runtime hook behavior breaks. Deploy order: REVOwner first, then REVDeployer(owner=REVOwner), then REVOwner.initialize(deployer). REVOwner stores `cashOutDelayOf` and `tiered721HookOf` mappings, which are set by REVDeployer via DEPLOYER-restricted setters (`setCashOutDelayOf()`, `setTiered721HookOf()`).
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
239
 
240
240
  ### NATIVE_TOKEN Accounting on Non-ETH Chains
241
241
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rev-net/core-v6",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -491,11 +491,6 @@ contract DeployScript is Script, Sphinx {
491
491
  owner: address(revOwner)
492
492
  });
493
493
 
494
- // Link the REVOwner to the REVDeployer (can only be called once).
495
- if (!_deployerIsDeployed) {
496
- revOwner.initialize(IREVDeployer(address(_basicDeployer)));
497
- }
498
-
499
494
  // Only configure the fee project if singletons were freshly deployed. Re-running `deployFor` on an
500
495
  // already-configured project would fail because the project is no longer blank.
501
496
  if (!_singletonsExist) {
@@ -7,11 +7,13 @@ import {SphinxConstants, NetworkInfo} from "@sphinx-labs/contracts/contracts/fou
7
7
 
8
8
  import {IREVDeployer} from "./../../src/interfaces/IREVDeployer.sol";
9
9
  import {IREVLoans} from "./../../src/interfaces/IREVLoans.sol";
10
+ import {REVOwner} from "./../../src/REVOwner.sol";
10
11
 
11
12
  struct RevnetCoreDeployment {
12
13
  // forge-lint: disable-next-line(mixed-case-variable)
13
14
  IREVDeployer basic_deployer;
14
15
  IREVLoans loans;
16
+ REVOwner owner;
15
17
  }
16
18
 
17
19
  library RevnetCoreDeploymentLib {
@@ -58,6 +60,12 @@ library RevnetCoreDeploymentLib {
58
60
  path: path, project_name: "revnet-core-v6", network_name: network_name, contractName: "REVLoans"
59
61
  })
60
62
  );
63
+
64
+ deployment.owner = REVOwner(
65
+ _getDeploymentAddress({
66
+ path: path, project_name: "revnet-core-v6", network_name: network_name, contractName: "REVOwner"
67
+ })
68
+ );
61
69
  }
62
70
 
63
71
  /// @notice Get the address of a contract that was deployed by the Deploy script.
@@ -210,6 +210,9 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
210
210
 
211
211
  // Give the buyback hook (registry) permission to configure pools on all revnets.
212
212
  _setPermission({operator: address(BUYBACK_HOOK), revnetId: 0, permissionId: JBPermissionIds.SET_BUYBACK_POOL});
213
+
214
+ // Link the REVOwner to this deployer so it can accept setter calls.
215
+ REVOwner(OWNER).setDeployer();
213
216
  }
214
217
 
215
218
  //*********************************************************************//
package/src/REVOwner.sol CHANGED
@@ -80,7 +80,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
80
80
  mapping(uint256 revnetId => IJB721TiersHook tiered721Hook) public tiered721HookOf;
81
81
 
82
82
  /// @notice The deployer that manages revnet state.
83
- /// @dev Set once via `initialize()` after deployment.
83
+ /// @dev Set once via `setDeployer()` from the REVDeployer's constructor. Reverts if called again.
84
84
  IREVDeployer public DEPLOYER;
85
85
 
86
86
  //*********************************************************************//
@@ -362,12 +362,11 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
362
362
  }
363
363
  }
364
364
 
365
- /// @notice Link this contract to the REVDeployer that manages revnet state.
366
- /// @dev Can only be called once.
367
- /// @param deployer The REVDeployer to link to.
368
- function initialize(IREVDeployer deployer) external {
365
+ /// @notice Set the caller as this contract's deployer.
366
+ /// @dev Called by the REVDeployer's constructor. Reverts if a deployer is already set.
367
+ function setDeployer() external {
369
368
  if (address(DEPLOYER) != address(0)) revert REVOwner_AlreadyInitialized();
370
- DEPLOYER = deployer;
369
+ DEPLOYER = IREVDeployer(msg.sender);
371
370
  }
372
371
 
373
372
  /// @notice Store the cash out delay for a revnet.
@@ -241,8 +241,6 @@ contract REVnet_Integrations is TestBaseWorkflow {
241
241
  address(revOwner)
242
242
  );
243
243
 
244
- revOwner.initialize(IREVDeployer(address(REV_DEPLOYER)));
245
-
246
244
  // Deploy the ARB sucker deployer.
247
245
  JBArbitrumSuckerDeployer _deployer =
248
246
  new JBArbitrumSuckerDeployer(jbDirectory(), jbPermissions(), jbTokens(), address(this), address(0));
@@ -104,8 +104,6 @@ contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow {
104
104
  address(revOwner)
105
105
  );
106
106
 
107
- revOwner.initialize(IREVDeployer(address(REV_DEPLOYER)));
108
-
109
107
  vm.prank(multisig());
110
108
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
111
109
  }
@@ -119,8 +119,6 @@ contract REVDeployerRegressions is TestBaseWorkflow {
119
119
  address(REV_OWNER)
120
120
  );
121
121
 
122
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
123
-
124
122
  vm.prank(multisig());
125
123
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
126
124
  }
@@ -271,8 +271,6 @@ contract REVInvincibility_PropertyTests is TestBaseWorkflow {
271
271
  address(REV_OWNER)
272
272
  );
273
273
 
274
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
275
-
276
274
  // Deploy fee project
277
275
  vm.prank(multisig());
278
276
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -1055,8 +1053,6 @@ contract REVInvincibility_Invariants is StdInvariant, TestBaseWorkflow {
1055
1053
  address(REV_OWNER)
1056
1054
  );
1057
1055
 
1058
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
1059
-
1060
1056
  // Deploy fee project
1061
1057
  {
1062
1058
  JBAccountingContext[] memory ctx = new JBAccountingContext[](1);
@@ -127,8 +127,6 @@ contract REVLifecycle_Local is TestBaseWorkflow {
127
127
  address(REV_OWNER)
128
128
  );
129
129
 
130
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
131
-
132
130
  vm.prank(multisig());
133
131
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
134
132
 
@@ -569,8 +569,6 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
569
569
  address(REV_OWNER)
570
570
  );
571
571
 
572
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
573
-
574
572
  // Approve the basic deployer to configure the project.
575
573
  vm.prank(address(multisig()));
576
574
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -410,8 +410,6 @@ contract REVLoansAttacks is TestBaseWorkflow {
410
410
  address(REV_OWNER)
411
411
  );
412
412
 
413
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
414
-
415
413
  // Deploy fee project
416
414
  vm.prank(address(multisig()));
417
415
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -376,8 +376,6 @@ contract REVLoansFeeRecovery is TestBaseWorkflow {
376
376
  address(REV_OWNER)
377
377
  );
378
378
 
379
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
380
-
381
379
  // Deploy fee project.
382
380
  vm.prank(multisig());
383
381
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -234,8 +234,6 @@ contract REVLoansFindings is TestBaseWorkflow {
234
234
  address(REV_OWNER)
235
235
  );
236
236
 
237
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
238
-
239
237
  vm.prank(multisig());
240
238
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
241
239
 
@@ -229,8 +229,6 @@ contract REVLoansRegressions is TestBaseWorkflow {
229
229
  address(REV_OWNER)
230
230
  );
231
231
 
232
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
233
-
234
232
  vm.prank(multisig());
235
233
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
236
234
 
@@ -256,8 +256,6 @@ contract REVLoansSourceFeeRecovery is TestBaseWorkflow {
256
256
  address(REV_OWNER)
257
257
  );
258
258
 
259
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
260
-
261
259
  // Deploy fee project.
262
260
  vm.prank(multisig());
263
261
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -355,8 +355,6 @@ contract REVLoansSourcedTests is TestBaseWorkflow {
355
355
  address(REV_OWNER)
356
356
  );
357
357
 
358
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
359
-
360
358
  // Approve the basic deployer to configure the project.
361
359
  vm.prank(address(multisig()));
362
360
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -331,8 +331,6 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow {
331
331
  address(REV_OWNER)
332
332
  );
333
333
 
334
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
335
-
336
334
  // Approve the basic deployer to configure the project.
337
335
  vm.prank(address(multisig()));
338
336
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -234,8 +234,6 @@ contract TestBurnHeldTokens is TestBaseWorkflow {
234
234
  address(REV_OWNER)
235
235
  );
236
236
 
237
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
238
-
239
237
  // Deploy fee project.
240
238
  vm.prank(multisig());
241
239
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -162,7 +162,6 @@ contract TestCEIPattern is TestBaseWorkflow {
162
162
  address(REV_OWNER)
163
163
  );
164
164
 
165
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
166
165
  vm.prank(multisig());
167
166
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
168
167
  _deployFeeProject();
@@ -247,8 +247,6 @@ contract TestCashOutCallerValidation is TestBaseWorkflow {
247
247
  address(REV_OWNER)
248
248
  );
249
249
 
250
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
251
-
252
250
  // Approve the deployer to configure the fee project.
253
251
  vm.prank(multisig());
254
252
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -177,8 +177,6 @@ contract TestConversionDocumentation is TestBaseWorkflow {
177
177
  address(REV_OWNER)
178
178
  );
179
179
 
180
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
181
-
182
180
  // Deploy fee project as revnet.
183
181
  vm.prank(multisig());
184
182
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -128,8 +128,6 @@ contract TestCrossCurrencyReclaim is TestBaseWorkflow {
128
128
  address(REV_OWNER)
129
129
  );
130
130
 
131
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
132
-
133
131
  vm.prank(multisig());
134
132
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
135
133
 
@@ -125,7 +125,6 @@ contract TestCrossSourceReallocation is TestBaseWorkflow {
125
125
  address(REV_OWNER)
126
126
  );
127
127
 
128
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
129
128
  vm.prank(multisig());
130
129
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
131
130
  _deployFeeProject();
@@ -318,8 +318,6 @@ contract TestERC2771MetaTx is TestBaseWorkflow {
318
318
  address(REV_OWNER)
319
319
  );
320
320
 
321
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
322
-
323
321
  // Approve the deployer to configure the project.
324
322
  vm.prank(address(multisig()));
325
323
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -116,7 +116,6 @@ contract TestEmptyBuybackSpecs is TestBaseWorkflow {
116
116
  address(REV_OWNER)
117
117
  );
118
118
 
119
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
120
119
  vm.prank(multisig());
121
120
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
122
121
  }
@@ -128,7 +128,6 @@ contract TestFlashLoanSurplus is TestBaseWorkflow {
128
128
  address(REV_OWNER)
129
129
  );
130
130
 
131
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
132
131
  vm.prank(multisig());
133
132
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
134
133
  _deployFeeProject();
@@ -118,7 +118,6 @@ contract TestHookArrayOOB is TestBaseWorkflow {
118
118
  address(REV_OWNER)
119
119
  );
120
120
 
121
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
122
121
  vm.prank(multisig());
123
122
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
124
123
  }
@@ -126,7 +126,6 @@ contract TestLiquidationBehavior is TestBaseWorkflow {
126
126
  address(REV_OWNER)
127
127
  );
128
128
 
129
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
130
129
  vm.prank(multisig());
131
130
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
132
131
  _deployFeeProject();
@@ -135,8 +135,6 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
135
135
  address(REV_OWNER)
136
136
  );
137
137
 
138
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
139
-
140
138
  vm.prank(multisig());
141
139
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
142
140
 
@@ -238,8 +238,6 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
238
238
  address(REV_OWNER)
239
239
  );
240
240
 
241
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
242
-
243
241
  // Approve the deployer to configure the fee project.
244
242
  vm.prank(multisig());
245
243
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -128,8 +128,6 @@ contract TestLongTailEconomics is TestBaseWorkflow {
128
128
  address(REV_OWNER)
129
129
  );
130
130
 
131
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
132
-
133
131
  vm.prank(multisig());
134
132
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
135
133
 
@@ -317,8 +317,6 @@ contract TestLowFindings is TestBaseWorkflow {
317
317
  address(REV_OWNER)
318
318
  );
319
319
 
320
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
321
-
322
320
  // Deploy fee project.
323
321
  vm.prank(multisig());
324
322
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -124,7 +124,6 @@ contract TestMixedFixes is TestBaseWorkflow {
124
124
  address(REV_OWNER)
125
125
  );
126
126
 
127
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
128
127
  vm.prank(multisig());
129
128
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
130
129
  _deployFeeProject();
@@ -285,8 +285,6 @@ contract TestPermit2Signatures is TestBaseWorkflow {
285
285
  address(REV_OWNER)
286
286
  );
287
287
 
288
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
289
-
290
288
  // Approve the basic deployer to configure the project.
291
289
  vm.prank(address(multisig()));
292
290
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -179,7 +179,6 @@ contract TestReallocationSandwich is TestBaseWorkflow {
179
179
  address(REV_OWNER)
180
180
  );
181
181
 
182
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
183
182
  vm.prank(multisig());
184
183
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
185
184
 
@@ -168,8 +168,6 @@ contract TestRevnetRegressions is TestBaseWorkflow {
168
168
  address(REV_OWNER)
169
169
  );
170
170
 
171
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
172
-
173
171
  vm.prank(multisig());
174
172
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
175
173
  }
@@ -117,7 +117,6 @@ contract TestSplitWeightAdjustment is TestBaseWorkflow {
117
117
  address(REV_OWNER)
118
118
  );
119
119
 
120
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
121
120
  vm.prank(multisig());
122
121
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
123
122
  }
@@ -341,7 +340,6 @@ contract TestSplitWeightAdjustment is TestBaseWorkflow {
341
340
  TRUSTED_FORWARDER,
342
341
  address(ammOwner)
343
342
  );
344
- ammOwner.initialize(IREVDeployer(address(ammDeployer)));
345
343
 
346
344
  vm.prank(multisig());
347
345
  jbProjects().approve(address(ammDeployer), FEE_PROJECT_ID);
@@ -137,8 +137,6 @@ contract TestSplitWeightE2E is TestBaseWorkflow {
137
137
  address(REV_OWNER)
138
138
  );
139
139
 
140
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
141
-
142
140
  vm.prank(multisig());
143
141
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
144
142
 
@@ -438,7 +436,6 @@ contract TestSplitWeightE2E is TestBaseWorkflow {
438
436
  TRUSTED_FORWARDER,
439
437
  address(ammOwner)
440
438
  );
441
- ammOwner.initialize(IREVDeployer(address(ammDeployer)));
442
439
 
443
440
  vm.prank(multisig());
444
441
  jbProjects().approve(address(ammDeployer), FEE_PROJECT_ID);
@@ -349,8 +349,6 @@ contract TestSplitWeightFork is TestBaseWorkflow {
349
349
  address(REV_OWNER)
350
350
  );
351
351
 
352
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
353
-
354
352
  vm.prank(multisig());
355
353
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
356
354
 
@@ -172,7 +172,6 @@ contract TestStageTransitionBorrowable is TestBaseWorkflow {
172
172
  address(REV_OWNER)
173
173
  );
174
174
 
175
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
176
175
  vm.prank(multisig());
177
176
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
178
177
 
@@ -113,7 +113,6 @@ contract TestSwapTerminalPermission is TestBaseWorkflow {
113
113
  address(REV_OWNER)
114
114
  );
115
115
 
116
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
117
116
  vm.prank(multisig());
118
117
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
119
118
 
@@ -133,8 +133,6 @@ contract TestUint112Overflow is TestBaseWorkflow {
133
133
  address(REV_OWNER)
134
134
  );
135
135
 
136
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
137
-
138
136
  // Deploy fee project
139
137
  vm.prank(multisig());
140
138
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -126,7 +126,6 @@ contract TestZeroRepayment is TestBaseWorkflow {
126
126
  address(REV_OWNER)
127
127
  );
128
128
 
129
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
130
129
  vm.prank(multisig());
131
130
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
132
131
  _deployFeeProject();
@@ -183,8 +183,6 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
183
183
  address(REV_OWNER)
184
184
  );
185
185
 
186
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
187
-
188
186
  // Approve the deployer to configure the fee project.
189
187
  vm.prank(multisig());
190
188
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
@@ -354,8 +354,6 @@ abstract contract ForkTestBase is TestBaseWorkflow {
354
354
  address(REV_OWNER)
355
355
  );
356
356
 
357
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
358
-
359
357
  vm.prank(multisig());
360
358
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
361
359
 
@@ -128,7 +128,6 @@ contract TestBurnPermissionRequired is TestBaseWorkflow {
128
128
  address(REV_OWNER)
129
129
  );
130
130
 
131
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
132
131
  vm.prank(multisig());
133
132
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
134
133
  _deployFeeProject();
@@ -104,8 +104,6 @@ contract TestCashOutBuybackFeeLeak is TestBaseWorkflow {
104
104
  address(revOwner)
105
105
  );
106
106
 
107
- revOwner.initialize(IREVDeployer(address(revDeployer)));
108
-
109
107
  vm.prank(multisig());
110
108
  jbProjects().approve(address(revDeployer), feeProjectId);
111
109
 
@@ -126,7 +126,6 @@ contract TestCrossRevnetLiquidation is TestBaseWorkflow {
126
126
  address(REV_OWNER)
127
127
  );
128
128
 
129
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
130
129
  vm.prank(multisig());
131
130
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
132
131
  _deployFeeProject();
@@ -130,7 +130,6 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
130
130
  address(REV_OWNER)
131
131
  );
132
132
 
133
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
134
133
  vm.prank(multisig());
135
134
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
136
135
  _deployFeeProject();
@@ -132,7 +132,6 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
132
132
  address(REV_OWNER)
133
133
  );
134
134
 
135
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
136
135
  vm.prank(multisig());
137
136
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
138
137
  _deployFeeProject();
@@ -141,8 +141,6 @@ contract TestZeroPriceFeed is TestBaseWorkflow {
141
141
  address(REV_OWNER)
142
142
  );
143
143
 
144
- REV_OWNER.initialize(IREVDeployer(address(REV_DEPLOYER)));
145
-
146
144
  vm.prank(multisig());
147
145
  jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
148
146