@rev-net/core-v6 0.0.30 → 0.0.31
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/USER_JOURNEYS.md +11 -0
- package/package.json +8 -8
- package/script/Deploy.s.sol +4 -1
- package/src/REVDeployer.sol +4 -2
- package/src/REVLoans.sol +81 -59
- package/src/REVOwner.sol +40 -11
- package/src/interfaces/IREVLoans.sol +5 -0
- package/test/REV.integrations.t.sol +10 -1
- package/test/REVAutoIssuanceFuzz.t.sol +10 -1
- package/test/REVDeployerRegressions.t.sol +12 -1
- package/test/REVInvincibility.t.sol +21 -2
- package/test/REVLifecycle.t.sol +12 -1
- package/test/REVLoans.invariants.t.sol +12 -1
- package/test/REVLoansAttacks.t.sol +12 -1
- package/test/REVLoansFeeRecovery.t.sol +12 -1
- package/test/REVLoansFindings.t.sol +12 -1
- package/test/REVLoansRegressions.t.sol +12 -1
- package/test/REVLoansSourceFeeRecovery.t.sol +12 -1
- package/test/REVLoansSourced.t.sol +12 -1
- package/test/REVLoansUnSourced.t.sol +12 -1
- package/test/TestBurnHeldTokens.t.sol +12 -1
- package/test/TestCEIPattern.t.sol +12 -1
- package/test/TestCashOutCallerValidation.t.sol +13 -2
- package/test/TestConversionDocumentation.t.sol +12 -1
- package/test/TestCrossCurrencyReclaim.t.sol +12 -1
- package/test/TestCrossSourceReallocation.t.sol +12 -1
- package/test/TestERC2771MetaTx.t.sol +12 -1
- package/test/TestEmptyBuybackSpecs.t.sol +12 -1
- package/test/TestFlashLoanSurplus.t.sol +12 -1
- package/test/TestHiddenTokens.t.sol +12 -1
- package/test/TestHookArrayOOB.t.sol +12 -1
- package/test/TestLiquidationBehavior.t.sol +12 -1
- package/test/TestLoanSourceRotation.t.sol +12 -1
- package/test/TestLoansCashOutDelay.t.sol +12 -1
- package/test/TestLongTailEconomics.t.sol +12 -1
- package/test/TestLowFindings.t.sol +12 -1
- package/test/TestMixedFixes.t.sol +12 -1
- package/test/TestPermit2Signatures.t.sol +12 -1
- package/test/TestReallocationSandwich.t.sol +12 -1
- package/test/TestRevnetRegressions.t.sol +14 -2
- package/test/TestSplitWeightAdjustment.t.sol +12 -1
- package/test/TestSplitWeightE2E.t.sol +14 -1
- package/test/TestSplitWeightFork.t.sol +14 -1
- package/test/TestStageTransitionBorrowable.t.sol +12 -1
- package/test/TestSwapTerminalPermission.t.sol +12 -1
- package/test/TestUint112Overflow.t.sol +12 -1
- package/test/TestZeroAmountLoanGuard.t.sol +12 -1
- package/test/TestZeroRepayment.t.sol +12 -1
- package/test/audit/CodexPhantomSurplusTerminal.t.sol +367 -0
- package/test/audit/LoanIdOverflowGuard.t.sol +12 -1
- package/test/audit/NemesisOperatorDelegation.t.sol +12 -1
- package/test/fork/ForkTestBase.sol +14 -1
- package/test/mock/MockBuybackCashOutRecorder.sol +2 -0
- package/test/mock/MockBuybackDataHook.sol +3 -1
- package/test/mock/MockBuybackDataHookMintPath.sol +2 -0
- package/test/mock/MockSuckerRegistry.sol +17 -0
- package/test/regression/TestBurnPermissionRequired.t.sol +12 -1
- package/test/regression/TestCashOutBuybackFeeLeak.t.sol +14 -1
- package/test/regression/TestCrossRevnetLiquidation.t.sol +12 -1
- package/test/regression/TestCumulativeLoanCounter.t.sol +12 -1
- package/test/regression/TestLiquidateGapHandling.t.sol +12 -1
- package/test/regression/TestZeroPriceFeed.t.sol +12 -1
package/USER_JOURNEYS.md
CHANGED
|
@@ -93,6 +93,17 @@
|
|
|
93
93
|
2. Use only those surfaces rather than treating the project like a normal owner-governed Juicebox project.
|
|
94
94
|
3. Audit cross-package behavior whenever the Revnet enabled buybacks, 721 hooks, router terminals, or suckers.
|
|
95
95
|
|
|
96
|
+
## Journey 7: Receive Cross-Chain Payments With Correct Hook Routing
|
|
97
|
+
|
|
98
|
+
**Starting state:** a sucker pays the Revnet on behalf of a remote user via `payRemote`, and the hooks attached via `REVOwner.beforePayRecordedWith` need to see the real user.
|
|
99
|
+
|
|
100
|
+
**Success:** the 721 hook and buyback hook see the real remote user as the beneficiary so NFTs mint to and buyback routing benefits the correct person.
|
|
101
|
+
|
|
102
|
+
**Flow**
|
|
103
|
+
1. The sucker calls `terminal.pay()` with relay-beneficiary metadata.
|
|
104
|
+
2. `REVOwner.beforePayRecordedWith()` resolves the relay beneficiary from the metadata when the payer is a registered sucker.
|
|
105
|
+
3. The swapped beneficiary is forwarded to both the 721 hook and the buyback hook.
|
|
106
|
+
|
|
96
107
|
## Hand-Offs
|
|
97
108
|
|
|
98
109
|
- Use [nana-core-v6](../nana-core-v6/USER_JOURNEYS.md) for the underlying project, terminal, and ruleset mechanics that Revnets package and constrain.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rev-net/core-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.31",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'revnet-core-v6'"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
23
|
-
"@bananapus/buyback-hook-v6": "^0.0.
|
|
24
|
-
"@bananapus/core-v6": "^0.0.
|
|
22
|
+
"@bananapus/721-hook-v6": "^0.0.33",
|
|
23
|
+
"@bananapus/buyback-hook-v6": "^0.0.27",
|
|
24
|
+
"@bananapus/core-v6": "^0.0.34",
|
|
25
25
|
"@bananapus/ownable-v6": "^0.0.17",
|
|
26
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
26
|
+
"@bananapus/permission-ids-v6": "^0.0.17",
|
|
27
27
|
"@bananapus/router-terminal-v6": "^0.0.26",
|
|
28
|
-
"@bananapus/suckers-v6": "^0.0.
|
|
29
|
-
"@croptop/core-v6": "
|
|
28
|
+
"@bananapus/suckers-v6": "^0.0.25",
|
|
29
|
+
"@croptop/core-v6": "github:mejango/croptop-core-v6",
|
|
30
30
|
"@openzeppelin/contracts": "^5.6.1",
|
|
31
31
|
"@uniswap/v4-core": "^1.0.2",
|
|
32
32
|
"@uniswap/v4-periphery": "^1.0.3"
|
|
@@ -35,4 +35,4 @@
|
|
|
35
35
|
"@bananapus/address-registry-v6": "^0.0.17",
|
|
36
36
|
"@sphinx-labs/plugins": "^0.33.2"
|
|
37
37
|
}
|
|
38
|
-
}
|
|
38
|
+
}
|
package/script/Deploy.s.sol
CHANGED
|
@@ -389,7 +389,9 @@ contract DeployScript is Script, Sphinx {
|
|
|
389
389
|
(address _candidateRevloansAddr, bool _candidateRevloansDeployed) = _isDeployed({
|
|
390
390
|
salt: REVLOANS_SALT,
|
|
391
391
|
creationCode: type(REVLoans).creationCode,
|
|
392
|
-
arguments: abi.encode(
|
|
392
|
+
arguments: abi.encode(
|
|
393
|
+
core.controller, suckers.registry, _candidateId, LOANS_OWNER, PERMIT2, TRUSTED_FORWARDER
|
|
394
|
+
)
|
|
393
395
|
});
|
|
394
396
|
|
|
395
397
|
if (_candidateRevloansDeployed) {
|
|
@@ -469,6 +471,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
469
471
|
? REVLoans(payable(_existingRevloansAddr))
|
|
470
472
|
: new REVLoans{salt: REVLOANS_SALT}({
|
|
471
473
|
controller: core.controller,
|
|
474
|
+
suckerRegistry: suckers.registry,
|
|
472
475
|
revId: FEE_PROJECT_ID,
|
|
473
476
|
owner: LOANS_OWNER,
|
|
474
477
|
permit2: PERMIT2,
|
package/src/REVDeployer.sol
CHANGED
|
@@ -375,7 +375,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
375
375
|
uint256[] memory customSplitOperatorPermissionIndexes = _extraOperatorPermissions[revnetId];
|
|
376
376
|
|
|
377
377
|
// Make the array that merges the default and custom operator permissions.
|
|
378
|
-
allOperatorPermissions = new uint256[](
|
|
378
|
+
allOperatorPermissions = new uint256[](10 + customSplitOperatorPermissionIndexes.length);
|
|
379
379
|
allOperatorPermissions[0] = JBPermissionIds.SET_SPLIT_GROUPS;
|
|
380
380
|
allOperatorPermissions[1] = JBPermissionIds.SET_BUYBACK_POOL;
|
|
381
381
|
allOperatorPermissions[2] = JBPermissionIds.SET_BUYBACK_TWAP;
|
|
@@ -385,10 +385,11 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
385
385
|
allOperatorPermissions[6] = JBPermissionIds.SET_BUYBACK_HOOK;
|
|
386
386
|
allOperatorPermissions[7] = JBPermissionIds.SET_ROUTER_TERMINAL;
|
|
387
387
|
allOperatorPermissions[8] = JBPermissionIds.SET_TOKEN_METADATA;
|
|
388
|
+
allOperatorPermissions[9] = JBPermissionIds.SIGN_FOR_ERC20;
|
|
388
389
|
|
|
389
390
|
// Copy the custom permissions into the array.
|
|
390
391
|
for (uint256 i; i < customSplitOperatorPermissionIndexes.length;) {
|
|
391
|
-
allOperatorPermissions[
|
|
392
|
+
allOperatorPermissions[10 + i] = customSplitOperatorPermissionIndexes[i];
|
|
392
393
|
unchecked {
|
|
393
394
|
++i;
|
|
394
395
|
}
|
|
@@ -833,6 +834,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
833
834
|
// Store the cash out delay of the revnet if its stages are already in progress.
|
|
834
835
|
// This prevents cash out liquidity/arbitrage issues for existing revnets which
|
|
835
836
|
// are deploying to a new chain.
|
|
837
|
+
// slither-disable-next-line reentrancy-events
|
|
836
838
|
_setCashOutDelayIfNeeded({revnetId: revnetId, firstStageConfig: configuration.stageConfigurations[0]});
|
|
837
839
|
|
|
838
840
|
// Deploy the revnet's ERC-20 token.
|
package/src/REVLoans.sol
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.28;
|
|
3
3
|
|
|
4
|
+
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
4
5
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
5
6
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
6
7
|
import {IJBPayoutTerminal} from "@bananapus/core-v6/src/interfaces/IJBPayoutTerminal.sol";
|
|
7
8
|
import {IJBPermissioned} from "@bananapus/core-v6/src/interfaces/IJBPermissioned.sol";
|
|
8
|
-
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
9
9
|
import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.sol";
|
|
10
10
|
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
11
11
|
import {IJBTokenUriResolver} from "@bananapus/core-v6/src/interfaces/IJBTokenUriResolver.sol";
|
|
12
|
-
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
13
12
|
import {JBCashOuts} from "@bananapus/core-v6/src/libraries/JBCashOuts.sol";
|
|
14
13
|
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
15
14
|
import {JBFees} from "@bananapus/core-v6/src/libraries/JBFees.sol";
|
|
@@ -18,6 +17,8 @@ import {JBSurplus} from "@bananapus/core-v6/src/libraries/JBSurplus.sol";
|
|
|
18
17
|
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
19
18
|
import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
20
19
|
import {JBSingleAllowance} from "@bananapus/core-v6/src/structs/JBSingleAllowance.sol";
|
|
20
|
+
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
21
|
+
import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
|
|
21
22
|
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
22
23
|
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
|
|
23
24
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
@@ -124,6 +125,9 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
|
|
|
124
125
|
/// @notice The ID of the REV revnet that will receive the fees.
|
|
125
126
|
uint256 public immutable override REV_ID;
|
|
126
127
|
|
|
128
|
+
/// @notice The sucker registry used to discover peer chain suckers for cross-chain awareness.
|
|
129
|
+
IJBSuckerRegistry public immutable override SUCKER_REGISTRY;
|
|
130
|
+
|
|
127
131
|
//*********************************************************************//
|
|
128
132
|
// --------------------- public stored properties -------------------- //
|
|
129
133
|
//*********************************************************************//
|
|
@@ -180,12 +184,14 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
|
|
|
180
184
|
//*********************************************************************//
|
|
181
185
|
|
|
182
186
|
/// @param controller The controller that manages revnets using this loans contract.
|
|
187
|
+
/// @param suckerRegistry The registry used to discover peer chain suckers for cross-chain supply/surplus awareness.
|
|
183
188
|
/// @param revId The ID of the REV revnet that will receive the fees.
|
|
184
189
|
/// @param owner The owner of the contract that can set the URI resolver.
|
|
185
190
|
/// @param permit2 A permit2 utility.
|
|
186
191
|
/// @param trustedForwarder A trusted forwarder of transactions to this contract.
|
|
187
192
|
constructor(
|
|
188
193
|
IJBController controller,
|
|
194
|
+
IJBSuckerRegistry suckerRegistry,
|
|
189
195
|
uint256 revId,
|
|
190
196
|
address owner,
|
|
191
197
|
IPermit2 permit2,
|
|
@@ -201,6 +207,7 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
|
|
|
201
207
|
PRICES = controller.PRICES();
|
|
202
208
|
REV_ID = revId;
|
|
203
209
|
PERMIT2 = permit2;
|
|
210
|
+
SUCKER_REGISTRY = suckerRegistry;
|
|
204
211
|
}
|
|
205
212
|
|
|
206
213
|
//*********************************************************************//
|
|
@@ -355,17 +362,29 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
|
|
|
355
362
|
// Get a refeerence to the collateral being used to secure loans.
|
|
356
363
|
uint256 totalCollateral = totalCollateralOf[revnetId];
|
|
357
364
|
|
|
365
|
+
// The local supply includes both circulating tokens and tokens locked as loan collateral.
|
|
366
|
+
uint256 localSupply = totalSupply + totalCollateral;
|
|
367
|
+
|
|
368
|
+
// The local surplus includes both the treasury surplus and the outstanding borrowed amounts.
|
|
369
|
+
uint256 localSurplus = totalSurplus + totalBorrowed;
|
|
370
|
+
|
|
358
371
|
// Proportional — uses the CURRENT stage's cashOutTaxRate.
|
|
359
372
|
// NOTE: When a revnet transitions between stages with different cashOutTaxRate values, the borrowable amount
|
|
360
373
|
// for the same collateral changes. A lower cashOutTaxRate in a later stage means more borrowable value per
|
|
361
374
|
// collateral. This is by design: loan value tracks the current bonding curve parameters, just as cash-out
|
|
362
375
|
// value does. Borrowers benefit from decreasing tax rates and are constrained by increasing ones.
|
|
363
|
-
|
|
364
|
-
|
|
376
|
+
// Add cross-chain remote values for proportional reclaim.
|
|
377
|
+
uint256 omnichainSurplus =
|
|
378
|
+
localSurplus + SUCKER_REGISTRY.remoteSurplusOf({projectId: revnetId, decimals: 18, currency: currency});
|
|
379
|
+
uint256 omnichainSupply = localSupply + SUCKER_REGISTRY.remoteTotalSupplyOf(revnetId);
|
|
380
|
+
uint256 reclaimable = JBCashOuts.cashOutFrom({
|
|
381
|
+
surplus: omnichainSurplus,
|
|
365
382
|
cashOutCount: collateralCount,
|
|
366
|
-
totalSupply:
|
|
383
|
+
totalSupply: omnichainSupply,
|
|
367
384
|
cashOutTaxRate: currentStage.cashOutTaxRate()
|
|
368
385
|
});
|
|
386
|
+
// Cap at local surplus — can't borrow more than what this chain's terminals actually hold.
|
|
387
|
+
return reclaimable > localSurplus ? localSurplus : reclaimable;
|
|
369
388
|
}
|
|
370
389
|
|
|
371
390
|
/// @notice The amount of the loan that should be borrowed for the given collateral amount.
|
|
@@ -1078,33 +1097,16 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
|
|
|
1078
1097
|
? 0
|
|
1079
1098
|
: JBFees.feeAmountFrom({amountBeforeFee: addedBorrowAmount, feePercent: REV_PREPAID_FEE_PERCENT});
|
|
1080
1099
|
|
|
1100
|
+
// Try to pay the REV fee. If it fails, revFeeAmount is zeroed so the borrower receives it instead.
|
|
1081
1101
|
if (revFeeAmount > 0) {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
// borrower still pays the source fee and protocol fee.
|
|
1091
|
-
// slither-disable-next-line arbitrary-send-eth,unused-return
|
|
1092
|
-
try feeTerminal.pay{value: payValue}({
|
|
1093
|
-
projectId: REV_ID,
|
|
1094
|
-
token: loan.source.token,
|
|
1095
|
-
amount: revFeeAmount,
|
|
1096
|
-
beneficiary: beneficiary,
|
|
1097
|
-
minReturnedTokens: 0,
|
|
1098
|
-
memo: "",
|
|
1099
|
-
metadata: bytes(abi.encodePacked(revnetId))
|
|
1100
|
-
}) {}
|
|
1101
|
-
catch (bytes memory) {
|
|
1102
|
-
// If the fee can't be processed, decrease the ERC-20 allowance and zero out the fee
|
|
1103
|
-
// so the borrower receives it instead.
|
|
1104
|
-
if (loan.source.token != JBConstants.NATIVE_TOKEN) {
|
|
1105
|
-
IERC20(loan.source.token)
|
|
1106
|
-
.safeDecreaseAllowance({spender: address(feeTerminal), requestedDecrease: revFeeAmount});
|
|
1107
|
-
}
|
|
1102
|
+
if (!_tryPayFee({
|
|
1103
|
+
terminal: feeTerminal,
|
|
1104
|
+
projectId: REV_ID,
|
|
1105
|
+
token: loan.source.token,
|
|
1106
|
+
amount: revFeeAmount,
|
|
1107
|
+
beneficiary: beneficiary,
|
|
1108
|
+
metadataProjectId: revnetId
|
|
1109
|
+
})) {
|
|
1108
1110
|
revFeeAmount = 0;
|
|
1109
1111
|
}
|
|
1110
1112
|
}
|
|
@@ -1195,35 +1197,16 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
|
|
|
1195
1197
|
});
|
|
1196
1198
|
}
|
|
1197
1199
|
|
|
1198
|
-
//
|
|
1199
|
-
// cannot block all loan operations (matching the REV fee pattern above).
|
|
1200
|
+
// Try to pay the source fee. If it fails, transfer the amount to the beneficiary instead.
|
|
1200
1201
|
if (sourceFeeAmount > 0) {
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
// the borrower still pays the REV fee and protocol fee.
|
|
1210
|
-
// slither-disable-next-line unused-return,arbitrary-send-eth
|
|
1211
|
-
try sourceTerminal.pay{value: payValue}({
|
|
1212
|
-
projectId: revnetId,
|
|
1213
|
-
token: sourceToken,
|
|
1214
|
-
amount: sourceFeeAmount,
|
|
1215
|
-
beneficiary: beneficiary,
|
|
1216
|
-
minReturnedTokens: 0,
|
|
1217
|
-
memo: "",
|
|
1218
|
-
metadata: bytes(abi.encodePacked(REV_ID))
|
|
1219
|
-
}) {}
|
|
1220
|
-
catch (bytes memory) {
|
|
1221
|
-
// If the fee can't be processed, decrease the ERC-20 allowance and return the amount
|
|
1222
|
-
// to the beneficiary instead.
|
|
1223
|
-
if (sourceToken != JBConstants.NATIVE_TOKEN) {
|
|
1224
|
-
IERC20(sourceToken)
|
|
1225
|
-
.safeDecreaseAllowance({spender: address(sourceTerminal), requestedDecrease: sourceFeeAmount});
|
|
1226
|
-
}
|
|
1202
|
+
if (!_tryPayFee({
|
|
1203
|
+
terminal: IJBTerminal(address(sourceTerminal)),
|
|
1204
|
+
projectId: revnetId,
|
|
1205
|
+
token: sourceToken,
|
|
1206
|
+
amount: sourceFeeAmount,
|
|
1207
|
+
beneficiary: beneficiary,
|
|
1208
|
+
metadataProjectId: REV_ID
|
|
1209
|
+
})) {
|
|
1227
1210
|
_transferFrom({from: address(this), to: beneficiary, token: sourceToken, amount: sourceFeeAmount});
|
|
1228
1211
|
}
|
|
1229
1212
|
}
|
|
@@ -1517,6 +1500,45 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
|
|
|
1517
1500
|
PERMIT2.transferFrom({from: from, to: to, amount: uint160(amount), token: token});
|
|
1518
1501
|
}
|
|
1519
1502
|
|
|
1503
|
+
/// @notice Attempts to pay a fee to a terminal. On failure, cleans up the ERC-20 allowance and returns false.
|
|
1504
|
+
/// @param terminal The terminal to pay the fee to.
|
|
1505
|
+
/// @param projectId The project receiving the fee.
|
|
1506
|
+
/// @param token The token being used to pay the fee.
|
|
1507
|
+
/// @param amount The fee amount.
|
|
1508
|
+
/// @param beneficiary The address to credit for the fee payment.
|
|
1509
|
+
/// @param metadataProjectId The project ID encoded in the payment metadata.
|
|
1510
|
+
/// @return success Whether the fee was successfully paid.
|
|
1511
|
+
function _tryPayFee(
|
|
1512
|
+
IJBTerminal terminal,
|
|
1513
|
+
uint256 projectId,
|
|
1514
|
+
address token,
|
|
1515
|
+
uint256 amount,
|
|
1516
|
+
address beneficiary,
|
|
1517
|
+
uint256 metadataProjectId
|
|
1518
|
+
)
|
|
1519
|
+
internal
|
|
1520
|
+
returns (bool success)
|
|
1521
|
+
{
|
|
1522
|
+
uint256 payValue = _beforeTransferTo({to: address(terminal), token: token, amount: amount});
|
|
1523
|
+
|
|
1524
|
+
// slither-disable-next-line arbitrary-send-eth,unused-return
|
|
1525
|
+
try terminal.pay{value: payValue}({
|
|
1526
|
+
projectId: projectId,
|
|
1527
|
+
token: token,
|
|
1528
|
+
amount: amount,
|
|
1529
|
+
beneficiary: beneficiary,
|
|
1530
|
+
minReturnedTokens: 0,
|
|
1531
|
+
memo: "",
|
|
1532
|
+
metadata: bytes(abi.encodePacked(metadataProjectId))
|
|
1533
|
+
}) {
|
|
1534
|
+
success = true;
|
|
1535
|
+
} catch (bytes memory) {
|
|
1536
|
+
if (token != JBConstants.NATIVE_TOKEN) {
|
|
1537
|
+
IERC20(token).safeDecreaseAllowance({spender: address(terminal), requestedDecrease: amount});
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1520
1542
|
fallback() external payable {}
|
|
1521
1543
|
receive() external payable {}
|
|
1522
1544
|
}
|
package/src/REVOwner.sol
CHANGED
|
@@ -137,7 +137,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
137
137
|
/// @return cashOutTaxRate The cash out tax rate, which influences the amount of terminal tokens which get cashed
|
|
138
138
|
/// out.
|
|
139
139
|
/// @return cashOutCount The number of revnet tokens that are cashed out.
|
|
140
|
-
/// @return totalSupply The total
|
|
140
|
+
/// @return totalSupply The total token supply across all chains (for both proportional reclaim and tax).
|
|
141
|
+
/// @return effectiveSurplusValue The global surplus across all chains for proportional reclaim.
|
|
141
142
|
/// @return hookSpecifications The amount of funds and the data to send to cash out hooks (this contract).
|
|
142
143
|
function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
|
|
143
144
|
external
|
|
@@ -147,6 +148,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
147
148
|
uint256 cashOutTaxRate,
|
|
148
149
|
uint256 cashOutCount,
|
|
149
150
|
uint256 totalSupply,
|
|
151
|
+
uint256 effectiveSurplusValue,
|
|
150
152
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
151
153
|
)
|
|
152
154
|
{
|
|
@@ -154,7 +156,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
154
156
|
// This relies on the sucker registry to only contain trusted sucker contracts deployed via
|
|
155
157
|
// the registry's own deploySuckersFor flow — external addresses cannot register as suckers.
|
|
156
158
|
if (_isSuckerOf({revnetId: context.projectId, addr: context.holder})) {
|
|
157
|
-
return (0, context.cashOutCount, context.totalSupply, hookSpecifications);
|
|
159
|
+
return (0, context.cashOutCount, context.totalSupply, context.surplus.value, hookSpecifications);
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
// Keep a reference to the cash out delay of the revnet.
|
|
@@ -168,11 +170,23 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
168
170
|
// Get the terminal that will receive the cash out fee.
|
|
169
171
|
IJBTerminal feeTerminal = DIRECTORY.primaryTerminalOf({projectId: FEE_REVNET_ID, token: context.surplus.token});
|
|
170
172
|
|
|
173
|
+
// Compute the cross-chain total supply (local + remote peer chain supplies) for cross-chain-aware bonding
|
|
174
|
+
// curve.
|
|
175
|
+
totalSupply = context.totalSupply + SUCKER_REGISTRY.remoteTotalSupplyOf(context.projectId);
|
|
176
|
+
effectiveSurplusValue = context.surplus.value
|
|
177
|
+
+ SUCKER_REGISTRY.remoteSurplusOf({
|
|
178
|
+
projectId: context.projectId,
|
|
179
|
+
decimals: context.surplus.decimals,
|
|
180
|
+
currency: uint256(uint160(context.surplus.token))
|
|
181
|
+
});
|
|
182
|
+
|
|
171
183
|
// If there's no cash out tax (100% cash out tax rate), if there's no fee terminal, or if the beneficiary is
|
|
172
|
-
// feeless (e.g. the router terminal routing value between projects), proxy
|
|
184
|
+
// feeless (e.g. the router terminal routing value between projects), proxy to the buyback hook with our
|
|
185
|
+
// totalSupply and effectiveSurplusValue.
|
|
173
186
|
if (context.cashOutTaxRate == 0 || address(feeTerminal) == address(0) || context.beneficiaryIsFeeless) {
|
|
174
187
|
// slither-disable-next-line unused-return
|
|
175
|
-
|
|
188
|
+
(cashOutTaxRate, cashOutCount,,, hookSpecifications) = BUYBACK_HOOK.beforeCashOutRecordedWith(context);
|
|
189
|
+
return (cashOutTaxRate, cashOutCount, totalSupply, effectiveSurplusValue, hookSpecifications);
|
|
176
190
|
}
|
|
177
191
|
|
|
178
192
|
// Split the cashed-out tokens into a fee portion and a non-fee portion.
|
|
@@ -185,20 +199,33 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
185
199
|
uint256 nonFeeCashOutCount = context.cashOutCount - feeCashOutCount;
|
|
186
200
|
|
|
187
201
|
// Calculate how much surplus the non-fee tokens can reclaim via the bonding curve.
|
|
202
|
+
// Use effective (cross-chain) surplus; cap at local surplus.
|
|
188
203
|
uint256 postFeeReclaimedAmount = JBCashOuts.cashOutFrom({
|
|
189
|
-
surplus:
|
|
204
|
+
surplus: effectiveSurplusValue,
|
|
190
205
|
cashOutCount: nonFeeCashOutCount,
|
|
191
|
-
totalSupply:
|
|
206
|
+
totalSupply: totalSupply,
|
|
192
207
|
cashOutTaxRate: context.cashOutTaxRate
|
|
193
208
|
});
|
|
209
|
+
// Cap at local surplus — the bonding curve uses cross-chain effective surplus which can exceed what this
|
|
210
|
+
// chain's terminal actually holds.
|
|
211
|
+
if (postFeeReclaimedAmount > context.surplus.value) postFeeReclaimedAmount = context.surplus.value;
|
|
194
212
|
|
|
195
213
|
// Calculate how much the fee tokens reclaim from the remaining surplus after the non-fee reclaim.
|
|
214
|
+
// Use remaining effective surplus; cap at remaining local surplus.
|
|
196
215
|
uint256 feeAmount = JBCashOuts.cashOutFrom({
|
|
197
|
-
surplus:
|
|
216
|
+
surplus: effectiveSurplusValue > postFeeReclaimedAmount
|
|
217
|
+
? effectiveSurplusValue - postFeeReclaimedAmount
|
|
218
|
+
: 0,
|
|
198
219
|
cashOutCount: feeCashOutCount,
|
|
199
|
-
totalSupply:
|
|
220
|
+
totalSupply: totalSupply - nonFeeCashOutCount,
|
|
200
221
|
cashOutTaxRate: context.cashOutTaxRate
|
|
201
222
|
});
|
|
223
|
+
// Cap the fee reclaim at remaining local surplus. The bonding curve uses the cross-chain effective surplus,
|
|
224
|
+
// which can exceed what's actually held locally. Without this cap, the terminal would try to send more than
|
|
225
|
+
// it has.
|
|
226
|
+
if (feeAmount > context.surplus.value - postFeeReclaimedAmount) {
|
|
227
|
+
feeAmount = context.surplus.value - postFeeReclaimedAmount;
|
|
228
|
+
}
|
|
202
229
|
|
|
203
230
|
// Build a context for the buyback hook using only the non-fee token count.
|
|
204
231
|
JBBeforeCashOutRecordedContext memory buybackHookContext = context;
|
|
@@ -206,11 +233,13 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
206
233
|
|
|
207
234
|
// Let the buyback hook adjust the cash out parameters and optionally return a hook specification.
|
|
208
235
|
JBCashOutHookSpecification[] memory buybackHookSpecifications;
|
|
209
|
-
(cashOutTaxRate, cashOutCount
|
|
236
|
+
(cashOutTaxRate, cashOutCount,,, buybackHookSpecifications) =
|
|
210
237
|
BUYBACK_HOOK.beforeCashOutRecordedWith(buybackHookContext);
|
|
211
238
|
|
|
212
239
|
// If the fee rounds down to zero, return the buyback hook's response directly — no fee to process.
|
|
213
|
-
if (feeAmount == 0)
|
|
240
|
+
if (feeAmount == 0) {
|
|
241
|
+
return (cashOutTaxRate, cashOutCount, totalSupply, effectiveSurplusValue, buybackHookSpecifications);
|
|
242
|
+
}
|
|
214
243
|
|
|
215
244
|
// Build a hook spec that routes the fee amount to this contract's `afterCashOutRecordedWith` for processing.
|
|
216
245
|
JBCashOutHookSpecification memory feeSpec = JBCashOutHookSpecification({
|
|
@@ -232,7 +261,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook {
|
|
|
232
261
|
hookSpecifications[0] = feeSpec;
|
|
233
262
|
}
|
|
234
263
|
|
|
235
|
-
return (cashOutTaxRate, cashOutCount, totalSupply, hookSpecifications);
|
|
264
|
+
return (cashOutTaxRate, cashOutCount, totalSupply, effectiveSurplusValue, hookSpecifications);
|
|
236
265
|
}
|
|
237
266
|
|
|
238
267
|
/// @notice Before a revnet processes an incoming payment, determine the weight and pay hooks to use.
|
|
@@ -7,6 +7,7 @@ import {IJBPayoutTerminal} from "@bananapus/core-v6/src/interfaces/IJBPayoutTerm
|
|
|
7
7
|
import {IJBPrices} from "@bananapus/core-v6/src/interfaces/IJBPrices.sol";
|
|
8
8
|
import {IJBTokenUriResolver} from "@bananapus/core-v6/src/interfaces/IJBTokenUriResolver.sol";
|
|
9
9
|
import {JBSingleAllowance} from "@bananapus/core-v6/src/structs/JBSingleAllowance.sol";
|
|
10
|
+
import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
|
|
10
11
|
import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
|
|
11
12
|
import {REVLoan} from "../structs/REVLoan.sol";
|
|
12
13
|
import {REVLoanSource} from "../structs/REVLoanSource.sol";
|
|
@@ -167,6 +168,10 @@ interface IREVLoans {
|
|
|
167
168
|
/// @return The REV revnet ID.
|
|
168
169
|
function REV_ID() external view returns (uint256);
|
|
169
170
|
|
|
171
|
+
/// @notice The sucker registry used to discover peer chain suckers for cross-chain supply/surplus awareness.
|
|
172
|
+
/// @return The sucker registry.
|
|
173
|
+
function SUCKER_REGISTRY() external view returns (IJBSuckerRegistry);
|
|
174
|
+
|
|
170
175
|
/// @notice The fee percent charged by the REV revnet on each loan, in terms of `JBConstants.MAX_FEE`.
|
|
171
176
|
/// @return The REV prepaid fee percent.
|
|
172
177
|
function REV_PREPAID_FEE_PERCENT() external view returns (uint256);
|
|
@@ -40,6 +40,8 @@ import {JBArbitrumSucker, JBLayer, IArbGatewayRouter, IInbox} from "@bananapus/s
|
|
|
40
40
|
|
|
41
41
|
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
42
42
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
43
|
+
import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
44
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
43
45
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
44
46
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
45
47
|
|
|
@@ -211,7 +213,14 @@ contract REVnet_Integrations is TestBaseWorkflow {
|
|
|
211
213
|
HOOK_STORE = new JB721TiersHookStore();
|
|
212
214
|
|
|
213
215
|
EXAMPLE_HOOK = new JB721TiersHook(
|
|
214
|
-
jbDirectory(),
|
|
216
|
+
jbDirectory(),
|
|
217
|
+
jbPermissions(),
|
|
218
|
+
jbPrices(),
|
|
219
|
+
jbRulesets(),
|
|
220
|
+
HOOK_STORE,
|
|
221
|
+
jbSplits(),
|
|
222
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
223
|
+
multisig()
|
|
215
224
|
);
|
|
216
225
|
|
|
217
226
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
@@ -32,6 +32,8 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
|
32
32
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
33
33
|
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
34
34
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
35
|
+
import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
36
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
35
37
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
36
38
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
37
39
|
import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
|
|
@@ -77,7 +79,14 @@ contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow {
|
|
|
77
79
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
78
80
|
HOOK_STORE = new JB721TiersHookStore();
|
|
79
81
|
EXAMPLE_HOOK = new JB721TiersHook(
|
|
80
|
-
jbDirectory(),
|
|
82
|
+
jbDirectory(),
|
|
83
|
+
jbPermissions(),
|
|
84
|
+
jbPrices(),
|
|
85
|
+
jbRulesets(),
|
|
86
|
+
HOOK_STORE,
|
|
87
|
+
jbSplits(),
|
|
88
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
89
|
+
multisig()
|
|
81
90
|
);
|
|
82
91
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
83
92
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
@@ -35,10 +35,13 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
|
35
35
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
36
36
|
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
37
37
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
38
|
+
import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
39
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
38
40
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
39
41
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
40
42
|
import {REVOwner} from "../src/REVOwner.sol";
|
|
41
43
|
import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
|
|
44
|
+
import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
|
|
42
45
|
|
|
43
46
|
/// @notice Regression tests for REVDeployer.
|
|
44
47
|
contract REVDeployerRegressions is TestBaseWorkflow {
|
|
@@ -83,7 +86,14 @@ contract REVDeployerRegressions is TestBaseWorkflow {
|
|
|
83
86
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
84
87
|
HOOK_STORE = new JB721TiersHookStore();
|
|
85
88
|
EXAMPLE_HOOK = new JB721TiersHook(
|
|
86
|
-
jbDirectory(),
|
|
89
|
+
jbDirectory(),
|
|
90
|
+
jbPermissions(),
|
|
91
|
+
jbPrices(),
|
|
92
|
+
jbRulesets(),
|
|
93
|
+
HOOK_STORE,
|
|
94
|
+
jbSplits(),
|
|
95
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
96
|
+
multisig()
|
|
87
97
|
);
|
|
88
98
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
89
99
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
@@ -92,6 +102,7 @@ contract REVDeployerRegressions is TestBaseWorkflow {
|
|
|
92
102
|
|
|
93
103
|
LOANS_CONTRACT = new REVLoans({
|
|
94
104
|
controller: jbController(),
|
|
105
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
95
106
|
revId: FEE_PROJECT_ID,
|
|
96
107
|
owner: address(this),
|
|
97
108
|
permit2: permit2(),
|
|
@@ -41,6 +41,8 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
|
41
41
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
42
42
|
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
43
43
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
44
|
+
import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
45
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
44
46
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
45
47
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
46
48
|
import {mulDiv} from "@prb/math/src/Common.sol";
|
|
@@ -50,6 +52,7 @@ import {BrokenFeeTerminal} from "./helpers/MaliciousContracts.sol";
|
|
|
50
52
|
import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
|
|
51
53
|
import {REVOwner} from "../src/REVOwner.sol";
|
|
52
54
|
import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
|
|
55
|
+
import {MockSuckerRegistry} from "./mock/MockSuckerRegistry.sol";
|
|
53
56
|
|
|
54
57
|
// =========================================================================
|
|
55
58
|
// Shared config struct
|
|
@@ -234,7 +237,14 @@ contract REVInvincibility_PropertyTests is TestBaseWorkflow {
|
|
|
234
237
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
235
238
|
HOOK_STORE = new JB721TiersHookStore();
|
|
236
239
|
EXAMPLE_HOOK = new JB721TiersHook(
|
|
237
|
-
jbDirectory(),
|
|
240
|
+
jbDirectory(),
|
|
241
|
+
jbPermissions(),
|
|
242
|
+
jbPrices(),
|
|
243
|
+
jbRulesets(),
|
|
244
|
+
HOOK_STORE,
|
|
245
|
+
jbSplits(),
|
|
246
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
247
|
+
multisig()
|
|
238
248
|
);
|
|
239
249
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
240
250
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
@@ -244,6 +254,7 @@ contract REVInvincibility_PropertyTests is TestBaseWorkflow {
|
|
|
244
254
|
|
|
245
255
|
LOANS_CONTRACT = new REVLoans({
|
|
246
256
|
controller: jbController(),
|
|
257
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
247
258
|
revId: FEE_PROJECT_ID,
|
|
248
259
|
owner: address(this),
|
|
249
260
|
permit2: permit2(),
|
|
@@ -1019,7 +1030,14 @@ contract REVInvincibility_Invariants is StdInvariant, TestBaseWorkflow {
|
|
|
1019
1030
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
1020
1031
|
HOOK_STORE = new JB721TiersHookStore();
|
|
1021
1032
|
EXAMPLE_HOOK = new JB721TiersHook(
|
|
1022
|
-
jbDirectory(),
|
|
1033
|
+
jbDirectory(),
|
|
1034
|
+
jbPermissions(),
|
|
1035
|
+
jbPrices(),
|
|
1036
|
+
jbRulesets(),
|
|
1037
|
+
HOOK_STORE,
|
|
1038
|
+
jbSplits(),
|
|
1039
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
1040
|
+
multisig()
|
|
1023
1041
|
);
|
|
1024
1042
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
1025
1043
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
@@ -1028,6 +1046,7 @@ contract REVInvincibility_Invariants is StdInvariant, TestBaseWorkflow {
|
|
|
1028
1046
|
|
|
1029
1047
|
LOANS_CONTRACT = new REVLoans({
|
|
1030
1048
|
controller: jbController(),
|
|
1049
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
1031
1050
|
revId: FEE_PROJECT_ID,
|
|
1032
1051
|
owner: address(this),
|
|
1033
1052
|
permit2: permit2(),
|