@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 +1 -1
- package/references/operations.md +36 -17
- package/references/runtime.md +6 -5
- package/script/Deploy.s.sol +6 -3
- package/src/REVDeployer.sol +125 -285
- package/src/REVOwner.sol +325 -24
- package/src/interfaces/IREVDeployer.sol +0 -50
- package/src/interfaces/IREVOwner.sol +117 -6
- package/src/structs/REVOwnerAutoIssuance.sol +14 -0
- package/src/structs/REVOwnerExtraGrant.sol +12 -0
- package/src/structs/REVOwnerRevnetInit.sol +29 -0
package/package.json
CHANGED
package/references/operations.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
|
119
|
+
| Storage | Visibility | Type | Purpose |
|
|
106
120
|
|---------|-----------|------|---------|
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
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.** `
|
|
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
|
|
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.
|
package/references/runtime.md
CHANGED
|
@@ -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
|
|
14
|
-
| `REVOwner` |
|
|
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
|
-
| `
|
|
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
|
-
| `
|
|
48
|
-
| `
|
|
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
|
|
package/script/Deploy.s.sol
CHANGED
|
@@ -475,9 +475,9 @@ contract DeployScript is Script, Sphinx {
|
|
|
475
475
|
address(revOwner)
|
|
476
476
|
)
|
|
477
477
|
});
|
|
478
|
-
|
|
479
|
-
|
|
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
|