Skip to content

Commit 800ef7a

Browse files
authored
Add permissioned rebase (#2889)
* Add permissioned rebase * Remove public rebase
1 parent 3de581a commit 800ef7a

29 files changed

Lines changed: 356 additions & 110 deletions

brownie/addresses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@
213213

214214
## Safe Modules
215215
ETHEREUM_BRIDGE_HELPER_MODULE = "0x630C1763D38AbE76301F58909fa174E7B84A7ECD"
216-
BASE_BRIDGE_HELPER_MODULE = "0x362DBD4Ff662b2E2b05b9cEDC91da2Dd2c655b26"
216+
BASE_BRIDGE_HELPER_MODULE = "0xe3B3b4Fc77505EcfAACf6dD21619a8Cc12fcc501"
217217
PLUME_BRIDGE_HELPER_MODULE = "0xAc58C88349e00509FEc216E1B61d13b43315E18D"
218218

219219
## Morpho V2 (Base) Crosschain strategy

contracts/contracts/interfaces/IVault.sol

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ interface IVault {
1818
event RebaseUnpaused();
1919
event VaultBufferUpdated(uint256 _vaultBuffer);
2020
event AllocateThresholdUpdated(uint256 _threshold);
21-
event RebaseThresholdUpdated(uint256 _threshold);
2221
event StrategistUpdated(address _address);
2322
event MaxSupplyDiffChanged(uint256 maxSupplyDiff);
2423
event YieldDistribution(address _to, uint256 _yield, uint256 _fee);
@@ -58,14 +57,14 @@ interface IVault {
5857

5958
function autoAllocateThreshold() external view returns (uint256);
6059

61-
function setRebaseThreshold(uint256 _threshold) external;
62-
63-
function rebaseThreshold() external view returns (uint256);
64-
6560
function setStrategistAddr(address _address) external;
6661

6762
function strategistAddr() external view returns (address);
6863

64+
function setOperatorAddr(address _operator) external;
65+
66+
function operatorAddr() external view returns (address);
67+
6968
function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;
7069

7170
function maxSupplyDiff() external view returns (uint256);

contracts/contracts/vault/VaultAdmin.sol

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,6 @@ abstract contract VaultAdmin is VaultCore {
6363
emit AllocateThresholdUpdated(_threshold);
6464
}
6565

66-
/**
67-
* @notice Set a minimum amount of OTokens in a mint or redeem that triggers a
68-
* rebase
69-
* @param _threshold OToken amount with 18 fixed decimals.
70-
*/
71-
function setRebaseThreshold(uint256 _threshold) external onlyGovernor {
72-
rebaseThreshold = _threshold;
73-
emit RebaseThresholdUpdated(_threshold);
74-
}
75-
7666
/**
7767
* @notice Set address of Strategist
7868
* @param _address Address of Strategist
@@ -82,6 +72,16 @@ abstract contract VaultAdmin is VaultCore {
8272
emit StrategistUpdated(_address);
8373
}
8474

75+
/**
76+
* @notice Set the address authorized to call `rebase()`.
77+
* @param _operator New operator address. May be set to the zero address
78+
* to disable operator-initiated rebases.
79+
*/
80+
function setOperatorAddr(address _operator) external onlyGovernor {
81+
operatorAddr = _operator;
82+
emit OperatorUpdated(_operator);
83+
}
84+
8585
/**
8686
* @notice Set the default Strategy for asset, i.e. the one which
8787
* the asset will be automatically allocated to and withdrawn from

contracts/contracts/vault/VaultCore.sol

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,6 @@ abstract contract VaultCore is VaultInitializer {
7979

8080
emit Mint(msg.sender, scaledAmount);
8181

82-
// Rebase must happen before any transfers occur.
83-
if (!rebasePaused && scaledAmount >= rebaseThreshold) {
84-
_rebase();
85-
}
86-
8782
// Mint oTokens
8883
oToken.mint(msg.sender, scaledAmount);
8984

@@ -218,7 +213,7 @@ abstract contract VaultCore is VaultInitializer {
218213
oToken.burn(msg.sender, _amount);
219214

220215
// Prevent withdrawal if the vault is solvent by more than the allowed percentage
221-
_postRedeem(_amount);
216+
_postRedeem();
222217

223218
emit WithdrawalRequested(msg.sender, requestId, _amount, queued);
224219
}
@@ -262,7 +257,7 @@ abstract contract VaultCore is VaultInitializer {
262257
IERC20(asset).safeTransfer(msg.sender, amount);
263258

264259
// Prevent insolvency
265-
_postRedeem(amount.scaleBy(18, assetDecimals));
260+
_postRedeem();
266261
}
267262

268263
// slither-disable-end reentrancy-no-eth
@@ -303,7 +298,7 @@ abstract contract VaultCore is VaultInitializer {
303298
IERC20(asset).safeTransfer(msg.sender, totalAmount);
304299

305300
// Prevent insolvency
306-
_postRedeem(totalAmount.scaleBy(18, assetDecimals));
301+
_postRedeem();
307302

308303
return (amounts, totalAmount);
309304
}
@@ -341,17 +336,12 @@ abstract contract VaultCore is VaultInitializer {
341336
return StableMath.scaleBy(request.amount, assetDecimals, 18);
342337
}
343338

344-
function _postRedeem(uint256 _amount) internal {
339+
function _postRedeem() internal view {
345340
// Until we can prove that we won't affect the prices of our asset
346341
// by withdrawing them, this should be here.
347342
// It's possible that a strategy was off on its asset total, perhaps
348343
// a reward token sold for more or for less than anticipated.
349-
uint256 totalUnits = 0;
350-
if (_amount >= rebaseThreshold && !rebasePaused) {
351-
totalUnits = _rebase();
352-
} else {
353-
totalUnits = _totalValue();
354-
}
344+
uint256 totalUnits = _totalValue();
355345

356346
// Check that the OTokens are backed by enough asset
357347
if (maxSupplyDiff > 0) {
@@ -420,8 +410,15 @@ abstract contract VaultCore is VaultInitializer {
420410
/**
421411
* @notice Calculate the total value of asset held by the Vault and all
422412
* strategies and update the supply of OTokens.
413+
* @dev Restricted to the Operator, Strategist or Governor.
423414
*/
424415
function rebase() external virtual nonReentrant {
416+
require(
417+
msg.sender == operatorAddr ||
418+
msg.sender == strategistAddr ||
419+
isGovernor(),
420+
"Caller not authorized"
421+
);
425422
_rebase();
426423
}
427424

contracts/contracts/vault/VaultInitializer.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ abstract contract VaultInitializer is VaultStorage {
2424
vaultBuffer = 0;
2525
// Initial allocate threshold of 25,000 OUSD
2626
autoAllocateThreshold = 25000e18;
27-
// Threshold for rebasing
28-
rebaseThreshold = 1000e18;
2927
// Initialize all strategies
3028
allStrategies = new address[](0);
3129
// Start with drip duration: 7 days

contracts/contracts/vault/VaultStorage.sol

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ abstract contract VaultStorage is Initializable, Governable {
3333
event RebaseUnpaused();
3434
event VaultBufferUpdated(uint256 _vaultBuffer);
3535
event AllocateThresholdUpdated(uint256 _threshold);
36-
event RebaseThresholdUpdated(uint256 _threshold);
3736
event StrategistUpdated(address _address);
3837
event MaxSupplyDiffChanged(uint256 maxSupplyDiff);
3938
event YieldDistribution(address _to, uint256 _yield, uint256 _fee);
@@ -43,6 +42,7 @@ abstract contract VaultStorage is Initializable, Governable {
4342
event StrategyRemovedFromMintWhitelist(address indexed strategy);
4443
event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond);
4544
event DripDurationChanged(uint256 dripDuration);
45+
event OperatorUpdated(address newOperator);
4646
event WithdrawalRequested(
4747
address indexed _withdrawer,
4848
uint256 indexed _requestId,
@@ -91,8 +91,9 @@ abstract contract VaultStorage is Initializable, Governable {
9191
uint256 public vaultBuffer;
9292
/// @notice OToken mints over this amount automatically allocate funds. 18 decimals.
9393
uint256 public autoAllocateThreshold;
94-
/// @notice OToken mints over this amount automatically rebase. 18 decimals.
95-
uint256 public rebaseThreshold;
94+
/// @dev Deprecated. Was the auto-rebase trigger threshold for mint/redeem.
95+
/// Storage slot retained for proxy compatibility; no longer read or written.
96+
uint256 internal __deprecatedRebaseThreshold;
9697

9798
/// @dev Address of the OToken token. eg OUSD or OETH.
9899
OUSD public oToken;
@@ -202,8 +203,12 @@ abstract contract VaultStorage is Initializable, Governable {
202203
/// @notice Default strategy for asset
203204
address public defaultStrategy;
204205

206+
/// @notice Address authorized to call `rebase()` directly. The Governor
207+
/// and Strategist are always allowed in addition to this address.
208+
address public operatorAddr;
209+
205210
// For future use
206-
uint256[42] private __gap;
211+
uint256[41] private __gap;
207212

208213
/// @notice Index of WETH asset in allAssets array
209214
/// Legacy OETHVaultCore code, relocated here for vault consistency.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const { deployOnBase } = require("../../utils/deploy-l2");
2+
const { deployWithConfirmation } = require("../../utils/deploy");
3+
const addresses = require("../../utils/addresses");
4+
5+
module.exports = deployOnBase(
6+
{
7+
deployName: "049_vault_permissioned_rebase",
8+
},
9+
async ({ ethers }) => {
10+
// 1. Deploy new OETHBaseVault implementation
11+
const dOETHbVault = await deployWithConfirmation(
12+
"OETHBaseVault",
13+
[addresses.base.WETH],
14+
"OETHBaseVault",
15+
true
16+
);
17+
18+
const cOETHbVaultProxy = await ethers.getContract("OETHBaseVaultProxy");
19+
const cOETHbVault = await ethers.getContractAt(
20+
"IVault",
21+
cOETHbVaultProxy.address
22+
);
23+
24+
return {
25+
name: "Upgrade OETHBaseVault: permissioned rebase, drop auto-rebase triggers",
26+
actions: [
27+
{
28+
contract: cOETHbVaultProxy,
29+
signature: "upgradeTo(address)",
30+
args: [dOETHbVault.address],
31+
},
32+
{
33+
contract: cOETHbVault,
34+
signature: "setOperatorAddr(address)",
35+
args: [addresses.multichainStrategist],
36+
},
37+
],
38+
};
39+
}
40+
);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const addresses = require("../../utils/addresses");
2+
const { deploymentWithGovernanceProposal } = require("../../utils/deploy");
3+
4+
module.exports = deploymentWithGovernanceProposal(
5+
{
6+
deployName: "193_vault_permissioned_rebase",
7+
forceDeploy: false,
8+
reduceQueueTime: true,
9+
deployerIsProposer: false,
10+
proposalId: "",
11+
},
12+
async ({ deployWithConfirmation }) => {
13+
// 1. Deploy new OUSD Vault implementation
14+
const dOUSDVault = await deployWithConfirmation("OUSDVault", [
15+
addresses.mainnet.USDC,
16+
]);
17+
18+
// 2. Deploy new OETH Vault implementation
19+
const dOETHVault = await deployWithConfirmation("OETHVault", [
20+
addresses.mainnet.WETH,
21+
]);
22+
23+
const cVaultProxy = await ethers.getContract("VaultProxy");
24+
const cOUSDVault = await ethers.getContractAt(
25+
"IVault",
26+
cVaultProxy.address
27+
);
28+
29+
const cOETHVaultProxy = await ethers.getContract("OETHVaultProxy");
30+
const cOETHVault = await ethers.getContractAt(
31+
"IVault",
32+
cOETHVaultProxy.address
33+
);
34+
35+
return {
36+
name: "Upgrade OUSD and OETH vaults: permissioned rebase, drop auto-rebase triggers",
37+
actions: [
38+
{
39+
contract: cVaultProxy,
40+
signature: "upgradeTo(address)",
41+
args: [dOUSDVault.address],
42+
},
43+
{
44+
contract: cOUSDVault,
45+
signature: "setOperatorAddr(address)",
46+
args: [addresses.multichainStrategist],
47+
},
48+
{
49+
contract: cOETHVaultProxy,
50+
signature: "upgradeTo(address)",
51+
args: [dOETHVault.address],
52+
},
53+
{
54+
contract: cOETHVault,
55+
signature: "setOperatorAddr(address)",
56+
args: [addresses.multichainStrategist],
57+
},
58+
],
59+
};
60+
}
61+
);

contracts/deploy/plume/002_core.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,10 @@ module.exports = deployOnPlume(
178178
signature: "setWithdrawalClaimDelay(uint256)",
179179
args: [24 * 60 * 60],
180180
},
181-
{
182-
// Set rebase threshold
183-
contract: cOETHpVault,
184-
signature: "setRebaseThreshold(uint256)",
185-
// TODO: Change this after Vault is fixed
186-
args: [parseUnits("10000", 18)], // 10000 OETHp
187-
},
181+
// setRebaseThreshold removed: rebaseThreshold is deprecated and the
182+
// setter no longer exists on the vault. This script has already run
183+
// on Plume mainnet so the action is preserved here as a comment for
184+
// historical context only.
188185
{
189186
// Set strategist
190187
contract: cOETHpVault,

contracts/deploy/plume/005_vault_config.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const { deployOnPlume } = require("../../utils/deploy-l2");
2-
const { parseUnits } = require("ethers/lib/utils.js");
32

43
module.exports = deployOnPlume(
54
{
@@ -28,12 +27,8 @@ module.exports = deployOnPlume(
2827
signature: "setTrusteeFeeBps(uint256)",
2928
args: [2000], // 20%
3029
},
31-
{
32-
// Set rebase threshold
33-
contract: cOETHpVault,
34-
signature: "setRebaseThreshold(uint256)",
35-
args: [parseUnits("1", 18)], // 1 OETHp
36-
},
30+
// setRebaseThreshold removed: rebaseThreshold deprecated and the
31+
// setter no longer exists on the vault. OETP is winding down.
3732
],
3833
};
3934
}

0 commit comments

Comments
 (0)