@rev-net/core-v6 0.0.56 → 0.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -8
- package/script/Deploy.s.sol +71 -96
- package/script/helpers/RevnetCoreDeploymentLib.sol +12 -17
- package/src/REVDeployer.sol +4 -8
- package/src/REVLoans.sol +8 -0
- package/src/REVOwner.sol +14 -22
- package/src/interfaces/IREVDeployer.sol +0 -4
- package/src/interfaces/IREVOwner.sol +2 -2
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.58",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'revnet-core-v6'"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
30
|
-
"@bananapus/buyback-hook-v6": "^0.0.
|
|
31
|
-
"@bananapus/core-v6": "^0.0.
|
|
32
|
-
"@bananapus/ownable-v6": "^0.0.
|
|
29
|
+
"@bananapus/721-hook-v6": "^0.0.51",
|
|
30
|
+
"@bananapus/buyback-hook-v6": "^0.0.47",
|
|
31
|
+
"@bananapus/core-v6": "^0.0.54",
|
|
32
|
+
"@bananapus/ownable-v6": "^0.0.25",
|
|
33
33
|
"@bananapus/permission-ids-v6": "^0.0.25",
|
|
34
|
-
"@bananapus/router-terminal-v6": "^0.0.
|
|
35
|
-
"@bananapus/suckers-v6": "^0.0.
|
|
36
|
-
"@croptop/core-v6": "^0.0.
|
|
34
|
+
"@bananapus/router-terminal-v6": "^0.0.44",
|
|
35
|
+
"@bananapus/suckers-v6": "^0.0.47",
|
|
36
|
+
"@croptop/core-v6": "^0.0.48",
|
|
37
37
|
"@openzeppelin/contracts": "5.6.1",
|
|
38
38
|
"@uniswap/permit2": "github:Uniswap/permit2#cc56ad0f3439c502c246fc5cfcc3db92bb8b7219"
|
|
39
39
|
},
|
package/script/Deploy.s.sol
CHANGED
|
@@ -67,54 +67,31 @@ contract DeployScript is Script, Sphinx {
|
|
|
67
67
|
SuckerDeployment suckers;
|
|
68
68
|
/// @notice tracks the deployment of the router terminal.
|
|
69
69
|
RouterTerminalDeployment routerTerminal;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
bytes32 DEPLOYER_SALT = "_REV_DEPLOYER_SALT_V6_";
|
|
93
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
94
|
-
bytes32 REVLOANS_SALT = "_REV_LOANS_SALT_V6_";
|
|
95
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
96
|
-
bytes32 REVOWNER_SALT = "_REV_OWNER_SALT_V6_";
|
|
97
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
98
|
-
address LOANS_OWNER;
|
|
99
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
100
|
-
address OPERATOR;
|
|
101
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
102
|
-
address TRUSTED_FORWARDER;
|
|
103
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
104
|
-
IPermit2 PERMIT2;
|
|
105
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
106
|
-
uint48 REV_START_TIME = 1_740_089_444;
|
|
107
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
108
|
-
uint104 REV_MAINNET_AUTO_ISSUANCE_ = 1_050_482_341_387_116_262_330_122;
|
|
109
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
110
|
-
uint104 REV_BASE_AUTO_ISSUANCE_ = 38_544_322_230_437_559_731_228;
|
|
111
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
112
|
-
uint104 REV_OP_AUTO_ISSUANCE_ = 32_069_388_242_375_817_844;
|
|
113
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
114
|
-
uint104 REV_ARB_AUTO_ISSUANCE_ = 3_479_431_776_906_850_000_000;
|
|
70
|
+
uint32 private constant _PREMINT_CHAIN_ID = 1;
|
|
71
|
+
string private constant _NAME = "Revnet";
|
|
72
|
+
string private constant _SYMBOL = "REV";
|
|
73
|
+
string private constant _PROJECT_URI = "ipfs://QmcCBD5fM927LjkLDSJWtNEU9FohcbiPSfqtGRHXFHzJ4W";
|
|
74
|
+
uint32 private constant _NATIVE_CURRENCY = uint32(uint160(JBConstants.NATIVE_TOKEN));
|
|
75
|
+
uint32 private constant _ETH_CURRENCY = JBCurrencyIds.ETH;
|
|
76
|
+
uint8 private constant _DECIMALS = 18;
|
|
77
|
+
uint256 private constant _DECIMAL_MULTIPLIER = 10 ** _DECIMALS;
|
|
78
|
+
bytes32 private constant _ERC20_SALT = "_REV_ERC20_SALT_V6_";
|
|
79
|
+
bytes32 private constant _SUCKER_SALT = "_REV_SUCKER_SALT_V6_";
|
|
80
|
+
bytes32 private constant _DEPLOYER_SALT = "_REV_DEPLOYER_SALT_V6_";
|
|
81
|
+
bytes32 private constant _REVLOANS_SALT = "_REV_LOANS_SALT_V6_";
|
|
82
|
+
bytes32 private constant _REVOWNER_SALT = "_REV_OWNER_SALT_V6_";
|
|
83
|
+
address private loansOwner;
|
|
84
|
+
address private operator;
|
|
85
|
+
address private trustedForwarder;
|
|
86
|
+
IPermit2 private permit2;
|
|
87
|
+
uint48 private constant _REV_START_TIME = 1_740_089_444;
|
|
88
|
+
uint104 private constant _REV_MAINNET_AUTO_ISSUANCE = 1_050_482_341_387_116_262_330_122;
|
|
89
|
+
uint104 private constant _REV_BASE_AUTO_ISSUANCE = 38_544_322_230_437_559_731_228;
|
|
90
|
+
uint104 private constant _REV_OP_AUTO_ISSUANCE = 32_069_388_242_375_817_844;
|
|
91
|
+
uint104 private constant _REV_ARB_AUTO_ISSUANCE = 3_479_431_776_906_850_000_000;
|
|
115
92
|
|
|
116
93
|
function configureSphinx() public override {
|
|
117
|
-
//
|
|
94
|
+
// Safe owners and threshold are resolved by the Sphinx project config.
|
|
118
95
|
sphinxConfig.projectName = "revnet-core-v6";
|
|
119
96
|
sphinxConfig.mainnets = ["ethereum", "optimism", "base", "arbitrum"];
|
|
120
97
|
sphinxConfig.testnets = ["ethereum_sepolia", "optimism_sepolia", "base_sepolia", "arbitrum_sepolia"];
|
|
@@ -122,9 +99,9 @@ contract DeployScript is Script, Sphinx {
|
|
|
122
99
|
|
|
123
100
|
function run() public {
|
|
124
101
|
// Get the operator address.
|
|
125
|
-
|
|
102
|
+
operator = safeAddress();
|
|
126
103
|
// Get the loans owner address.
|
|
127
|
-
|
|
104
|
+
loansOwner = safeAddress();
|
|
128
105
|
|
|
129
106
|
// Get the deployment addresses for the nana CORE for this chain.
|
|
130
107
|
// We want to do this outside of the `sphinx` modifier.
|
|
@@ -169,8 +146,8 @@ contract DeployScript is Script, Sphinx {
|
|
|
169
146
|
);
|
|
170
147
|
|
|
171
148
|
// We use the same trusted forwarder and permit2 as the core deployment.
|
|
172
|
-
|
|
173
|
-
|
|
149
|
+
trustedForwarder = core.controller.trustedForwarder();
|
|
150
|
+
permit2 = core.terminal.PERMIT2();
|
|
174
151
|
|
|
175
152
|
// Perform the deployment transactions.
|
|
176
153
|
deploy();
|
|
@@ -182,7 +159,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
182
159
|
|
|
183
160
|
// Accept the chain's native currency through the multi terminal.
|
|
184
161
|
accountingContextsToAccept[0] =
|
|
185
|
-
JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals:
|
|
162
|
+
JBAccountingContext({token: JBConstants.NATIVE_TOKEN, decimals: _DECIMALS, currency: _NATIVE_CURRENCY});
|
|
186
163
|
|
|
187
164
|
// The terminals that the project will accept funds through.
|
|
188
165
|
JBTerminalConfig[] memory terminalConfigurations = new JBTerminalConfig[](2);
|
|
@@ -202,25 +179,25 @@ contract DeployScript is Script, Sphinx {
|
|
|
202
179
|
preferAddToBalance: false,
|
|
203
180
|
percent: JBConstants.SPLITS_TOTAL_PERCENT,
|
|
204
181
|
projectId: 0,
|
|
205
|
-
beneficiary: payable(
|
|
182
|
+
beneficiary: payable(operator),
|
|
206
183
|
lockedUntil: 0,
|
|
207
184
|
hook: IJBSplitHook(address(0))
|
|
208
185
|
});
|
|
209
186
|
|
|
210
187
|
{
|
|
211
188
|
REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](4);
|
|
212
|
-
issuanceConfs[0] = REVAutoIssuance({chainId: 1, count:
|
|
213
|
-
issuanceConfs[1] = REVAutoIssuance({chainId: 8453, count:
|
|
214
|
-
issuanceConfs[2] = REVAutoIssuance({chainId: 10, count:
|
|
215
|
-
issuanceConfs[3] = REVAutoIssuance({chainId: 42_161, count:
|
|
189
|
+
issuanceConfs[0] = REVAutoIssuance({chainId: 1, count: _REV_MAINNET_AUTO_ISSUANCE, beneficiary: operator});
|
|
190
|
+
issuanceConfs[1] = REVAutoIssuance({chainId: 8453, count: _REV_BASE_AUTO_ISSUANCE, beneficiary: operator});
|
|
191
|
+
issuanceConfs[2] = REVAutoIssuance({chainId: 10, count: _REV_OP_AUTO_ISSUANCE, beneficiary: operator});
|
|
192
|
+
issuanceConfs[3] = REVAutoIssuance({chainId: 42_161, count: _REV_ARB_AUTO_ISSUANCE, beneficiary: operator});
|
|
216
193
|
|
|
217
194
|
stageConfigurations[0] = REVStageConfig({
|
|
218
|
-
startsAtOrAfter:
|
|
195
|
+
startsAtOrAfter: _REV_START_TIME,
|
|
219
196
|
autoIssuances: issuanceConfs,
|
|
220
197
|
splitPercent: 3800, // 38%
|
|
221
198
|
splits: splits,
|
|
222
199
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
223
|
-
initialIssuance: uint112(10_000 *
|
|
200
|
+
initialIssuance: uint112(10_000 * _DECIMAL_MULTIPLIER),
|
|
224
201
|
issuanceCutFrequency: 90 days,
|
|
225
202
|
issuanceCutPercent: 380_000_000, // 38%
|
|
226
203
|
cashOutTaxRate: 1000, // 0.1
|
|
@@ -232,10 +209,10 @@ contract DeployScript is Script, Sphinx {
|
|
|
232
209
|
REVAutoIssuance[] memory issuanceConfs = new REVAutoIssuance[](1);
|
|
233
210
|
issuanceConfs[0] = REVAutoIssuance({
|
|
234
211
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
235
|
-
chainId:
|
|
212
|
+
chainId: _PREMINT_CHAIN_ID,
|
|
236
213
|
// forge-lint: disable-next-line(unsafe-typecast)
|
|
237
|
-
count: uint104(1_550_000 *
|
|
238
|
-
beneficiary:
|
|
214
|
+
count: uint104(1_550_000 * _DECIMAL_MULTIPLIER),
|
|
215
|
+
beneficiary: operator
|
|
239
216
|
});
|
|
240
217
|
|
|
241
218
|
stageConfigurations[1] = REVStageConfig({
|
|
@@ -265,9 +242,9 @@ contract DeployScript is Script, Sphinx {
|
|
|
265
242
|
|
|
266
243
|
// The project's revnet configuration
|
|
267
244
|
REVConfig memory revnetConfiguration = REVConfig({
|
|
268
|
-
description: REVDescription({name:
|
|
269
|
-
baseCurrency:
|
|
270
|
-
operator:
|
|
245
|
+
description: REVDescription({name: _NAME, ticker: _SYMBOL, uri: _PROJECT_URI, salt: _ERC20_SALT}),
|
|
246
|
+
baseCurrency: _ETH_CURRENCY,
|
|
247
|
+
operator: operator,
|
|
271
248
|
scopeCashOutsToLocalBalances: false,
|
|
272
249
|
stageConfigurations: stageConfigurations
|
|
273
250
|
});
|
|
@@ -314,7 +291,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
314
291
|
}
|
|
315
292
|
// Specify all sucker deployments.
|
|
316
293
|
suckerDeploymentConfiguration =
|
|
317
|
-
REVSuckerDeploymentConfig({deployerConfigurations: suckerDeployerConfigurations, salt:
|
|
294
|
+
REVSuckerDeploymentConfig({deployerConfigurations: suckerDeployerConfigurations, salt: _SUCKER_SALT});
|
|
318
295
|
}
|
|
319
296
|
|
|
320
297
|
return FeeProjectConfig({
|
|
@@ -329,7 +306,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
329
306
|
tokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
330
307
|
contractUri: "",
|
|
331
308
|
tiersConfig: JB721InitTiersConfig({
|
|
332
|
-
tiers: new JB721TierConfig[](0), currency:
|
|
309
|
+
tiers: new JB721TierConfig[](0), currency: _ETH_CURRENCY, decimals: 18
|
|
333
310
|
}),
|
|
334
311
|
flags: REV721TiersHookFlags({
|
|
335
312
|
noNewTiersWithReserves: false,
|
|
@@ -351,8 +328,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
351
328
|
function deploy() public sphinx {
|
|
352
329
|
// Check if singletons are already deployed before creating a new fee project.
|
|
353
330
|
// This prevents creating orphan projects on script restarts.
|
|
354
|
-
|
|
355
|
-
uint256 FEE_PROJECT_ID;
|
|
331
|
+
uint256 feeProjectId;
|
|
356
332
|
|
|
357
333
|
// Predict the REVLoans address for an arbitrary fee project ID to check if it exists.
|
|
358
334
|
// We can't predict without a fee project ID, so we first check the REVDeployer which also stores it.
|
|
@@ -360,7 +336,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
360
336
|
|
|
361
337
|
// First, check if REVLoans is already deployed by trying with project ID = 0 (placeholder).
|
|
362
338
|
// We need to iterate: if either singleton exists, extract the fee project ID from it.
|
|
363
|
-
// Since both encode
|
|
339
|
+
// Since both encode feeProjectId, we check if any code exists at the predicted address
|
|
364
340
|
// for sequential project IDs starting from 1.
|
|
365
341
|
|
|
366
342
|
// A simpler approach: predict the REVDeployer address for each possible fee project ID
|
|
@@ -385,10 +361,10 @@ contract DeployScript is Script, Sphinx {
|
|
|
385
361
|
for (uint256 _candidateId = 1; _candidateId < _nextProjectId; _candidateId++) {
|
|
386
362
|
// Predict REVLoans address for this candidate fee project ID.
|
|
387
363
|
(address _candidateRevloansAddr, bool _candidateRevloansDeployed) = _isDeployed({
|
|
388
|
-
salt:
|
|
364
|
+
salt: _REVLOANS_SALT,
|
|
389
365
|
creationCode: type(REVLoans).creationCode,
|
|
390
366
|
arguments: abi.encode(
|
|
391
|
-
core.controller, suckers.registry, _candidateId,
|
|
367
|
+
core.controller, suckers.registry, _candidateId, loansOwner, permit2, trustedForwarder
|
|
392
368
|
)
|
|
393
369
|
});
|
|
394
370
|
|
|
@@ -396,14 +372,14 @@ contract DeployScript is Script, Sphinx {
|
|
|
396
372
|
// Verify the fee project ID matches by reading the immutable.
|
|
397
373
|
if (REVLoans(payable(_candidateRevloansAddr)).REV_ID() == _candidateId) {
|
|
398
374
|
// Record the fee project ID from the existing deployment.
|
|
399
|
-
|
|
375
|
+
feeProjectId = _candidateId;
|
|
400
376
|
// Record the existing REVLoans address.
|
|
401
377
|
_existingRevloansAddr = _candidateRevloansAddr;
|
|
402
378
|
_revloansExists = true;
|
|
403
379
|
|
|
404
380
|
// Also predict and verify the owner.
|
|
405
381
|
(_existingOwnerAddr, _revOwnerExists) = _isDeployed({
|
|
406
|
-
salt:
|
|
382
|
+
salt: _REVOWNER_SALT,
|
|
407
383
|
creationCode: type(REVOwner).creationCode,
|
|
408
384
|
arguments: abi.encode(
|
|
409
385
|
IJBBuybackHookRegistry(address(buybackHook.registry)),
|
|
@@ -417,7 +393,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
417
393
|
|
|
418
394
|
// Also predict and verify the deployer.
|
|
419
395
|
(_existingDeployerAddr, _revDeployerExists) = _isDeployed({
|
|
420
|
-
salt:
|
|
396
|
+
salt: _DEPLOYER_SALT,
|
|
421
397
|
creationCode: type(REVDeployer).creationCode,
|
|
422
398
|
arguments: abi.encode(
|
|
423
399
|
core.controller,
|
|
@@ -427,7 +403,7 @@ contract DeployScript is Script, Sphinx {
|
|
|
427
403
|
croptop.publisher,
|
|
428
404
|
IJBBuybackHookRegistry(address(buybackHook.registry)),
|
|
429
405
|
_candidateRevloansAddr,
|
|
430
|
-
|
|
406
|
+
trustedForwarder,
|
|
431
407
|
_existingOwnerAddr
|
|
432
408
|
)
|
|
433
409
|
});
|
|
@@ -445,91 +421,90 @@ contract DeployScript is Script, Sphinx {
|
|
|
445
421
|
for (uint256 i = _nextProjectId - 1; i >= 1; i--) {
|
|
446
422
|
if (core.projects.ownerOf(i) == safeAddress()) {
|
|
447
423
|
if (address(core.controller.DIRECTORY().controllerOf(i)) == address(0)) {
|
|
448
|
-
|
|
424
|
+
feeProjectId = i;
|
|
449
425
|
_foundExisting = true;
|
|
450
426
|
break;
|
|
451
427
|
}
|
|
452
428
|
}
|
|
453
429
|
}
|
|
454
430
|
if (!_foundExisting) {
|
|
455
|
-
|
|
456
|
-
FEE_PROJECT_ID = core.projects.createFor(safeAddress());
|
|
431
|
+
feeProjectId = core.projects.createFor(safeAddress());
|
|
457
432
|
}
|
|
458
433
|
}
|
|
459
434
|
|
|
460
435
|
// Deploy REVLoans first — it only depends on the controller.
|
|
461
436
|
REVLoans revloans = _revloansExists
|
|
462
437
|
? REVLoans(payable(_existingRevloansAddr))
|
|
463
|
-
: new REVLoans{salt:
|
|
438
|
+
: new REVLoans{salt: _REVLOANS_SALT}({
|
|
464
439
|
controller: core.controller,
|
|
465
440
|
suckerRegistry: suckers.registry,
|
|
466
|
-
revId:
|
|
467
|
-
owner:
|
|
468
|
-
permit2:
|
|
469
|
-
trustedForwarder:
|
|
441
|
+
revId: feeProjectId,
|
|
442
|
+
owner: loansOwner,
|
|
443
|
+
permit2: permit2,
|
|
444
|
+
trustedForwarder: trustedForwarder
|
|
470
445
|
});
|
|
471
446
|
|
|
472
447
|
// Deploy REVOwner — the runtime data hook that handles pay and cash out callbacks.
|
|
473
448
|
REVOwner revOwner = _revOwnerExists
|
|
474
449
|
? REVOwner(_existingOwnerAddr)
|
|
475
|
-
: new REVOwner{salt:
|
|
450
|
+
: new REVOwner{salt: _REVOWNER_SALT}({
|
|
476
451
|
buybackHook: IJBBuybackHookRegistry(address(buybackHook.registry)),
|
|
477
452
|
directory: core.controller.DIRECTORY(),
|
|
478
|
-
feeRevnetId:
|
|
453
|
+
feeRevnetId: feeProjectId,
|
|
479
454
|
suckerRegistry: suckers.registry,
|
|
480
455
|
loans: revloans,
|
|
481
|
-
|
|
456
|
+
deployerAddress: msg.sender
|
|
482
457
|
});
|
|
483
458
|
|
|
484
459
|
// Deploy REVDeployer with the REVLoans, buyback hook, and REVOwner addresses.
|
|
485
460
|
(address _deployerAddr, bool _deployerIsDeployed) = _revDeployerExists
|
|
486
461
|
? (_existingDeployerAddr, true)
|
|
487
462
|
: _isDeployed({
|
|
488
|
-
salt:
|
|
463
|
+
salt: _DEPLOYER_SALT,
|
|
489
464
|
creationCode: type(REVDeployer).creationCode,
|
|
490
465
|
arguments: abi.encode(
|
|
491
466
|
core.controller,
|
|
492
467
|
suckers.registry,
|
|
493
|
-
|
|
468
|
+
feeProjectId,
|
|
494
469
|
hook.hook_deployer,
|
|
495
470
|
croptop.publisher,
|
|
496
471
|
IJBBuybackHookRegistry(address(buybackHook.registry)),
|
|
497
472
|
revloans,
|
|
498
|
-
|
|
473
|
+
trustedForwarder,
|
|
499
474
|
address(revOwner)
|
|
500
475
|
)
|
|
501
476
|
});
|
|
502
|
-
if (address(revOwner.
|
|
477
|
+
if (address(revOwner.deployer()) == address(0)) {
|
|
503
478
|
revOwner.setDeployer(IREVDeployer(_deployerAddr));
|
|
504
479
|
}
|
|
505
480
|
REVDeployer _basicDeployer = _deployerIsDeployed
|
|
506
481
|
? REVDeployer(payable(_deployerAddr))
|
|
507
|
-
: new REVDeployer{salt:
|
|
482
|
+
: new REVDeployer{salt: _DEPLOYER_SALT}({
|
|
508
483
|
controller: core.controller,
|
|
509
484
|
suckerRegistry: suckers.registry,
|
|
510
|
-
feeRevnetId:
|
|
485
|
+
feeRevnetId: feeProjectId,
|
|
511
486
|
hookDeployer: hook.hook_deployer,
|
|
512
487
|
publisher: croptop.publisher,
|
|
513
488
|
buybackHook: IJBBuybackHookRegistry(address(buybackHook.registry)),
|
|
514
489
|
loans: revloans,
|
|
515
|
-
trustedForwarder:
|
|
490
|
+
trustedForwarder: trustedForwarder,
|
|
516
491
|
owner: address(revOwner)
|
|
517
492
|
});
|
|
518
493
|
|
|
519
494
|
// Only configure the fee project if it doesn't already have a controller.
|
|
520
495
|
// This handles both fresh deploys and restarts where singletons exist but the fee project
|
|
521
496
|
// was not yet configured.
|
|
522
|
-
bool _feeProjectConfigured = address(core.controller.DIRECTORY().controllerOf(
|
|
497
|
+
bool _feeProjectConfigured = address(core.controller.DIRECTORY().controllerOf(feeProjectId)) != address(0);
|
|
523
498
|
if (!_feeProjectConfigured) {
|
|
524
499
|
// Approve the basic deployer to configure the project.
|
|
525
|
-
core.projects.approve({to: address(_basicDeployer), tokenId:
|
|
500
|
+
core.projects.approve({to: address(_basicDeployer), tokenId: feeProjectId});
|
|
526
501
|
|
|
527
502
|
// Build the config.
|
|
528
503
|
FeeProjectConfig memory feeProjectConfig = getFeeProjectConfig();
|
|
529
504
|
|
|
530
505
|
// Configure the project.
|
|
531
506
|
_basicDeployer.deployFor({
|
|
532
|
-
revnetId:
|
|
507
|
+
revnetId: feeProjectId,
|
|
533
508
|
configuration: feeProjectConfig.configuration,
|
|
534
509
|
terminalConfigurations: feeProjectConfig.terminalConfigurations,
|
|
535
510
|
suckerDeploymentConfiguration: feeProjectConfig.suckerDeploymentConfiguration,
|
|
@@ -10,8 +10,7 @@ import {IREVLoans} from "./../../src/interfaces/IREVLoans.sol";
|
|
|
10
10
|
import {REVOwner} from "./../../src/REVOwner.sol";
|
|
11
11
|
|
|
12
12
|
struct RevnetCoreDeployment {
|
|
13
|
-
|
|
14
|
-
IREVDeployer basic_deployer;
|
|
13
|
+
IREVDeployer basicDeployer;
|
|
15
14
|
IREVLoans loans;
|
|
16
15
|
REVOwner owner;
|
|
17
16
|
}
|
|
@@ -23,17 +22,16 @@ library RevnetCoreDeploymentLib {
|
|
|
23
22
|
Vm internal constant vm = Vm(VM_ADDRESS);
|
|
24
23
|
|
|
25
24
|
function getDeployment(string memory path) internal returns (RevnetCoreDeployment memory deployment) {
|
|
26
|
-
//
|
|
25
|
+
// Match the current chain ID to the Sphinx network name used in deployment artifacts.
|
|
27
26
|
uint256 chainId = block.chainid;
|
|
28
27
|
|
|
29
|
-
//
|
|
30
|
-
// TODO: get constants without deploy.
|
|
28
|
+
// `SphinxConstants` exposes Sphinx's supported chain ID to network name mapping.
|
|
31
29
|
SphinxConstants sphinxConstants = new SphinxConstants();
|
|
32
30
|
NetworkInfo[] memory networks = sphinxConstants.getNetworkInfoArray();
|
|
33
31
|
|
|
34
32
|
for (uint256 _i; _i < networks.length; _i++) {
|
|
35
33
|
if (networks[_i].chainId == chainId) {
|
|
36
|
-
return getDeployment({path: path,
|
|
34
|
+
return getDeployment({path: path, networkName: networks[_i].name});
|
|
37
35
|
}
|
|
38
36
|
}
|
|
39
37
|
|
|
@@ -42,28 +40,27 @@ library RevnetCoreDeploymentLib {
|
|
|
42
40
|
|
|
43
41
|
function getDeployment(
|
|
44
42
|
string memory path,
|
|
45
|
-
|
|
46
|
-
string memory network_name
|
|
43
|
+
string memory networkName
|
|
47
44
|
)
|
|
48
45
|
internal
|
|
49
46
|
view
|
|
50
47
|
returns (RevnetCoreDeployment memory deployment)
|
|
51
48
|
{
|
|
52
|
-
deployment.
|
|
49
|
+
deployment.basicDeployer = IREVDeployer(
|
|
53
50
|
_getDeploymentAddress({
|
|
54
|
-
path: path,
|
|
51
|
+
path: path, projectName: "revnet-core-v6", networkName: networkName, contractName: "REVDeployer"
|
|
55
52
|
})
|
|
56
53
|
);
|
|
57
54
|
|
|
58
55
|
deployment.loans = IREVLoans(
|
|
59
56
|
_getDeploymentAddress({
|
|
60
|
-
path: path,
|
|
57
|
+
path: path, projectName: "revnet-core-v6", networkName: networkName, contractName: "REVLoans"
|
|
61
58
|
})
|
|
62
59
|
);
|
|
63
60
|
|
|
64
61
|
deployment.owner = REVOwner(
|
|
65
62
|
_getDeploymentAddress({
|
|
66
|
-
path: path,
|
|
63
|
+
path: path, projectName: "revnet-core-v6", networkName: networkName, contractName: "REVOwner"
|
|
67
64
|
})
|
|
68
65
|
);
|
|
69
66
|
}
|
|
@@ -75,10 +72,8 @@ library RevnetCoreDeploymentLib {
|
|
|
75
72
|
/// @return The address of the contract.
|
|
76
73
|
function _getDeploymentAddress(
|
|
77
74
|
string memory path,
|
|
78
|
-
|
|
79
|
-
string memory
|
|
80
|
-
// forge-lint: disable-next-line(mixed-case-variable)
|
|
81
|
-
string memory network_name,
|
|
75
|
+
string memory projectName,
|
|
76
|
+
string memory networkName,
|
|
82
77
|
string memory contractName
|
|
83
78
|
)
|
|
84
79
|
internal
|
|
@@ -87,7 +82,7 @@ library RevnetCoreDeploymentLib {
|
|
|
87
82
|
{
|
|
88
83
|
string memory deploymentJson =
|
|
89
84
|
// forge-lint: disable-next-line(unsafe-cheatcode)
|
|
90
|
-
vm.readFile(string.concat(path,
|
|
85
|
+
vm.readFile(string.concat(path, projectName, "/", networkName, "/", contractName, ".json"));
|
|
91
86
|
return stdJson.readAddress({json: deploymentJson, key: ".address"});
|
|
92
87
|
}
|
|
93
88
|
}
|
package/src/REVDeployer.sol
CHANGED
|
@@ -79,12 +79,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
79
79
|
/// @dev 30 days, in seconds.
|
|
80
80
|
uint256 public constant override CASH_OUT_DELAY = 2_592_000;
|
|
81
81
|
|
|
82
|
-
/// @notice The cash out fee (as a fraction out of `JBConstants.MAX_FEE`).
|
|
83
|
-
/// Cashout fees are paid to the revnet with the `FEE_REVNET_ID`.
|
|
84
|
-
/// @dev Fees are charged on cashouts if the cash out tax rate is greater than 0%.
|
|
85
|
-
/// @dev When suckers withdraw funds, they do not pay cash out fees.
|
|
86
|
-
uint256 public constant override FEE = 25; // 2.5%
|
|
87
|
-
|
|
88
82
|
/// @notice The default Uniswap pool fee tier used when auto-configuring buyback pools.
|
|
89
83
|
/// @dev 10_000 = 1%. This is the standard fee tier for most project token pairs.
|
|
90
84
|
uint24 public constant DEFAULT_BUYBACK_POOL_FEE = 10_000;
|
|
@@ -421,10 +415,12 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
421
415
|
sqrtPriceX96 = uint160(1 << 96);
|
|
422
416
|
} else if (normalizedTerminalToken < projectToken) {
|
|
423
417
|
// token0 = terminal, token1 = project → price = issuance / terminalTokenUnit
|
|
424
|
-
sqrtPriceX96 =
|
|
418
|
+
sqrtPriceX96 =
|
|
419
|
+
uint160(sqrt(mulDiv({x: uint256(initialIssuance), y: 1 << 192, denominator: terminalTokenUnit})));
|
|
425
420
|
} else {
|
|
426
421
|
// token0 = project, token1 = terminal → price = terminalTokenUnit / issuance
|
|
427
|
-
sqrtPriceX96 =
|
|
422
|
+
sqrtPriceX96 =
|
|
423
|
+
uint160(sqrt(mulDiv({x: terminalTokenUnit, y: 1 << 192, denominator: uint256(initialIssuance)})));
|
|
428
424
|
}
|
|
429
425
|
}
|
|
430
426
|
|
package/src/REVLoans.sol
CHANGED
|
@@ -747,6 +747,14 @@ contract REVLoans is ERC721, ERC2771Context, JBPermissioned, Ownable, IREVLoans
|
|
|
747
747
|
address loanOwner = _ownerOf(loanId);
|
|
748
748
|
_requirePermissionFrom({account: loanOwner, projectId: revnetId, permissionId: JBPermissionIds.REALLOCATE_LOAN});
|
|
749
749
|
|
|
750
|
+
// If the caller is adding fresh holder collateral on top of the reallocated amount, they must also have
|
|
751
|
+
// OPEN_LOAN permission for the loan owner: that fresh collateral comes from the owner's project-token
|
|
752
|
+
// balance and would otherwise let a REALLOCATE_LOAN-only operator open a brand-new loan against the owner's
|
|
753
|
+
// tokens through this entry point, bypassing the OPEN_LOAN gate that `borrowFrom` enforces.
|
|
754
|
+
if (collateralCountToAdd != 0) {
|
|
755
|
+
_requirePermissionFrom({account: loanOwner, projectId: revnetId, permissionId: JBPermissionIds.OPEN_LOAN});
|
|
756
|
+
}
|
|
757
|
+
|
|
750
758
|
// Make sure the loan hasn't expired.
|
|
751
759
|
// forge-lint: disable-next-line(block-timestamp)
|
|
752
760
|
if (block.timestamp - _loanOf[loanId].createdAt > LOAN_LIQUIDATION_DURATION) {
|
package/src/REVOwner.sol
CHANGED
|
@@ -9,6 +9,7 @@ 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 {JBFees} from "@bananapus/core-v6/src/libraries/JBFees.sol";
|
|
12
13
|
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
13
14
|
import {JBAfterCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBAfterCashOutRecordedContext.sol";
|
|
14
15
|
import {JBBeforeCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforeCashOutRecordedContext.sol";
|
|
@@ -46,15 +47,6 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
|
|
|
46
47
|
error REVOwner_CashOutDelayNotFinished(uint256 cashOutDelay, uint256 blockTimestamp);
|
|
47
48
|
error REVOwner_Unauthorized(address caller, address expectedCaller);
|
|
48
49
|
|
|
49
|
-
//*********************************************************************//
|
|
50
|
-
// ------------------------- public constants ------------------------ //
|
|
51
|
-
//*********************************************************************//
|
|
52
|
-
|
|
53
|
-
/// @notice The cash out fee (as a fraction out of `JBConstants.MAX_FEE`).
|
|
54
|
-
/// @dev Cashout fees are paid to the revnet with the `FEE_REVNET_ID`.
|
|
55
|
-
/// @dev When suckers withdraw funds, they do not pay cash out fees.
|
|
56
|
-
uint256 public constant FEE = 25; // 2.5%
|
|
57
|
-
|
|
58
50
|
//*********************************************************************//
|
|
59
51
|
// --------------- public immutable stored properties ---------------- //
|
|
60
52
|
//*********************************************************************//
|
|
@@ -89,7 +81,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
|
|
|
89
81
|
|
|
90
82
|
/// @notice The deployer that manages revnet state.
|
|
91
83
|
/// @dev Set once via `setDeployer()` using the precomputed canonical REVDeployer address.
|
|
92
|
-
IREVDeployer public
|
|
84
|
+
IREVDeployer public deployer;
|
|
93
85
|
|
|
94
86
|
//*********************************************************************//
|
|
95
87
|
// -------------------- private stored properties -------------------- //
|
|
@@ -107,7 +99,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
|
|
|
107
99
|
/// @param feeRevnetId The Juicebox project ID of the fee revnet.
|
|
108
100
|
/// @param suckerRegistry The sucker registry.
|
|
109
101
|
/// @param loans The loan contract.
|
|
110
|
-
/// @param
|
|
102
|
+
/// @param deployerAddress The account allowed to bind the canonical deployer via `setDeployer`. Passed explicitly
|
|
111
103
|
/// because CREATE2 deployments set `msg.sender` to the factory, not the intended operator.
|
|
112
104
|
constructor(
|
|
113
105
|
IJBBuybackHookRegistry buybackHook,
|
|
@@ -115,14 +107,14 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
|
|
|
115
107
|
uint256 feeRevnetId,
|
|
116
108
|
IJBSuckerRegistry suckerRegistry,
|
|
117
109
|
IREVLoans loans,
|
|
118
|
-
address
|
|
110
|
+
address deployerAddress
|
|
119
111
|
) {
|
|
120
112
|
BUYBACK_HOOK = buybackHook;
|
|
121
113
|
DIRECTORY = directory;
|
|
122
114
|
FEE_REVNET_ID = feeRevnetId;
|
|
123
115
|
SUCKER_REGISTRY = suckerRegistry;
|
|
124
116
|
LOANS = loans;
|
|
125
|
-
_DEPLOYER =
|
|
117
|
+
_DEPLOYER = deployerAddress;
|
|
126
118
|
}
|
|
127
119
|
|
|
128
120
|
//*********************************************************************//
|
|
@@ -219,7 +211,7 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
|
|
|
219
211
|
// a better price. This is by design.
|
|
220
212
|
// Micro cash outs (< 40 wei at 2.5% fee) round feeCashOutCount to zero, bypassing the fee.
|
|
221
213
|
// Economically insignificant: the gas cost of the transaction far exceeds the bypassed fee. No fix needed.
|
|
222
|
-
uint256 feeCashOutCount =
|
|
214
|
+
uint256 feeCashOutCount = JBFees.standardFeeAmountFrom(context.cashOutCount);
|
|
223
215
|
uint256 nonFeeCashOutCount = context.cashOutCount - feeCashOutCount;
|
|
224
216
|
|
|
225
217
|
// Compute the gross (effective-surplus) reclaim and fee amounts. The bonding curve uses cross-chain effective
|
|
@@ -516,14 +508,14 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
|
|
|
516
508
|
/// @dev The deployer address is precomputed and supplied by the account that created this REVOwner instance.
|
|
517
509
|
/// Only that deploy-time binder may call this, which avoids an ambient public initializer where any first caller
|
|
518
510
|
/// could seize the deployer role before the deterministic REVDeployer is actually deployed.
|
|
519
|
-
/// @param
|
|
520
|
-
function setDeployer(IREVDeployer
|
|
511
|
+
/// @param newDeployer The canonical REVDeployer instance that will manage revnet runtime state.
|
|
512
|
+
function setDeployer(IREVDeployer newDeployer) external {
|
|
521
513
|
// Only the account that deployed this REVOwner may complete the one-time deployer binding.
|
|
522
514
|
if (msg.sender != _DEPLOYER) revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: _DEPLOYER});
|
|
523
515
|
// Prevent the deployer binding from being overwritten after initialization.
|
|
524
|
-
if (address(
|
|
516
|
+
if (address(deployer) != address(0)) revert REVOwner_AlreadyInitialized({deployer: address(deployer)});
|
|
525
517
|
// Store the canonical REVDeployer that is authorized to manage runtime hook state.
|
|
526
|
-
|
|
518
|
+
deployer = newDeployer;
|
|
527
519
|
}
|
|
528
520
|
|
|
529
521
|
/// @notice Store the cash out delay for a revnet.
|
|
@@ -531,8 +523,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
|
|
|
531
523
|
/// @param revnetId The ID of the revnet.
|
|
532
524
|
/// @param cashOutDelay The timestamp after which cash outs are allowed.
|
|
533
525
|
function setCashOutDelayOf(uint256 revnetId, uint256 cashOutDelay) external {
|
|
534
|
-
if (msg.sender != address(
|
|
535
|
-
revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: address(
|
|
526
|
+
if (msg.sender != address(deployer)) {
|
|
527
|
+
revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: address(deployer)});
|
|
536
528
|
}
|
|
537
529
|
cashOutDelayOf[revnetId] = cashOutDelay;
|
|
538
530
|
}
|
|
@@ -542,8 +534,8 @@ contract REVOwner is IJBRulesetDataHook, IJBCashOutHook, IJBPeerChainAdjustedAcc
|
|
|
542
534
|
/// @param revnetId The ID of the revnet.
|
|
543
535
|
/// @param hook The tiered ERC-721 hook.
|
|
544
536
|
function setTiered721HookOf(uint256 revnetId, IJB721TiersHook hook) external {
|
|
545
|
-
if (msg.sender != address(
|
|
546
|
-
revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: address(
|
|
537
|
+
if (msg.sender != address(deployer)) {
|
|
538
|
+
revert REVOwner_Unauthorized({caller: msg.sender, expectedCaller: address(deployer)});
|
|
547
539
|
}
|
|
548
540
|
tiered721HookOf[revnetId] = hook;
|
|
549
541
|
}
|
|
@@ -119,10 +119,6 @@ interface IREVDeployer {
|
|
|
119
119
|
/// @return The directory contract.
|
|
120
120
|
function DIRECTORY() external view returns (IJBDirectory);
|
|
121
121
|
|
|
122
|
-
/// @notice The cash out fee as a fraction out of `JBConstants.MAX_FEE`.
|
|
123
|
-
/// @return The fee value.
|
|
124
|
-
function FEE() external view returns (uint256);
|
|
125
|
-
|
|
126
122
|
/// @notice The Juicebox project ID of the revnet that receives cash out fees.
|
|
127
123
|
/// @return The fee revnet ID.
|
|
128
124
|
function FEE_REVNET_ID() external view returns (uint256);
|
|
@@ -11,6 +11,6 @@ interface IREVOwner {
|
|
|
11
11
|
function cashOutDelayOf(uint256 revnetId) external view returns (uint256);
|
|
12
12
|
|
|
13
13
|
/// @notice Bind the canonical deployer exactly once.
|
|
14
|
-
/// @param
|
|
15
|
-
function setDeployer(IREVDeployer
|
|
14
|
+
/// @param newDeployer The revnet deployer instance.
|
|
15
|
+
function setDeployer(IREVDeployer newDeployer) external;
|
|
16
16
|
}
|