@rev-net/core-v6 0.0.37 → 0.0.40
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/CHANGELOG.md +2 -2
- package/README.md +6 -7
- package/foundry.toml +1 -1
- package/package.json +23 -16
- package/references/operations.md +1 -1
- package/references/runtime.md +1 -1
- package/script/Deploy.s.sol +12 -9
- package/src/REVDeployer.sol +69 -67
- package/src/REVHiddenTokens.sol +2 -2
- package/src/REVLoans.sol +26 -22
- package/src/REVOwner.sol +147 -29
- package/src/interfaces/IREVDeployer.sol +2 -1
- package/src/interfaces/IREVHiddenTokens.sol +4 -1
- package/src/interfaces/IREVOwner.sol +5 -0
- package/src/structs/REVAutoIssuance.sol +4 -2
- package/src/structs/REVConfig.sol +8 -5
- package/src/structs/REVDescription.sol +6 -5
- package/src/structs/REVLoan.sol +8 -5
- package/src/structs/REVStageConfig.sol +14 -16
- package/ADMINISTRATION.md +0 -73
- package/ARCHITECTURE.md +0 -116
- package/AUDIT_INSTRUCTIONS.md +0 -90
- package/RISKS.md +0 -107
- package/SKILLS.md +0 -46
- package/STYLE_GUIDE.md +0 -610
- package/USER_JOURNEYS.md +0 -195
- package/foundry.lock +0 -11
- package/slither-ci.config.json +0 -10
- package/sphinx.lock +0 -507
- package/test/REV.integrations.t.sol +0 -573
- package/test/REVAutoIssuanceFuzz.t.sol +0 -328
- package/test/REVDeployerRegressions.t.sol +0 -396
- package/test/REVInvincibility.t.sol +0 -1371
- package/test/REVInvincibilityHandler.sol +0 -387
- package/test/REVLifecycle.t.sol +0 -420
- package/test/REVLoans.invariants.t.sol +0 -724
- package/test/REVLoansAttacks.t.sol +0 -816
- package/test/REVLoansFeeRecovery.t.sol +0 -783
- package/test/REVLoansFindings.t.sol +0 -711
- package/test/REVLoansRegressions.t.sol +0 -364
- package/test/REVLoansSourceFeeRecovery.t.sol +0 -517
- package/test/REVLoansSourced.t.sol +0 -1839
- package/test/REVLoansUnSourced.t.sol +0 -409
- package/test/TestAuditFixVerification.t.sol +0 -675
- package/test/TestBurnHeldTokens.t.sol +0 -394
- package/test/TestCEIPattern.t.sol +0 -508
- package/test/TestCashOutCallerValidation.t.sol +0 -452
- package/test/TestConversionDocumentation.t.sol +0 -365
- package/test/TestCrossCurrencyReclaim.t.sol +0 -610
- package/test/TestCrossSourceReallocation.t.sol +0 -361
- package/test/TestERC2771MetaTx.t.sol +0 -585
- package/test/TestEmptyBuybackSpecs.t.sol +0 -300
- package/test/TestFlashLoanSurplus.t.sol +0 -365
- package/test/TestHiddenTokens.t.sol +0 -474
- package/test/TestHookArrayOOB.t.sol +0 -278
- package/test/TestLiquidationBehavior.t.sol +0 -398
- package/test/TestLoanSourceRotation.t.sol +0 -553
- package/test/TestLoansCashOutDelay.t.sol +0 -493
- package/test/TestLongTailEconomics.t.sol +0 -677
- package/test/TestLowFindings.t.sol +0 -677
- package/test/TestMixedFixes.t.sol +0 -593
- package/test/TestPermit2Signatures.t.sol +0 -683
- package/test/TestReallocationSandwich.t.sol +0 -412
- package/test/TestRevnetRegressions.t.sol +0 -350
- package/test/TestSplitWeightAdjustment.t.sol +0 -527
- package/test/TestSplitWeightE2E.t.sol +0 -605
- package/test/TestSplitWeightFork.t.sol +0 -855
- package/test/TestStageTransitionBorrowable.t.sol +0 -301
- package/test/TestSwapTerminalPermission.t.sol +0 -262
- package/test/TestTerminalEncodingInHash.t.sol +0 -326
- package/test/TestUint112Overflow.t.sol +0 -311
- package/test/TestZeroAmountLoanGuard.t.sol +0 -378
- package/test/TestZeroRepayment.t.sol +0 -354
- package/test/audit/CrossChainBuybackRouteMismatch.t.sol +0 -184
- package/test/audit/HiddenSupplyCashout.t.sol +0 -61
- package/test/audit/LoanIdOverflowGuard.t.sol +0 -523
- package/test/audit/NemesisVerification.t.sol +0 -97
- package/test/audit/OperatorDelegation.t.sol +0 -356
- package/test/audit/PhantomSurplusTerminal.t.sol +0 -367
- package/test/audit/REVOwnerCurrencyMismatch.t.sol +0 -188
- package/test/audit/REVOwnerRemoteSurplusCurrencyMismatch.t.sol +0 -140
- package/test/audit/ReallocatePermission.t.sol +0 -363
- package/test/audit/RemoteLoanAccountingGap.t.sol +0 -74
- package/test/audit/SupportsInterfaceTest.t.sol +0 -51
- package/test/audit/TestFeeAllowanceLeak.t.sol +0 -197
- package/test/audit/TestLoansAndDeployerFixes.t.sol +0 -576
- package/test/fork/ForkTestBase.sol +0 -727
- package/test/fork/TestAutoIssuanceFork.t.sol +0 -148
- package/test/fork/TestCashOutFork.t.sol +0 -253
- package/test/fork/TestIssuanceDecayFork.t.sol +0 -158
- package/test/fork/TestLoanAdversarialFork.t.sol +0 -744
- package/test/fork/TestLoanBorrowFork.t.sol +0 -163
- package/test/fork/TestLoanCrossRulesetFork.t.sol +0 -308
- package/test/fork/TestLoanERC20Fork.t.sol +0 -459
- package/test/fork/TestLoanLiquidationFork.t.sol +0 -135
- package/test/fork/TestLoanReallocateFork.t.sol +0 -113
- package/test/fork/TestLoanRepayFork.t.sol +0 -188
- package/test/fork/TestLoanTransferFork.t.sol +0 -143
- package/test/fork/TestPermit2PaymentFork.t.sol +0 -300
- package/test/fork/TestSplitWeightFork.t.sol +0 -189
- package/test/helpers/MaliciousContracts.sol +0 -247
- package/test/helpers/REVEmpty721Config.sol +0 -45
- package/test/mock/MockBuybackCashOutRecorder.sol +0 -84
- package/test/mock/MockBuybackDataHook.sol +0 -112
- package/test/mock/MockBuybackDataHookMintPath.sol +0 -68
- package/test/mock/MockSuckerRegistry.sol +0 -17
- package/test/regression/TestBurnPermissionRequired.t.sol +0 -294
- package/test/regression/TestCashOutBuybackFeeLeak.t.sol +0 -232
- package/test/regression/TestCrossRevnetLiquidation.t.sol +0 -255
- package/test/regression/TestCumulativeLoanCounter.t.sol +0 -361
- package/test/regression/TestLiquidateGapHandling.t.sol +0 -394
- 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,18 @@ 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";
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
import {IREVHiddenTokens} from "./interfaces/IREVHiddenTokens.sol";
|
|
28
|
+
import {IREVLoans} from "./interfaces/IREVLoans.sol";
|
|
29
|
+
import {REVLoanSource} from "./structs/REVLoanSource.sol";
|
|
30
|
+
|
|
31
|
+
/// @notice The runtime hook for all revnets — set as every revnet's `dataHook` in ruleset metadata. At pay time, it
|
|
32
|
+
/// coordinates the 721 hook (NFT tier minting) with the buyback hook (secondary market swap routing) and scales weight
|
|
33
|
+
/// for split deductions. At cash-out time, it aggregates cross-chain total supply and surplus (including outstanding
|
|
34
|
+
/// loan debt and collateral), grants suckers 0% tax, splits a 2.5% fee from non-sucker cash outs, and routes fee
|
|
35
|
+
/// proceeds to the fee revnet via `afterCashOutRecordedWith`.
|
|
36
|
+
/// @dev Separated from `REVDeployer` to stay within the EIP-170 contract size limit. Also implements
|
|
37
|
+
/// `IJBPeerChainAdjustedAccounts` to expose loan state to peer-chain supply/surplus snapshots.
|
|
38
|
+
contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAccounts {
|
|
30
39
|
// A library that adds default safety checks to ERC20 functionality.
|
|
31
40
|
using SafeERC20 for IERC20;
|
|
32
41
|
|
|
@@ -61,10 +70,10 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
61
70
|
uint256 public immutable FEE_REVNET_ID;
|
|
62
71
|
|
|
63
72
|
/// @notice The hidden tokens contract used by all revnets.
|
|
64
|
-
|
|
73
|
+
IREVHiddenTokens public immutable HIDDEN_TOKENS;
|
|
65
74
|
|
|
66
75
|
/// @notice The loan contract used by all revnets.
|
|
67
|
-
|
|
76
|
+
IREVLoans public immutable LOANS;
|
|
68
77
|
|
|
69
78
|
/// @notice Deploys and tracks suckers for revnets.
|
|
70
79
|
IJBSuckerRegistry public immutable SUCKER_REGISTRY;
|
|
@@ -102,23 +111,21 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
102
111
|
/// @param directory The directory of terminals and controllers.
|
|
103
112
|
/// @param feeRevnetId The Juicebox project ID of the fee revnet.
|
|
104
113
|
/// @param suckerRegistry The sucker registry.
|
|
105
|
-
/// @param loans The loan contract
|
|
106
|
-
/// @param hiddenTokens The hidden tokens contract
|
|
114
|
+
/// @param loans The loan contract.
|
|
115
|
+
/// @param hiddenTokens The hidden tokens contract.
|
|
107
116
|
constructor(
|
|
108
117
|
IJBBuybackHookRegistry buybackHook,
|
|
109
118
|
IJBDirectory directory,
|
|
110
119
|
uint256 feeRevnetId,
|
|
111
120
|
IJBSuckerRegistry suckerRegistry,
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
IREVLoans loans,
|
|
122
|
+
IREVHiddenTokens hiddenTokens
|
|
114
123
|
) {
|
|
115
124
|
BUYBACK_HOOK = buybackHook;
|
|
116
125
|
DIRECTORY = directory;
|
|
117
126
|
FEE_REVNET_ID = feeRevnetId;
|
|
118
127
|
SUCKER_REGISTRY = suckerRegistry;
|
|
119
|
-
// slither-disable-next-line missing-zero-check
|
|
120
128
|
LOANS = loans;
|
|
121
|
-
// slither-disable-next-line missing-zero-check
|
|
122
129
|
HIDDEN_TOKENS = hiddenTokens;
|
|
123
130
|
_DEPLOYER_BINDER = msg.sender;
|
|
124
131
|
}
|
|
@@ -127,13 +134,14 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
127
134
|
// ------------------------- external views -------------------------- //
|
|
128
135
|
//*********************************************************************//
|
|
129
136
|
|
|
130
|
-
/// @notice
|
|
131
|
-
///
|
|
132
|
-
///
|
|
133
|
-
///
|
|
134
|
-
///
|
|
135
|
-
/// protocol
|
|
136
|
-
///
|
|
137
|
+
/// @notice Called before a cash out is recorded. Suckers get 0% tax (bridged tokens redeem at face value). For
|
|
138
|
+
/// regular holders, aggregates cross-chain total supply and surplus (including outstanding loan debt/collateral),
|
|
139
|
+
/// splits a 2.5% fee from the cashed-out token count, computes bonding curve reclaims for both the holder's portion
|
|
140
|
+
/// and the fee portion, then delegates to the buyback hook for potential swap routing.
|
|
141
|
+
/// @dev Part of `IJBRulesetDataHook`. REVOwner is intentionally not registered as a feeless address — the
|
|
142
|
+
/// protocol
|
|
143
|
+
/// fee (2.5%) applies on top of the rev fee. The fee hook spec amount sent to `afterCashOutRecordedWith` will have
|
|
144
|
+
/// the protocol fee deducted by the terminal before reaching this contract.
|
|
137
145
|
/// @param context Standard Juicebox cash out context. See `JBBeforeCashOutRecordedContext`.
|
|
138
146
|
/// @return cashOutTaxRate The cash out tax rate, which influences the amount of terminal tokens which get cashed
|
|
139
147
|
/// out.
|
|
@@ -153,11 +161,23 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
153
161
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
154
162
|
)
|
|
155
163
|
{
|
|
164
|
+
// Treat outstanding local loans as temporarily off-terminal revnet assets. Borrowed funds are owed back to
|
|
165
|
+
// the revnet, while burned loan collateral can be re-minted on repayment, so both affect fair cash-out math.
|
|
166
|
+
(uint256 totalBorrowed, uint256 totalCollateral) = _localLoanStateOf({
|
|
167
|
+
revnetId: context.projectId, decimals: context.surplus.decimals, currency: context.surplus.currency
|
|
168
|
+
});
|
|
169
|
+
|
|
156
170
|
// If the cash out is from a sucker, return the full cash out amount without taxes or fees.
|
|
157
171
|
// This relies on the sucker registry to only contain trusted sucker contracts deployed via
|
|
158
172
|
// the registry's own deploySuckersFor flow — external addresses cannot register as suckers.
|
|
159
173
|
if (_isSuckerOf({revnetId: context.projectId, addr: context.holder})) {
|
|
160
|
-
return (
|
|
174
|
+
return (
|
|
175
|
+
0,
|
|
176
|
+
context.cashOutCount,
|
|
177
|
+
context.totalSupply + totalCollateral,
|
|
178
|
+
context.surplus.value + totalBorrowed,
|
|
179
|
+
hookSpecifications
|
|
180
|
+
);
|
|
161
181
|
}
|
|
162
182
|
|
|
163
183
|
// Keep a reference to the cash out delay of the revnet.
|
|
@@ -173,8 +193,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
173
193
|
|
|
174
194
|
// Compute the cross-chain total supply (local + remote peer chain supplies) for cross-chain-aware bonding
|
|
175
195
|
// curve.
|
|
176
|
-
totalSupply = context.totalSupply + SUCKER_REGISTRY.remoteTotalSupplyOf(context.projectId);
|
|
177
|
-
effectiveSurplusValue = context.surplus.value
|
|
196
|
+
totalSupply = context.totalSupply + totalCollateral + SUCKER_REGISTRY.remoteTotalSupplyOf(context.projectId);
|
|
197
|
+
effectiveSurplusValue = context.surplus.value + totalBorrowed
|
|
178
198
|
+ SUCKER_REGISTRY.remoteSurplusOf({
|
|
179
199
|
projectId: context.projectId,
|
|
180
200
|
decimals: context.surplus.decimals,
|
|
@@ -274,8 +294,11 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
274
294
|
return (cashOutTaxRate, cashOutCount, totalSupply, effectiveSurplusValue, hookSpecifications);
|
|
275
295
|
}
|
|
276
296
|
|
|
277
|
-
/// @notice
|
|
278
|
-
///
|
|
297
|
+
/// @notice Called before a payment is recorded. Coordinates the 721 hook (NFT tier minting with split deductions)
|
|
298
|
+
/// with the buyback hook (which may route funds through a Uniswap pool for a better token price). Merges their hook
|
|
299
|
+
/// specifications and scales the minting weight so payers only receive tokens proportional to funds entering the
|
|
300
|
+
/// project (not the split portion).
|
|
301
|
+
/// @dev Part of `IJBRulesetDataHook`. The 721 hook spec comes first in the returned array.
|
|
279
302
|
/// @param context Standard Juicebox payment context. See `JBBeforePayRecordedContext`.
|
|
280
303
|
/// @return weight The weight which revnet tokens are minted relative to. This can be used to customize how many
|
|
281
304
|
/// tokens get minted by a payment.
|
|
@@ -332,8 +355,11 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
332
355
|
if (usesBuybackHook) hookSpecifications[usesTiered721Hook ? 1 : 0] = buybackHookSpecs[0];
|
|
333
356
|
}
|
|
334
357
|
|
|
335
|
-
/// @notice
|
|
336
|
-
///
|
|
358
|
+
/// @notice Returns whether an address may mint a revnet's tokens on-demand. Grants permission to: the loans
|
|
359
|
+
/// contract (re-mints collateral on repayment), hidden tokens contract (re-mints on reveal), buyback hook and its
|
|
360
|
+
/// delegates
|
|
361
|
+
/// (mints tokens from pool swaps), and suckers (mints bridged tokens on the destination chain).
|
|
362
|
+
/// @dev Part of `IJBRulesetDataHook`.
|
|
337
363
|
/// @param revnetId The ID of the revnet to check permissions for.
|
|
338
364
|
/// @param ruleset The ruleset to check the mint permission for.
|
|
339
365
|
/// @param addr The address to check the mint permission of.
|
|
@@ -350,11 +376,35 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
350
376
|
{
|
|
351
377
|
// The loans contract, hidden tokens contract, buyback hook (and its delegates), and suckers are allowed to mint
|
|
352
378
|
// the revnet's tokens.
|
|
353
|
-
return addr == LOANS || addr == HIDDEN_TOKENS || addr == address(BUYBACK_HOOK)
|
|
379
|
+
return addr == address(LOANS) || addr == address(HIDDEN_TOKENS) || addr == address(BUYBACK_HOOK)
|
|
354
380
|
|| BUYBACK_HOOK.hasMintPermissionFor({projectId: revnetId, ruleset: ruleset, addr: addr})
|
|
355
381
|
|| _isSuckerOf({revnetId: revnetId, addr: addr});
|
|
356
382
|
}
|
|
357
383
|
|
|
384
|
+
/// @notice Additional revnet accounts that peer-chain snapshots should include.
|
|
385
|
+
/// @dev Hidden tokens are intentionally excluded. Revnet operators can hide tokens as a security handle without
|
|
386
|
+
/// changing loan or cash-out math for other holders. Outstanding loan debt is counted as both surplus and balance:
|
|
387
|
+
/// it is value owed back to this chain's revnet and should travel to peer snapshots with the collateral supply.
|
|
388
|
+
/// @param revnetId The ID of the revnet being snapshotted.
|
|
389
|
+
/// @param decimals The decimals the returned surplus should use.
|
|
390
|
+
/// @param currency The currency the returned surplus should be in terms of.
|
|
391
|
+
/// @return supply The loan-collateral supply to include in the peer snapshot.
|
|
392
|
+
/// @return surplus The outstanding loan debt to include in `sourceSurplus`.
|
|
393
|
+
/// @return balance The outstanding loan debt to include in `sourceBalance`.
|
|
394
|
+
function peerChainAdjustedAccountsOf(
|
|
395
|
+
uint256 revnetId,
|
|
396
|
+
uint256 decimals,
|
|
397
|
+
uint256 currency
|
|
398
|
+
)
|
|
399
|
+
external
|
|
400
|
+
view
|
|
401
|
+
override
|
|
402
|
+
returns (uint256 supply, uint256 surplus, uint256 balance)
|
|
403
|
+
{
|
|
404
|
+
(surplus, supply) = _localLoanStateOf({revnetId: revnetId, decimals: decimals, currency: currency});
|
|
405
|
+
balance = surplus;
|
|
406
|
+
}
|
|
407
|
+
|
|
358
408
|
//*********************************************************************//
|
|
359
409
|
// --------------------- external transactions ----------------------- //
|
|
360
410
|
//*********************************************************************//
|
|
@@ -460,7 +510,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
460
510
|
/// @return A flag indicating if the provided interface ID is supported.
|
|
461
511
|
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
|
462
512
|
return interfaceId == type(IERC165).interfaceId || interfaceId == type(IJBRulesetDataHook).interfaceId
|
|
463
|
-
|| interfaceId == type(IJBCashOutHook).interfaceId
|
|
513
|
+
|| interfaceId == type(IJBCashOutHook).interfaceId
|
|
514
|
+
|| interfaceId == type(IJBPeerChainAdjustedAccounts).interfaceId;
|
|
464
515
|
}
|
|
465
516
|
|
|
466
517
|
//*********************************************************************//
|
|
@@ -475,6 +526,73 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
475
526
|
return SUCKER_REGISTRY.isSuckerOf({projectId: revnetId, addr: addr});
|
|
476
527
|
}
|
|
477
528
|
|
|
529
|
+
/// @notice Total outstanding local loan debt and collateral for a revnet.
|
|
530
|
+
/// @dev This is included in cash-out and peer-snapshot math because borrowed funds are still owed to the revnet
|
|
531
|
+
/// and collateral can re-enter supply when the loan is repaid.
|
|
532
|
+
/// @param revnetId The ID of the revnet to check.
|
|
533
|
+
/// @param decimals The decimals the resulting fixed point debt value should use.
|
|
534
|
+
/// @param currency The currency the resulting debt value should be in terms of.
|
|
535
|
+
/// @return borrowedAmount The local outstanding loan debt converted into `currency`.
|
|
536
|
+
/// @return collateralCount The local burned loan collateral count.
|
|
537
|
+
function _localLoanStateOf(
|
|
538
|
+
uint256 revnetId,
|
|
539
|
+
uint256 decimals,
|
|
540
|
+
uint256 currency
|
|
541
|
+
)
|
|
542
|
+
internal
|
|
543
|
+
view
|
|
544
|
+
returns (uint256 borrowedAmount, uint256 collateralCount)
|
|
545
|
+
{
|
|
546
|
+
IREVLoans loans = LOANS;
|
|
547
|
+
if (address(loans) == address(0) || address(loans).code.length == 0) return (0, 0);
|
|
548
|
+
|
|
549
|
+
collateralCount = loans.totalCollateralOf(revnetId);
|
|
550
|
+
|
|
551
|
+
REVLoanSource[] memory sources = loans.loanSourcesOf(revnetId);
|
|
552
|
+
// Loan sources are project configuration, and this read-only aggregation needs the latest terminal/pricing
|
|
553
|
+
// state for each configured source.
|
|
554
|
+
for (uint256 i; i < sources.length; i++) {
|
|
555
|
+
REVLoanSource memory source = sources[i];
|
|
556
|
+
// Each configured source must be queried live so cash-out math includes current outstanding debt.
|
|
557
|
+
// slither-disable-next-line calls-loop
|
|
558
|
+
uint256 tokensLoaned =
|
|
559
|
+
loans.totalBorrowedFrom({revnetId: revnetId, terminal: source.terminal, token: source.token});
|
|
560
|
+
if (tokensLoaned == 0) continue;
|
|
561
|
+
|
|
562
|
+
// Read the source token's accounting context so debt can be normalized before cross-currency conversion.
|
|
563
|
+
// slither-disable-next-line calls-loop
|
|
564
|
+
JBAccountingContext memory accountingContext =
|
|
565
|
+
source.terminal.accountingContextForTokenOf({projectId: revnetId, token: source.token});
|
|
566
|
+
|
|
567
|
+
// Normalize each source from its native token decimals into the caller's requested decimals.
|
|
568
|
+
uint256 normalizedTokens;
|
|
569
|
+
if (accountingContext.decimals > decimals) {
|
|
570
|
+
normalizedTokens = tokensLoaned / (10 ** (accountingContext.decimals - decimals));
|
|
571
|
+
} else if (accountingContext.decimals < decimals) {
|
|
572
|
+
normalizedTokens = tokensLoaned * (10 ** (decimals - accountingContext.decimals));
|
|
573
|
+
} else {
|
|
574
|
+
normalizedTokens = tokensLoaned;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (accountingContext.currency == currency) {
|
|
578
|
+
borrowedAmount += normalizedTokens;
|
|
579
|
+
} else {
|
|
580
|
+
// Convert source-token debt into the requested currency using the loans contract's shared prices.
|
|
581
|
+
// slither-disable-next-line calls-loop
|
|
582
|
+
uint256 pricePerUnit = loans.PRICES()
|
|
583
|
+
.pricePerUnitOf({
|
|
584
|
+
projectId: revnetId,
|
|
585
|
+
pricingCurrency: accountingContext.currency,
|
|
586
|
+
unitCurrency: currency,
|
|
587
|
+
decimals: decimals
|
|
588
|
+
});
|
|
589
|
+
if (pricePerUnit == 0) continue;
|
|
590
|
+
|
|
591
|
+
borrowedAmount += mulDiv({x: normalizedTokens, y: 10 ** decimals, denominator: pricePerUnit});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
478
596
|
//*********************************************************************//
|
|
479
597
|
// --------------------- internal transactions ----------------------- //
|
|
480
598
|
//*********************************************************************//
|
|
@@ -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 (
|
|
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.
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
/// @
|
|
5
|
-
///
|
|
4
|
+
/// @notice A per-stage token mint that happens without any payment — think of it as a scheduled premint. Each auto
|
|
5
|
+
/// issuance specifies a chain, beneficiary, and token count. Can be claimed once per stage via `autoIssueFor`.
|
|
6
|
+
/// @custom:member chainId The chain ID where this auto-issuance should be honored (only mints on the matching chain).
|
|
7
|
+
/// @custom:member count The number of tokens to mint for the beneficiary.
|
|
6
8
|
/// @custom:member beneficiary The address that will receive the minted tokens.
|
|
7
9
|
struct REVAutoIssuance {
|
|
8
10
|
uint32 chainId;
|
|
@@ -4,11 +4,14 @@ pragma solidity ^0.8.0;
|
|
|
4
4
|
import {REVDescription} from "./REVDescription.sol";
|
|
5
5
|
import {REVStageConfig} from "./REVStageConfig.sol";
|
|
6
6
|
|
|
7
|
-
/// @
|
|
8
|
-
///
|
|
9
|
-
///
|
|
10
|
-
///
|
|
11
|
-
/// @custom:member
|
|
7
|
+
/// @notice Top-level configuration for deploying a revnet. Defines the revnet's identity, base currency for issuance
|
|
8
|
+
/// pricing, the split operator (who receives production splits and can reassign that role), and the ordered list of
|
|
9
|
+
/// stages that govern the revnet's lifecycle.
|
|
10
|
+
/// @custom:member description The revnet's name, ticker, metadata URI, and deployment salt.
|
|
11
|
+
/// @custom:member baseCurrency The currency that issuance pricing is denominated in (e.g. ETH or USD).
|
|
12
|
+
/// @custom:member splitOperator The address that receives production splits and can reassign the operator role.
|
|
13
|
+
/// Only the current operator can replace itself after deployment.
|
|
14
|
+
/// @custom:member stageConfigurations The ordered stages that define how the revnet's tokenomics evolve over time.
|
|
12
15
|
struct REVConfig {
|
|
13
16
|
REVDescription description;
|
|
14
17
|
uint32 baseCurrency;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity ^0.8.0;
|
|
3
3
|
|
|
4
|
-
/// @
|
|
5
|
-
/// @custom:member
|
|
6
|
-
/// @custom:member
|
|
7
|
-
/// @custom:member
|
|
8
|
-
/// address
|
|
4
|
+
/// @notice Identity and metadata for a revnet deployment.
|
|
5
|
+
/// @custom:member name The name of the ERC-20 token created for the revnet.
|
|
6
|
+
/// @custom:member ticker The ticker symbol of the ERC-20 token created for the revnet.
|
|
7
|
+
/// @custom:member uri The metadata URI containing the revnet's off-chain info (logo, description, links).
|
|
8
|
+
/// @custom:member salt A deployment salt — revnets deployed across chains by the same address with the same salt get
|
|
9
|
+
/// deterministic matching addresses.
|
|
9
10
|
struct REVDescription {
|
|
10
11
|
string name;
|
|
11
12
|
string ticker;
|
package/src/structs/REVLoan.sol
CHANGED
|
@@ -3,12 +3,15 @@ pragma solidity ^0.8.0;
|
|
|
3
3
|
|
|
4
4
|
import {REVLoanSource} from "./REVLoanSource.sol";
|
|
5
5
|
|
|
6
|
-
/// @
|
|
7
|
-
///
|
|
6
|
+
/// @notice An active loan against a revnet. The borrower locked collateral tokens (which were burned) and received
|
|
7
|
+
/// funds from the revnet's terminal. The loan can be repaid within the prepaid duration at no extra cost; after that,
|
|
8
|
+
/// repayment cost increases linearly until liquidation at 10 years.
|
|
9
|
+
/// @custom:member amount The amount borrowed (includes fees taken at creation).
|
|
10
|
+
/// @custom:member collateral The number of revnet tokens burned as collateral.
|
|
8
11
|
/// @custom:member createdAt The timestamp when the loan was created.
|
|
9
|
-
/// @custom:member prepaidFeePercent The percentage of
|
|
10
|
-
/// @custom:member prepaidDuration The duration
|
|
11
|
-
/// @custom:member source The
|
|
12
|
+
/// @custom:member prepaidFeePercent The percentage of fees prepaid at creation (determines prepaid duration).
|
|
13
|
+
/// @custom:member prepaidDuration The duration (seconds) during which repayment costs nothing beyond the original
|
|
14
|
+
/// amount. @custom:member source The terminal and token from which funds were drawn.
|
|
12
15
|
struct REVLoan {
|
|
13
16
|
uint112 amount;
|
|
14
17
|
uint112 collateral;
|
|
@@ -5,22 +5,20 @@ import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
|
5
5
|
|
|
6
6
|
import {REVAutoIssuance} from "./REVAutoIssuance.sol";
|
|
7
7
|
|
|
8
|
-
/// @
|
|
9
|
-
///
|
|
10
|
-
///
|
|
11
|
-
///
|
|
12
|
-
/// @custom:member
|
|
13
|
-
/// @custom:member
|
|
14
|
-
///
|
|
15
|
-
///
|
|
16
|
-
/// @custom:member
|
|
17
|
-
///
|
|
18
|
-
/// @custom:member issuanceCutPercent
|
|
19
|
-
/// of
|
|
20
|
-
///
|
|
21
|
-
///
|
|
22
|
-
/// out.
|
|
23
|
-
/// @custom:member extraMetadata Extra info to attach set into this stage that may affect hooks.
|
|
8
|
+
/// @notice A stage in a revnet's lifecycle. Each stage defines the token issuance rate, how quickly it decays, what
|
|
9
|
+
/// percentage goes to splits, and the cash-out tax rate. Stages are processed in order — each one activates at or
|
|
10
|
+
/// after
|
|
11
|
+
/// its `startsAtOrAfter` timestamp.
|
|
12
|
+
/// @custom:member startsAtOrAfter The earliest timestamp this stage can begin. Must be strictly increasing across
|
|
13
|
+
/// stages. @custom:member autoIssuances Tokens to mint without payment during this stage (per-chain, per-beneficiary).
|
|
14
|
+
/// @custom:member splitPercent The percentage of newly issued tokens routed to splits, out of 10,000.
|
|
15
|
+
/// @custom:member splits The split recipients for this stage's production allocation.
|
|
16
|
+
/// @custom:member initialIssuance Tokens per unit of base currency at stage start (18-decimal fixed point).
|
|
17
|
+
/// @custom:member issuanceCutFrequency Seconds between each issuance reduction. Should be >= 24 hours.
|
|
18
|
+
/// @custom:member issuanceCutPercent How much issuance decreases each period, out of 1,000,000,000. 0 = no decay.
|
|
19
|
+
/// @custom:member cashOutTaxRate The tax on cash outs, out of 10,000. 0 = no tax (full reclaim). Higher = more tax
|
|
20
|
+
/// retained by the treasury.
|
|
21
|
+
/// @custom:member extraMetadata Additional metadata bits passed to hooks for stage-specific behavior.
|
|
24
22
|
struct REVStageConfig {
|
|
25
23
|
uint48 startsAtOrAfter;
|
|
26
24
|
REVAutoIssuance[] autoIssuances;
|
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.
|