@rev-net/core-v6 0.0.37 → 0.0.39

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 (107) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +6 -7
  3. package/foundry.toml +1 -1
  4. package/package.json +23 -16
  5. package/references/operations.md +1 -1
  6. package/references/runtime.md +1 -1
  7. package/script/Deploy.s.sol +12 -9
  8. package/src/REVDeployer.sol +60 -65
  9. package/src/REVHiddenTokens.sol +2 -2
  10. package/src/REVLoans.sol +17 -10
  11. package/src/REVOwner.sol +121 -14
  12. package/src/interfaces/IREVDeployer.sol +2 -1
  13. package/src/interfaces/IREVHiddenTokens.sol +4 -1
  14. package/src/interfaces/IREVOwner.sol +5 -0
  15. package/ADMINISTRATION.md +0 -73
  16. package/ARCHITECTURE.md +0 -116
  17. package/AUDIT_INSTRUCTIONS.md +0 -90
  18. package/RISKS.md +0 -107
  19. package/SKILLS.md +0 -46
  20. package/STYLE_GUIDE.md +0 -610
  21. package/USER_JOURNEYS.md +0 -195
  22. package/foundry.lock +0 -11
  23. package/slither-ci.config.json +0 -10
  24. package/sphinx.lock +0 -507
  25. package/test/REV.integrations.t.sol +0 -573
  26. package/test/REVAutoIssuanceFuzz.t.sol +0 -328
  27. package/test/REVDeployerRegressions.t.sol +0 -396
  28. package/test/REVInvincibility.t.sol +0 -1371
  29. package/test/REVInvincibilityHandler.sol +0 -387
  30. package/test/REVLifecycle.t.sol +0 -420
  31. package/test/REVLoans.invariants.t.sol +0 -724
  32. package/test/REVLoansAttacks.t.sol +0 -816
  33. package/test/REVLoansFeeRecovery.t.sol +0 -783
  34. package/test/REVLoansFindings.t.sol +0 -711
  35. package/test/REVLoansRegressions.t.sol +0 -364
  36. package/test/REVLoansSourceFeeRecovery.t.sol +0 -517
  37. package/test/REVLoansSourced.t.sol +0 -1839
  38. package/test/REVLoansUnSourced.t.sol +0 -409
  39. package/test/TestAuditFixVerification.t.sol +0 -675
  40. package/test/TestBurnHeldTokens.t.sol +0 -394
  41. package/test/TestCEIPattern.t.sol +0 -508
  42. package/test/TestCashOutCallerValidation.t.sol +0 -452
  43. package/test/TestConversionDocumentation.t.sol +0 -365
  44. package/test/TestCrossCurrencyReclaim.t.sol +0 -610
  45. package/test/TestCrossSourceReallocation.t.sol +0 -361
  46. package/test/TestERC2771MetaTx.t.sol +0 -585
  47. package/test/TestEmptyBuybackSpecs.t.sol +0 -300
  48. package/test/TestFlashLoanSurplus.t.sol +0 -365
  49. package/test/TestHiddenTokens.t.sol +0 -474
  50. package/test/TestHookArrayOOB.t.sol +0 -278
  51. package/test/TestLiquidationBehavior.t.sol +0 -398
  52. package/test/TestLoanSourceRotation.t.sol +0 -553
  53. package/test/TestLoansCashOutDelay.t.sol +0 -493
  54. package/test/TestLongTailEconomics.t.sol +0 -677
  55. package/test/TestLowFindings.t.sol +0 -677
  56. package/test/TestMixedFixes.t.sol +0 -593
  57. package/test/TestPermit2Signatures.t.sol +0 -683
  58. package/test/TestReallocationSandwich.t.sol +0 -412
  59. package/test/TestRevnetRegressions.t.sol +0 -350
  60. package/test/TestSplitWeightAdjustment.t.sol +0 -527
  61. package/test/TestSplitWeightE2E.t.sol +0 -605
  62. package/test/TestSplitWeightFork.t.sol +0 -855
  63. package/test/TestStageTransitionBorrowable.t.sol +0 -301
  64. package/test/TestSwapTerminalPermission.t.sol +0 -262
  65. package/test/TestTerminalEncodingInHash.t.sol +0 -326
  66. package/test/TestUint112Overflow.t.sol +0 -311
  67. package/test/TestZeroAmountLoanGuard.t.sol +0 -378
  68. package/test/TestZeroRepayment.t.sol +0 -354
  69. package/test/audit/CrossChainBuybackRouteMismatch.t.sol +0 -184
  70. package/test/audit/HiddenSupplyCashout.t.sol +0 -61
  71. package/test/audit/LoanIdOverflowGuard.t.sol +0 -523
  72. package/test/audit/NemesisVerification.t.sol +0 -97
  73. package/test/audit/OperatorDelegation.t.sol +0 -356
  74. package/test/audit/PhantomSurplusTerminal.t.sol +0 -367
  75. package/test/audit/REVOwnerCurrencyMismatch.t.sol +0 -188
  76. package/test/audit/REVOwnerRemoteSurplusCurrencyMismatch.t.sol +0 -140
  77. package/test/audit/ReallocatePermission.t.sol +0 -363
  78. package/test/audit/RemoteLoanAccountingGap.t.sol +0 -74
  79. package/test/audit/SupportsInterfaceTest.t.sol +0 -51
  80. package/test/audit/TestFeeAllowanceLeak.t.sol +0 -197
  81. package/test/audit/TestLoansAndDeployerFixes.t.sol +0 -576
  82. package/test/fork/ForkTestBase.sol +0 -727
  83. package/test/fork/TestAutoIssuanceFork.t.sol +0 -148
  84. package/test/fork/TestCashOutFork.t.sol +0 -253
  85. package/test/fork/TestIssuanceDecayFork.t.sol +0 -158
  86. package/test/fork/TestLoanAdversarialFork.t.sol +0 -744
  87. package/test/fork/TestLoanBorrowFork.t.sol +0 -163
  88. package/test/fork/TestLoanCrossRulesetFork.t.sol +0 -308
  89. package/test/fork/TestLoanERC20Fork.t.sol +0 -459
  90. package/test/fork/TestLoanLiquidationFork.t.sol +0 -135
  91. package/test/fork/TestLoanReallocateFork.t.sol +0 -113
  92. package/test/fork/TestLoanRepayFork.t.sol +0 -188
  93. package/test/fork/TestLoanTransferFork.t.sol +0 -143
  94. package/test/fork/TestPermit2PaymentFork.t.sol +0 -300
  95. package/test/fork/TestSplitWeightFork.t.sol +0 -189
  96. package/test/helpers/MaliciousContracts.sol +0 -247
  97. package/test/helpers/REVEmpty721Config.sol +0 -45
  98. package/test/mock/MockBuybackCashOutRecorder.sol +0 -84
  99. package/test/mock/MockBuybackDataHook.sol +0 -112
  100. package/test/mock/MockBuybackDataHookMintPath.sol +0 -68
  101. package/test/mock/MockSuckerRegistry.sol +0 -17
  102. package/test/regression/TestBurnPermissionRequired.t.sol +0 -294
  103. package/test/regression/TestCashOutBuybackFeeLeak.t.sol +0 -232
  104. package/test/regression/TestCrossRevnetLiquidation.t.sol +0 -255
  105. package/test/regression/TestCumulativeLoanCounter.t.sol +0 -361
  106. package/test/regression/TestLiquidateGapHandling.t.sol +0 -394
  107. package/test/regression/TestZeroPriceFeed.t.sol +0 -422
package/src/REVOwner.sol CHANGED
@@ -9,12 +9,14 @@ import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDa
9
9
  import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
10
10
  import {JBCashOuts} from "@bananapus/core-v6/src/libraries/JBCashOuts.sol";
11
11
  import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
12
+ import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
12
13
  import {JBAfterCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBAfterCashOutRecordedContext.sol";
13
14
  import {JBBeforeCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforeCashOutRecordedContext.sol";
14
15
  import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforePayRecordedContext.sol";
15
16
  import {JBCashOutHookSpecification} from "@bananapus/core-v6/src/structs/JBCashOutHookSpecification.sol";
16
17
  import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
17
18
  import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
19
+ import {IJBPeerChainAdjustedAccounts} from "@bananapus/suckers-v6/src/interfaces/IJBPeerChainAdjustedAccounts.sol";
18
20
  import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
19
21
  import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
20
22
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
@@ -22,11 +24,14 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
22
24
  import {mulDiv} from "@prb/math/src/Common.sol";
23
25
 
24
26
  import {IREVDeployer} from "./interfaces/IREVDeployer.sol";
27
+ import {IREVHiddenTokens} from "./interfaces/IREVHiddenTokens.sol";
28
+ import {IREVLoans} from "./interfaces/IREVLoans.sol";
29
+ import {REVLoanSource} from "./structs/REVLoanSource.sol";
25
30
 
26
31
  /// @notice Handles the runtime data hook and cash out hook behavior for revnets.
27
32
  /// @dev Separated from `REVDeployer` to stay within the EIP-170 contract size limit.
28
33
  /// This contract is set as the `dataHook` in each revnet's ruleset metadata.
29
- contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
34
+ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAccounts {
30
35
  // A library that adds default safety checks to ERC20 functionality.
31
36
  using SafeERC20 for IERC20;
32
37
 
@@ -61,10 +66,10 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
61
66
  uint256 public immutable FEE_REVNET_ID;
62
67
 
63
68
  /// @notice The hidden tokens contract used by all revnets.
64
- address public immutable HIDDEN_TOKENS;
69
+ IREVHiddenTokens public immutable HIDDEN_TOKENS;
65
70
 
66
71
  /// @notice The loan contract used by all revnets.
67
- address public immutable LOANS;
72
+ IREVLoans public immutable LOANS;
68
73
 
69
74
  /// @notice Deploys and tracks suckers for revnets.
70
75
  IJBSuckerRegistry public immutable SUCKER_REGISTRY;
@@ -102,23 +107,21 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
102
107
  /// @param directory The directory of terminals and controllers.
103
108
  /// @param feeRevnetId The Juicebox project ID of the fee revnet.
104
109
  /// @param suckerRegistry The sucker registry.
105
- /// @param loans The loan contract address.
106
- /// @param hiddenTokens The hidden tokens contract address.
110
+ /// @param loans The loan contract.
111
+ /// @param hiddenTokens The hidden tokens contract.
107
112
  constructor(
108
113
  IJBBuybackHookRegistry buybackHook,
109
114
  IJBDirectory directory,
110
115
  uint256 feeRevnetId,
111
116
  IJBSuckerRegistry suckerRegistry,
112
- address loans,
113
- address hiddenTokens
117
+ IREVLoans loans,
118
+ IREVHiddenTokens hiddenTokens
114
119
  ) {
115
120
  BUYBACK_HOOK = buybackHook;
116
121
  DIRECTORY = directory;
117
122
  FEE_REVNET_ID = feeRevnetId;
118
123
  SUCKER_REGISTRY = suckerRegistry;
119
- // slither-disable-next-line missing-zero-check
120
124
  LOANS = loans;
121
- // slither-disable-next-line missing-zero-check
122
125
  HIDDEN_TOKENS = hiddenTokens;
123
126
  _DEPLOYER_BINDER = msg.sender;
124
127
  }
@@ -153,11 +156,23 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
153
156
  JBCashOutHookSpecification[] memory hookSpecifications
154
157
  )
155
158
  {
159
+ // Treat outstanding local loans as temporarily off-terminal revnet assets. Borrowed funds are owed back to
160
+ // the revnet, while burned loan collateral can be re-minted on repayment, so both affect fair cash-out math.
161
+ (uint256 totalBorrowed, uint256 totalCollateral) = _localLoanStateOf({
162
+ revnetId: context.projectId, decimals: context.surplus.decimals, currency: context.surplus.currency
163
+ });
164
+
156
165
  // If the cash out is from a sucker, return the full cash out amount without taxes or fees.
157
166
  // This relies on the sucker registry to only contain trusted sucker contracts deployed via
158
167
  // the registry's own deploySuckersFor flow — external addresses cannot register as suckers.
159
168
  if (_isSuckerOf({revnetId: context.projectId, addr: context.holder})) {
160
- return (0, context.cashOutCount, context.totalSupply, context.surplus.value, hookSpecifications);
169
+ return (
170
+ 0,
171
+ context.cashOutCount,
172
+ context.totalSupply + totalCollateral,
173
+ context.surplus.value + totalBorrowed,
174
+ hookSpecifications
175
+ );
161
176
  }
162
177
 
163
178
  // Keep a reference to the cash out delay of the revnet.
@@ -173,8 +188,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
173
188
 
174
189
  // Compute the cross-chain total supply (local + remote peer chain supplies) for cross-chain-aware bonding
175
190
  // curve.
176
- totalSupply = context.totalSupply + SUCKER_REGISTRY.remoteTotalSupplyOf(context.projectId);
177
- effectiveSurplusValue = context.surplus.value
191
+ totalSupply = context.totalSupply + totalCollateral + SUCKER_REGISTRY.remoteTotalSupplyOf(context.projectId);
192
+ effectiveSurplusValue = context.surplus.value + totalBorrowed
178
193
  + SUCKER_REGISTRY.remoteSurplusOf({
179
194
  projectId: context.projectId,
180
195
  decimals: context.surplus.decimals,
@@ -350,11 +365,35 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
350
365
  {
351
366
  // The loans contract, hidden tokens contract, buyback hook (and its delegates), and suckers are allowed to mint
352
367
  // the revnet's tokens.
353
- return addr == LOANS || addr == HIDDEN_TOKENS || addr == address(BUYBACK_HOOK)
368
+ return addr == address(LOANS) || addr == address(HIDDEN_TOKENS) || addr == address(BUYBACK_HOOK)
354
369
  || BUYBACK_HOOK.hasMintPermissionFor({projectId: revnetId, ruleset: ruleset, addr: addr})
355
370
  || _isSuckerOf({revnetId: revnetId, addr: addr});
356
371
  }
357
372
 
373
+ /// @notice Additional revnet accounts that peer-chain snapshots should include.
374
+ /// @dev Hidden tokens are intentionally excluded. Revnet operators can hide tokens as a security handle without
375
+ /// changing loan or cash-out math for other holders. Outstanding loan debt is counted as both surplus and balance:
376
+ /// it is value owed back to this chain's revnet and should travel to peer snapshots with the collateral supply.
377
+ /// @param revnetId The ID of the revnet being snapshotted.
378
+ /// @param decimals The decimals the returned surplus should use.
379
+ /// @param currency The currency the returned surplus should be in terms of.
380
+ /// @return supply The loan-collateral supply to include in the peer snapshot.
381
+ /// @return surplus The outstanding loan debt to include in `sourceSurplus`.
382
+ /// @return balance The outstanding loan debt to include in `sourceBalance`.
383
+ function peerChainAdjustedAccountsOf(
384
+ uint256 revnetId,
385
+ uint256 decimals,
386
+ uint256 currency
387
+ )
388
+ external
389
+ view
390
+ override
391
+ returns (uint256 supply, uint256 surplus, uint256 balance)
392
+ {
393
+ (surplus, supply) = _localLoanStateOf({revnetId: revnetId, decimals: decimals, currency: currency});
394
+ balance = surplus;
395
+ }
396
+
358
397
  //*********************************************************************//
359
398
  // --------------------- external transactions ----------------------- //
360
399
  //*********************************************************************//
@@ -460,7 +499,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
460
499
  /// @return A flag indicating if the provided interface ID is supported.
461
500
  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
462
501
  return interfaceId == type(IERC165).interfaceId || interfaceId == type(IJBRulesetDataHook).interfaceId
463
- || interfaceId == type(IJBCashOutHook).interfaceId;
502
+ || interfaceId == type(IJBCashOutHook).interfaceId
503
+ || interfaceId == type(IJBPeerChainAdjustedAccounts).interfaceId;
464
504
  }
465
505
 
466
506
  //*********************************************************************//
@@ -475,6 +515,73 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
475
515
  return SUCKER_REGISTRY.isSuckerOf({projectId: revnetId, addr: addr});
476
516
  }
477
517
 
518
+ /// @notice Total outstanding local loan debt and collateral for a revnet.
519
+ /// @dev This is included in cash-out and peer-snapshot math because borrowed funds are still owed to the revnet
520
+ /// and collateral can re-enter supply when the loan is repaid.
521
+ /// @param revnetId The ID of the revnet to check.
522
+ /// @param decimals The decimals the resulting fixed point debt value should use.
523
+ /// @param currency The currency the resulting debt value should be in terms of.
524
+ /// @return borrowedAmount The local outstanding loan debt converted into `currency`.
525
+ /// @return collateralCount The local burned loan collateral count.
526
+ function _localLoanStateOf(
527
+ uint256 revnetId,
528
+ uint256 decimals,
529
+ uint256 currency
530
+ )
531
+ internal
532
+ view
533
+ returns (uint256 borrowedAmount, uint256 collateralCount)
534
+ {
535
+ IREVLoans loans = LOANS;
536
+ if (address(loans) == address(0) || address(loans).code.length == 0) return (0, 0);
537
+
538
+ collateralCount = loans.totalCollateralOf(revnetId);
539
+
540
+ REVLoanSource[] memory sources = loans.loanSourcesOf(revnetId);
541
+ // Loan sources are project configuration, and this read-only aggregation needs the latest terminal/pricing
542
+ // state for each configured source.
543
+ for (uint256 i; i < sources.length; i++) {
544
+ REVLoanSource memory source = sources[i];
545
+ // Each configured source must be queried live so cash-out math includes current outstanding debt.
546
+ // slither-disable-next-line calls-loop
547
+ uint256 tokensLoaned =
548
+ loans.totalBorrowedFrom({revnetId: revnetId, terminal: source.terminal, token: source.token});
549
+ if (tokensLoaned == 0) continue;
550
+
551
+ // Read the source token's accounting context so debt can be normalized before cross-currency conversion.
552
+ // slither-disable-next-line calls-loop
553
+ JBAccountingContext memory accountingContext =
554
+ source.terminal.accountingContextForTokenOf({projectId: revnetId, token: source.token});
555
+
556
+ // Normalize each source from its native token decimals into the caller's requested decimals.
557
+ uint256 normalizedTokens;
558
+ if (accountingContext.decimals > decimals) {
559
+ normalizedTokens = tokensLoaned / (10 ** (accountingContext.decimals - decimals));
560
+ } else if (accountingContext.decimals < decimals) {
561
+ normalizedTokens = tokensLoaned * (10 ** (decimals - accountingContext.decimals));
562
+ } else {
563
+ normalizedTokens = tokensLoaned;
564
+ }
565
+
566
+ if (accountingContext.currency == currency) {
567
+ borrowedAmount += normalizedTokens;
568
+ } else {
569
+ // Convert source-token debt into the requested currency using the loans contract's shared prices.
570
+ // slither-disable-next-line calls-loop
571
+ uint256 pricePerUnit = loans.PRICES()
572
+ .pricePerUnitOf({
573
+ projectId: revnetId,
574
+ pricingCurrency: accountingContext.currency,
575
+ unitCurrency: currency,
576
+ decimals: decimals
577
+ });
578
+ if (pricePerUnit == 0) continue;
579
+
580
+ borrowedAmount += mulDiv({x: normalizedTokens, y: 10 ** decimals, denominator: pricePerUnit});
581
+ }
582
+ }
583
+ }
584
+
478
585
  //*********************************************************************//
479
586
  // --------------------- internal transactions ----------------------- //
480
587
  //*********************************************************************//
@@ -16,6 +16,7 @@ import {REVConfig} from "../structs/REVConfig.sol";
16
16
  import {REVCroptopAllowedPost} from "../structs/REVCroptopAllowedPost.sol";
17
17
  import {REVDeploy721TiersHookConfig} from "../structs/REVDeploy721TiersHookConfig.sol";
18
18
  import {REVSuckerDeploymentConfig} from "../structs/REVSuckerDeploymentConfig.sol";
19
+ import {IREVLoans} from "./IREVLoans.sol";
19
20
 
20
21
  /// @notice Deploys and manages revnets -- Juicebox projects with pre-configured tokenomics.
21
22
  interface IREVDeployer {
@@ -143,7 +144,7 @@ interface IREVDeployer {
143
144
 
144
145
  /// @notice The loan contract used by all revnets.
145
146
  /// @return The loans contract address.
146
- function LOANS() external view returns (address);
147
+ function LOANS() external view returns (IREVLoans);
147
148
 
148
149
  /// @notice The runtime data hook contract that handles pay and cash out callbacks for revnets.
149
150
  /// @return The owner contract address.
@@ -3,7 +3,10 @@ pragma solidity ^0.8.0;
3
3
 
4
4
  import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
5
5
 
6
- /// @notice Manages hiding (burning) and revealing (re-minting) revnet tokens to exclude them from totalSupply.
6
+ /// @notice Manages hiding (burning) and revealing (re-minting) revnet tokens to exclude them from live totalSupply.
7
+ /// @dev Hidden balances are an operator-controlled security handle. They remain revealable, but cash-out and loan
8
+ /// accounting intentionally excludes `totalHiddenOf` so hidden inventory cannot dilute other holders' access to
9
+ /// revnet capital.
7
10
  interface IREVHiddenTokens {
8
11
  /// @notice Emitted when a holder is allowed or disallowed to hide their own tokens.
9
12
  /// @param revnetId The ID of the revnet.
@@ -2,9 +2,14 @@
2
2
  pragma solidity ^0.8.0;
3
3
 
4
4
  import {IREVDeployer} from "./IREVDeployer.sol";
5
+ import {IREVHiddenTokens} from "./IREVHiddenTokens.sol";
5
6
 
6
7
  /// @notice Interface for the REVOwner contract that handles runtime data hook and cash out hook behavior for revnets.
7
8
  interface IREVOwner {
9
+ /// @notice The hidden tokens contract used by the revnet owner hook.
10
+ /// @return The hidden tokens contract.
11
+ function HIDDEN_TOKENS() external view returns (IREVHiddenTokens);
12
+
8
13
  /// @notice The timestamp of when cashouts will become available to a specific revnet's participants.
9
14
  /// @param revnetId The ID of the revnet.
10
15
  /// @return The cash out delay timestamp.
package/ADMINISTRATION.md DELETED
@@ -1,73 +0,0 @@
1
- # Administration
2
-
3
- ## At A Glance
4
-
5
- | Item | Details |
6
- | --- | --- |
7
- | Scope | Revnet deployment shape, bounded runtime operators, loan-owner cosmetics, and optional integration control surfaces |
8
- | Control posture | Intentionally narrow and mostly deployment-defined |
9
- | Highest-risk actions | Bad stage design, wrong split-operator assignment, and misunderstanding which runtime surfaces stay live after launch |
10
- | Recovery posture | Usually replacement, not patching; the design intentionally avoids easy admin escape hatches |
11
-
12
- ## Purpose
13
-
14
- `revnet-core-v6` is designed to collapse ordinary post-launch governance into deployment-time decisions plus a small set of bounded runtime roles. The main administration task is understanding which power still exists and which power was intentionally removed.
15
-
16
- ## Control Model
17
-
18
- - `REVDeployer` holds the project NFT and therefore remains part of the ownership model.
19
- - Revnet economics are mainly fixed at deployment through staged rulesets.
20
- - `REVOwner` provides live runtime policy, but not broad human governance.
21
- - Split operators can hold narrow powers depending on stage and deployment config.
22
- - `REVLoans` has a cosmetic global owner surface, but loan economics are still bounded by revnet logic.
23
-
24
- ## Roles
25
-
26
- | Role | How Assigned | Scope | Notes |
27
- | --- | --- | --- | --- |
28
- | `REVDeployer` | Deployed singleton | Global launcher and project-NFT holder | Part of the ownership model |
29
- | Split operator | Deployment config | Per revnet | Holds only the allowed operator envelope |
30
- | Auto-issuance beneficiary | Deployment config | Per stage | Can receive preconfigured stage issuance |
31
- | Borrower or delegated loan operator | Token holder plus permission | Per holder or loan | Can open or manage loans within loan rules |
32
- | `REVLoans` owner | Constructor owner | Global cosmetic/admin surface | Does not turn Revnets back into ordinary governed projects |
33
-
34
- ## Privileged Surfaces
35
-
36
- - `deployFor(...)` defines the revnet's long-lived shape
37
- - split-operator paths can manage only the permissions left open by deployment
38
- - `autoIssueFor(...)` consumes preconfigured stage issuance
39
- - loan operators can redirect borrowed value if a holder delegates loan permissions
40
- - hidden-token flows require the holder's permission grant and mint permission wiring through `REVOwner`
41
-
42
- ## Immutable And One-Way
43
-
44
- - Stage configuration is effectively permanent after deployment.
45
- - The deployer-held project NFT is not a normal owner-recovery tool.
46
- - Loan collateral is burned at borrow time and only reminted through repayment or documented flows.
47
- - Hidden-token balances change visible supply until reveal.
48
-
49
- ## Operational Notes
50
-
51
- - Treat revnet launch as the real governance decision.
52
- - Validate stage timing, split-operator scope, and optional integrations before deployment.
53
- - Review cash-out delay, hidden-token semantics, and loan permissions together.
54
- - Do not assume there is a broad admin override for bad economics after launch.
55
-
56
- ## Machine Notes
57
-
58
- - Do not describe Revnets as fully adminless if the deployer-held NFT still matters for the trust model.
59
- - Also do not describe them as ordinary owner-controlled projects. The point is that the available control surface is intentionally narrow.
60
- - If a question is about runtime cash-outs, buybacks, or mint permissions, inspect `REVOwner` before inferring behavior from deployment prose.
61
-
62
- ## Recovery
63
-
64
- - If launch-time economics are wrong, recovery usually means replacement, not in-place repair.
65
- - If optional integrations are misconfigured, fix only where the code still exposes a valid path.
66
- - If the design intentionally omitted a recovery path, do not invent one in documentation or ops guidance.
67
-
68
- ## Admin Boundaries
69
-
70
- - No ordinary owner can casually rewrite staged economics after launch.
71
- - Split operators are not general-purpose governors.
72
- - Loan mechanics, hidden-token mechanics, and cash-out policy remain bounded by the deployed revnet logic.
73
- - This repo should not be documented as if it had a normal mutable project-owner model.
package/ARCHITECTURE.md DELETED
@@ -1,116 +0,0 @@
1
- # Architecture
2
-
3
- ## Purpose
4
-
5
- `revnet-core-v6` defines an autonomous Juicebox project pattern with staged, precommitted economics and token-collateralized loans. A revnet is intentionally ownerless after deployment in the human sense: behavior follows staged configuration and constrained runtime hooks instead of ongoing governance.
6
-
7
- ## System Overview
8
-
9
- `REVDeployer` handles launch-time shape, staged rulesets, hook wiring, and runtime wrapper behavior. `REVOwner` provides the owner-like runtime policy surface for pay and cash-out hooks after launch. `REVLoans` manages burn-collateral loan positions represented as NFTs. `REVHiddenTokens` lets holders burn tokens to exclude them from visible supply until they reveal them again.
10
-
11
- ## Core Invariants
12
-
13
- - Revnets are intended to be ownerless after deployment; easy admin recovery paths would violate the product model.
14
- - Stage configuration is effectively permanent once queued.
15
- - Loan collateral is burned, not escrowed.
16
- - Hidden tokens are burned, not escrowed, and reduce visible supply until revealed.
17
- - `REVOwner` and `REVDeployer` are tightly coupled; their setup order matters.
18
- - Cash-out delay affects both exits and borrowing power.
19
- - Cross-chain supply and surplus are part of revnet economics. Local payouts and loans must not ignore remote sucker snapshots.
20
-
21
- ## Modules
22
-
23
- | Module | Responsibility | Notes |
24
- | --- | --- | --- |
25
- | `REVDeployer` | Launch, staged rulesets, hook wiring, permissions, runtime wrapper behavior | Launch-time and runtime wrapper |
26
- | `REVOwner` | Runtime owner-like policy surface | Hook-facing policy |
27
- | `REVLoans` | Borrow, repay, and liquidate burned-collateral loan positions | Economic core |
28
- | `REVHiddenTokens` | Temporary supply exclusion through burn and reveal | Supply-sensitive utility |
29
- | config structs | Stage, loan-source, auto-issuance, and hook config | Launch-time inputs |
30
-
31
- ## Trust Boundaries
32
-
33
- - Treasury and ruleset mechanics remain rooted in `nana-core-v6`.
34
- - Optional integrations come from `nana-buyback-hook-v6`, `nana-router-terminal-v6`, `nana-suckers-v6`, and `nana-721-hook-v6`.
35
- - This repo composes those systems into an ownerless product shape instead of reimplementing them.
36
-
37
- ## Critical Flows
38
-
39
- ### Revnet Lifecycle
40
-
41
- ```text
42
- creator
43
- -> deploys a revnet with a fixed stage sequence
44
- stage transitions
45
- -> activate automatically over time through rulesets
46
- participants
47
- -> pay in, receive tokens, cash out, and interact with enabled integrations
48
- operators or permissionless callers
49
- -> perform bounded maintenance such as auto-issuance claims
50
- ```
51
-
52
- ### Loan Lifecycle
53
-
54
- ```text
55
- borrower
56
- -> burns revnet tokens as collateral
57
- -> borrowability is computed from the current stage, omnichain supply/surplus, and local liquidity caps
58
- -> receives treasury-backed funds through REVLoans
59
- -> later repays to remint collateral
60
- -> or is liquidated after the expiration window
61
- ```
62
-
63
- ## Accounting Model
64
-
65
- The repo does not replace core treasury accounting. Its critical economic logic is the interaction between staged revnet config, burned-collateral loan state, hidden-token supply exclusion, and omnichain revnet state imported from suckers.
66
-
67
- `REVOwner` also composes payment and cash-out hooks. On pay, it can merge 721-tier split forwarding with buyback-hook behavior and scale mint weight so the terminal only mints against the share that actually enters the project. On cash out, it can use omnichain supply and surplus for reclaim math, exempt trusted suckers, and append fee-hook specs.
68
-
69
- ## Security Model
70
-
71
- - The highest-risk interactions sit where stage economics, treasury state, and loan borrowability meet.
72
- - Ownerlessness removes convenient recovery from misconfiguration.
73
- - Hidden-token and burned-collateral semantics materially affect supply-sensitive pricing.
74
- - `REVOwner` is a live runtime policy surface, not just a launch helper.
75
- - Rev cash-out fees stack on top of protocol-fee behavior rather than replacing it.
76
-
77
- ## Safe Change Guide
78
-
79
- - Review deploy-time behavior and runtime wrapper behavior together.
80
- - If stage semantics change, inspect loan math, cash-out behavior, and downstream fee expectations together.
81
- - Do not casually add mutable admin escape hatches.
82
- - If you change borrowability, re-check cash-out-delay gating, omnichain surplus inputs, and local-surplus caps together.
83
- - If you change hook composition, re-check 721 split handling, buyback assumptions, and mint-permission flows.
84
-
85
- ## Cross-Chain Configuration Hash
86
-
87
- `REVDeployer` produces an `encodedConfigurationHash` for each revnet that determines sucker deployment salts. This hash commits the revnet's identity across chains. It includes:
88
-
89
- - `baseCurrency`, `description.name`, `description.ticker`, `description.salt`
90
- - Terminal addresses (order-sensitive)
91
- - Stage parameters (timing, issuance, splits, tax rates, auto-issuances)
92
-
93
- Terminal addresses are included because they are deployed deterministically at the same address across chains. Accounting contexts (token addresses) are excluded because tokens like USDC legitimately differ per chain.
94
-
95
- This means a revnet can only expand to a new chain if it uses the exact same terminal contract it used on the host chain. Different terminal addresses produce a different hash, preventing accidental cross-chain mismatches in sucker deployments.
96
-
97
- ## Canonical Checks
98
-
99
- - cash-out-delay interaction with loans:
100
- `test/TestLoansCashOutDelay.t.sol`
101
- - stage transitions and borrowability drift:
102
- `test/TestStageTransitionBorrowable.t.sol`
103
- - omnichain or phantom-surplus edge cases:
104
- `test/audit/CodexPhantomSurplusTerminal.t.sol`
105
- - terminal encoding in configuration hash:
106
- `test/TestTerminalEncodingInHash.t.sol`
107
-
108
- ## Source Map
109
-
110
- - `src/REVDeployer.sol`
111
- - `src/REVOwner.sol`
112
- - `src/REVLoans.sol`
113
- - `src/REVHiddenTokens.sol`
114
- - `test/TestLoansCashOutDelay.t.sol`
115
- - `test/TestStageTransitionBorrowable.t.sol`
116
- - `test/audit/CodexPhantomSurplusTerminal.t.sol`
@@ -1,90 +0,0 @@
1
- # Audit Instructions
2
-
3
- Revnet is a staged, owner-minimized product layer on top of Juicebox core. Audit it as an economic system, not just a deployer plus a loan contract.
4
-
5
- ## Audit Objective
6
-
7
- Find issues that:
8
-
9
- - break stage progression or let users act under the wrong stage assumptions
10
- - overstate or understate borrowability
11
- - mis-handle hidden tokens or burned-collateral accounting
12
- - give operators or integrations more power than the revnet model intends
13
- - make omnichain supply, surplus, or sucker assumptions drift from runtime behavior
14
-
15
- ## Scope
16
-
17
- In scope:
18
-
19
- - `src/REVDeployer.sol`
20
- - `src/REVOwner.sol`
21
- - `src/REVLoans.sol`
22
- - `src/REVHiddenTokens.sol`
23
- - structs, interfaces, and deployment helpers
24
-
25
- ## Start Here
26
-
27
- 1. `src/REVDeployer.sol`
28
- 2. `src/REVOwner.sol`
29
- 3. `src/REVLoans.sol`
30
- 4. `src/REVHiddenTokens.sol`
31
-
32
- ## Security Model
33
-
34
- Revnet composes several sensitive systems:
35
-
36
- - staged rulesets and launch-time immutability
37
- - runtime pay and cash-out policy in `REVOwner`
38
- - burned-collateral lending in `REVLoans`
39
- - hidden-token supply exclusion in `REVHiddenTokens`
40
-
41
- The main audit mindset is composition:
42
-
43
- - stage economics affect borrowability
44
- - hidden supply affects cash-out math
45
- - omnichain state can affect reclaim and borrowing power
46
- - optional integrations can widen the effective trust surface
47
-
48
- ## Roles And Privileges
49
-
50
- | Role | Powers | How constrained |
51
- |------|--------|-----------------|
52
- | Revnet deployer path | Define long-lived stage and operator shape | Must not retain unexpected mutable governance |
53
- | Split operator | Use the allowed runtime envelope | Must stay within deployment-defined permissions |
54
- | Borrower or delegated operator | Open or manage loans | Must not escape collateral, delay, or source limits |
55
- | Hidden-token user or delegate | Burn and reveal visible supply | Must not create extra supply or break accounting |
56
-
57
- ## Integration Assumptions
58
-
59
- | Dependency | Assumption | What breaks if wrong |
60
- |------------|------------|----------------------|
61
- | `nana-core-v6` | Rulesets, reclaim math, and surplus views stay coherent | Stage and cash-out behavior drift |
62
- | `nana-suckers-v6` | Remote supply/surplus snapshots are authentic | Omnichain reclaim and borrowability drift |
63
- | Buyback and 721 integrations | Hook composition remains consistent with revnet expectations | Pay-path and mint-permission behavior drift |
64
-
65
- ## Critical Invariants
66
-
67
- 1. Stage progression stays monotonic and follows deployed timing.
68
- 2. Borrowability respects cash-out delay, surplus, supply, and source limits.
69
- 3. Burned collateral is not accidentally treated like escrowed collateral.
70
- 4. Hidden-token accounting preserves total claims while changing visible supply intentionally.
71
- 5. Optional integrations do not silently widen revnet authority or mint rights.
72
-
73
- ## Attack Surfaces
74
-
75
- - stage-transition boundaries
76
- - live borrowability and cross-currency debt aggregation
77
- - hidden-token burn and reveal flows
78
- - omnichain surplus and sucker exemptions
79
- - payment and cash-out hook composition in `REVOwner`
80
-
81
- ## Accepted Risks Or Behaviors
82
-
83
- - Revnets intentionally trade recoverability for predictable launch-time economics.
84
- - Some economic surfaces are conservative by design and may refuse otherwise-valid actions rather than risk an unsafe result.
85
-
86
- ## Verification
87
-
88
- - `npm install`
89
- - `forge build`
90
- - `forge test`