@rev-net/core-v6 0.0.66 → 0.0.68

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rev-net/core-v6",
3
- "version": "0.0.66",
3
+ "version": "0.0.68",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,13 +8,19 @@ Use this file when you need revnet-specific risks, state reads, constants, or ex
8
8
 
9
9
  | Event | When It Fires |
10
10
  |-------|---------------|
11
- | `AutoIssue(revnetId, stageId, beneficiary, count, caller)` | When tokens are auto-issued for a beneficiary during a stage via `autoIssueFor`. |
12
- | `BurnHeldTokens(revnetId, count, caller)` | When held tokens are burned from the deployer contract via `burnHeldTokensOf`. |
13
11
  | `DeployRevnet(revnetId, configuration, terminalConfigurations, suckerDeploymentConfiguration, rulesetConfigurations, encodedConfigurationHash, caller)` | When a new revnet is deployed via `deployFor`. |
14
12
  | `DeploySuckers(revnetId, encodedConfigurationHash, suckerDeploymentConfiguration, caller)` | When suckers are deployed for a revnet via `deploySuckersFor`. |
15
- | `ReplaceOperator(revnetId, newOperator, caller)` | When the operator of a revnet is replaced via `setOperatorOf`. |
16
13
  | `SetCashOutDelay(revnetId, cashOutDelay, caller)` | When the cash out delay is set for a revnet during deployment to a new chain. |
17
- | `StoreAutoIssuanceAmount(revnetId, stageId, beneficiary, count, caller)` | When an auto-issuance amount is stored for a beneficiary during deployment. |
14
+ | `StoreAutoIssuanceAmount(revnetId, stageId, beneficiary, count, caller)` | When an auto-issuance amount is recorded for a beneficiary during deployment (the same allocation is bundled into REVOwner's `initializeRevnet` call later in the same transaction). |
15
+
16
+ ### REVOwner
17
+
18
+ | Event | When It Fires |
19
+ |-------|---------------|
20
+ | `AutoIssue(revnetId, stageId, beneficiary, count, caller)` | When tokens are auto-issued for a beneficiary during a stage via `autoIssueFor`. |
21
+ | `BurnHeldTokens(revnetId, count, caller)` | When held project tokens are burned from this contract via `burnHeldTokensOf`. |
22
+ | `InitializeRevnet(revnetId, caller)` | When a revnet's full runtime state is bound during deployment via `initializeRevnet`. |
23
+ | `ReplaceOperator(revnetId, newOperator, caller)` | When the operator of a revnet is replaced via `setOperatorOf`. |
18
24
 
19
25
  ### REVLoans
20
26
 
@@ -33,16 +39,26 @@ Use this file when you need revnet-specific risks, state reads, constants, or ex
33
39
  | Error | When It Fires |
34
40
  |-------|---------------|
35
41
  | `REVDeployer_AutoIssuanceBeneficiaryZeroAddress()` | When an auto-issuance config has a zero-address beneficiary. |
36
- | `REVDeployer_CashOutDelayNotFinished(cashOutDelay, blockTimestamp)` | When a cash out is attempted before the 30-day delay has elapsed. |
37
42
  | `REVDeployer_CashOutsCantBeTurnedOffCompletely(cashOutTaxRate, maxCashOutTaxRate)` | When `cashOutTaxRate` equals `MAX_CASH_OUT_TAX_RATE` (10,000). Must be strictly less. |
38
43
  | `REVDeployer_MustHaveSplits()` | When a stage with `splitPercent > 0` has no splits configured. |
39
- | `REVDeployer_NothingToAutoIssue()` | When `autoIssueFor` is called but no tokens are available for auto-issuance. |
40
- | `REVDeployer_NothingToBurn()` | When `burnHeldTokensOf` is called but the deployer holds no tokens. |
41
44
  | `REVDeployer_RulesetDoesNotAllowDeployingSuckers()` | When `deploySuckersFor` is called but the current ruleset's `extraMetadata` bit 2 is not set. |
42
- | `REVDeployer_StageNotStarted(stageId)` | When `autoIssueFor` is called for a stage that hasn't started yet. |
43
45
  | `REVDeployer_StagesRequired()` | When `deployFor` is called with zero stage configurations. |
44
46
  | `REVDeployer_StageTimesMustIncrease()` | When stage `startsAtOrAfter` values are not strictly increasing. |
45
- | `REVDeployer_Unauthorized(revnetId, caller)` | When a non-operator calls a operator-only function. |
47
+ | `REVDeployer_Unauthorized(revnetId, caller)` | When a non-operator calls an operator-only function on REVDeployer (e.g., `deploySuckersFor`). |
48
+
49
+ ### REVOwner
50
+
51
+ | Error | When It Fires |
52
+ |-------|---------------|
53
+ | `REVOwner_AlreadyInitialized(deployer)` | When `setDeployer` is called after the deployer binding has already been set. |
54
+ | `REVOwner_CashOutDelayNotFinished(cashOutDelay, blockTimestamp)` | When a cash out is attempted before the 30-day delay has elapsed. |
55
+ | `REVOwner_InvalidLoanSourceToken(revnetId, token)` | When a peer-snapshot loan-state call references a token the revnet does not source loans from. |
56
+ | `REVOwner_NativeFeeValueMismatch(expected, actual)` | When `afterCashOutRecordedWith` receives a native-token fee whose `msg.value` does not match the expected `forwardedAmount.value`. |
57
+ | `REVOwner_NothingToAutoIssue(revnetId, stageId, beneficiary)` | When `autoIssueFor` is called but no tokens are recorded for the (revnet, stage, beneficiary) triple. |
58
+ | `REVOwner_NothingToBurn(revnetId, holder)` | When `burnHeldTokensOf` is called but REVOwner holds no project tokens for the revnet. |
59
+ | `REVOwner_StageNotStarted(stageId)` | When `autoIssueFor` is called for a stage that hasn't started yet. |
60
+ | `REVOwner_Unauthorized(caller, expectedCaller)` | When a deployer-only entrypoint (`setDeployer`, `initializeRevnet`) is called by a non-deployer. |
61
+ | `REVOwner_UnauthorizedOperator(revnetId, caller)` | When `setOperatorOf` is called by an address that is not the revnet's current operator. |
46
62
 
47
63
  ### REVLoans
48
64
 
@@ -96,17 +112,20 @@ Use this file when you need revnet-specific risks, state reads, constants, or ex
96
112
 
97
113
  | Mapping | Visibility | Type | Purpose |
98
114
  |---------|-----------|------|---------|
99
- | `amountToAutoIssue` | `public` | `revnetId => stageId => beneficiary => uint256` | Premint tokens per stage per beneficiary |
100
115
  | `hashedEncodedConfigurationOf` | `public` | `revnetId => bytes32` | Config hash for cross-chain sucker validation |
101
- | `_extraOperatorPermissions` | `internal` | `revnetId => uint256[]` | Custom permissions for operator (no auto-getter) |
102
116
 
103
117
  ### REVOwner
104
118
 
105
- | Mapping | Visibility | Type | Purpose |
119
+ | Storage | Visibility | Type | Purpose |
106
120
  |---------|-----------|------|---------|
107
- | `DEPLOYER` | `public` | `address` | REVDeployer address (storage variable, set once by the REVOwner initializer using the precomputed canonical deployer address) |
108
- | `cashOutDelayOf` | `public` | `revnetId => uint256` | Timestamp when cash outs unlock (0 = no delay). Set by REVDeployer via `setCashOutDelayOf()`. |
109
- | `tiered721HookOf` | `public` | `revnetId => address` | Deployed 721 hook address (if any). Set by REVDeployer via `setTiered721HookOf()`. |
121
+ | `deployer` | `public` | `IREVDeployer` | REVDeployer address (set once by the REVOwner initializer using the precomputed canonical deployer address) |
122
+ | `CONTROLLER` | `public` | `IJBController` | Cached from `deployer.CONTROLLER()` at `setDeployer` time. Used by `autoIssueFor` and `burnHeldTokensOf`. |
123
+ | `PERMISSIONS` | `public` | `IJBPermissions` | Cached from `deployer.PERMISSIONS()` at `setDeployer` time. Backs operator permission grants. |
124
+ | `PROJECTS` | `public` | `IJBProjects` | Cached from `deployer.PROJECTS()` at `setDeployer` time. Used by `onERC721Received` to verify the NFT origin. |
125
+ | `cashOutDelayOf` | `public` | `revnetId => uint256` | Timestamp when cash outs unlock (0 = no delay). Populated by REVDeployer via the bundled `initializeRevnet()` call. |
126
+ | `tiered721HookOf` | `public` | `revnetId => address` | Deployed 721 hook address (if any). Populated by REVDeployer via the bundled `initializeRevnet()` call. |
127
+ | `amountToAutoIssue` | `public` | `revnetId => stageId => beneficiary => uint256` | Pre-mint allocation per stage per beneficiary. Populated by REVDeployer via the bundled `initializeRevnet()` call. |
128
+ | `_extraOperatorPermissions` | `internal` | `revnetId => uint256[]` | Custom operator permissions appended on top of the protocol-default set (no auto-getter). |
110
129
 
111
130
  ### REVLoans
112
131
 
@@ -122,14 +141,14 @@ Use this file when you need revnet-specific risks, state reads, constants, or ex
122
141
 
123
142
  ## Gotchas
124
143
 
125
- 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.
144
+ 1. **Revnets are permanently ownerless.** `REVOwner` holds the project NFT for every Revnet and exposes no function to release it. Stage parameters cannot be changed after deployment.
126
145
  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.
127
146
  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.
128
147
  4. **Loan ID encoding.** `loanId = revnetId * 1_000_000_000_000 + loanNumber`. Each revnet supports ~1 trillion loans. Use `revnetIdOfLoanWith(loanId)` to decode.
129
148
  5. **uint112 truncation risk.** `REVLoan.amount` and `REVLoan.collateral` are `uint112`. Values above ~5.19e33 truncate silently.
130
149
  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.
131
150
  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.
132
- 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.
151
+ 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 populated by REVDeployer during deployment via the bundled `initializeRevnet()` call. REVLoans imports IREVOwner (not IREVDeployer) to read it.
133
152
  9. **`cashOutTaxRate` cannot be MAX.** Must be strictly less than `MAX_CASH_OUT_TAX_RATE` (10,000). Revnets cannot fully disable cash outs.
134
153
  10. **Split operator is singular.** Only ONE address can be operator at a time. The operator can replace itself via `setOperatorOf` but cannot delegate or multi-sig.
135
154
  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.
@@ -10,8 +10,8 @@ Deploy and manage Revnets -- autonomous, unowned Juicebox projects with staged i
10
10
 
11
11
  | Contract | Role |
12
12
  |----------|------|
13
- | `REVDeployer` | Deploys revnets, permanently owns the project NFT. Manages stages, splits, auto-issuance, buyback hooks, suckers, 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`. |
14
- | `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()`). |
13
+ | `REVDeployer` | Deploys revnets. Configures stages, splits, auto-issuance amounts, buyback hooks, suckers, operators, and stores `hashedEncodedConfigurationOf`. Exposes `OWNER()` view returning the REVOwner address. Hands the JBProjects NFT to REVOwner mid-deploy and finishes by bundling every per-revnet runtime write into a single `REVOwner.initializeRevnet(revnetId, init)` call (cash-out delay, tiered 721 hook, auto-issuance allocations, extra operator permissions, the initial operator, and any integration grants). |
14
+ | `REVOwner` | Project NFT owner for every revnet and runtime hook for all revnets. Implements `IJBRulesetDataHook` + `IJBCashOutHook` + `IERC721Receiver`. Set as the `dataHook` in each revnet's ruleset metadata. Holds the JBProjects NFT (project owner of record). Manages operator permissions (grants `DEPLOY_SUCKERS` + `MAP_SUCKER_TOKEN` to REVDeployer in `setDeployer` so the deployer can continue driving sucker setup). Exposes `initializeRevnet` (deployer-only single-call setup), `autoIssueFor`, `burnHeldTokensOf`, `setOperatorOf`, `isOperatorOf`. Handles pay hooks, cash-out hooks, mint permissions, and sucker verification. Stores `cashOutDelayOf`, `tiered721HookOf`, `amountToAutoIssue`, and extra-operator-permission state. |
15
15
  | `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). |
16
16
 
17
17
  ## Key Functions
@@ -38,14 +38,15 @@ Deploy and manage Revnets -- autonomous, unowned Juicebox projects with staged i
38
38
 
39
39
  | Function | Permissions | What it does |
40
40
  |----------|------------|-------------|
41
- | `REVDeployer.setOperatorOf(revnetId, newOperator)` | Split operator | Replace the current operator. Revokes old permissions, grants new ones. |
41
+ | `REVOwner.setOperatorOf(revnetId, newOperator)` | Current operator | Replace the current operator. Revokes old permissions, grants new ones — all scoped on REVOwner's account. |
42
+ | `REVOwner.isOperatorOf(revnetId, addr)` | View | Returns whether `addr` holds the revnet's operator permissions on REVOwner's account. |
42
43
 
43
44
  ### Auto-Issuance
44
45
 
45
46
  | Function | Permissions | What it does |
46
47
  |----------|------------|-------------|
47
- | `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. |
48
- | `REVDeployer.burnHeldTokensOf(revnetId)` | Permissionless | Burn any reserved tokens held by the deployer (when splits < 100%). |
48
+ | `REVOwner.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. |
49
+ | `REVOwner.burnHeldTokensOf(revnetId)` | Permissionless | Burn any reserved tokens held by REVOwner (e.g., leftovers when reserved-token splits don't sum to 100%, since the JBController mints the residue to the project owner — REVOwner). |
49
50
 
50
51
  ### Loans -- Borrowing
51
52
 
@@ -475,9 +475,9 @@ contract DeployScript is Script, Sphinx {
475
475
  address(revOwner)
476
476
  )
477
477
  });
478
- if (address(revOwner.deployer()) == address(0)) {
479
- revOwner.setDeployer(IREVDeployer(_deployerAddr));
480
- }
478
+ // Deploy `REVDeployer` (or pick up the existing bytecode) BEFORE binding it on `revOwner`. `setDeployer`
479
+ // reads `CONTROLLER()` / `PERMISSIONS()` / `PROJECTS()` off the bound contract, so binding a CREATE2
480
+ // prediction that has no code yet would revert the whole deploy on a fresh chain.
481
481
  REVDeployer _basicDeployer = _deployerIsDeployed
482
482
  ? REVDeployer(payable(_deployerAddr))
483
483
  : new REVDeployer{salt: _DEPLOYER_SALT}({
@@ -493,6 +493,9 @@ contract DeployScript is Script, Sphinx {
493
493
  trustedForwarder: trustedForwarder,
494
494
  owner: address(revOwner)
495
495
  });
496
+ if (address(revOwner.deployer()) == address(0)) {
497
+ revOwner.setDeployer(IREVDeployer(address(_basicDeployer)));
498
+ }
496
499
 
497
500
  // Only configure the fee project if it doesn't already have a controller.
498
501
  // This handles both fresh deploys and restarts where singletons exist but the fee project