Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[profile.default]
evm_version = "osaka"
optimizer = true
optimizer_runs = 200
optimizer_runs = 25
bytecode_hash = "none" # The metadata hash removed from the bytecode (not the metadata itself).
# uncomment this to inspect storage layouts in build artifacts
# extra_output = ["storageLayout"]
Expand Down
35 changes: 34 additions & 1 deletion script/curated/DeployBase.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Verifier } from "../../src/Verifier.sol";
import { ParametersRegistry } from "../../src/ParametersRegistry.sol";
import { ExitPenalties } from "../../src/ExitPenalties.sol";
import { MetaRegistry } from "../../src/MetaRegistry.sol";
import { AdditionalBondRegistry } from "../../src/AdditionalBondRegistry.sol";
import { CuratedGate } from "../../src/CuratedGate.sol";
import { MerkleGateFactory } from "../../src/MerkleGateFactory.sol";

Expand Down Expand Up @@ -58,6 +59,10 @@ struct CuratedGateConfig {
GateCurveParams params;
}

struct AdditionalBondRegistryConfig {
uint256 curveMultiplierCooldown;
}

struct CuratedDeployParams {
// Lido addresses
address lidoLocatorAddress;
Expand Down Expand Up @@ -121,6 +126,8 @@ struct CuratedDeployParams {
address resealManager;
// Testnet stuff
address secondAdminAddress;
// AdditionalBondRegistry
AdditionalBondRegistryConfig additionalBondRegistryConfig;
}

abstract contract DeployBase is Script {
Expand All @@ -143,6 +150,7 @@ abstract contract DeployBase is Script {
HashConsensus public hashConsensus;
ParametersRegistry public parametersRegistry;
MetaRegistry public metaRegistry;
AdditionalBondRegistry public additionalBondRegistry;
MerkleGateFactory public curatedGateFactory;
address[] public curatedGateInstances;
address internal curatedGateImpl;
Expand Down Expand Up @@ -231,6 +239,7 @@ abstract contract DeployBase is Script {
accounting = Accounting(_deployProxy(deployer, address(dummyImpl)));
oracle = FeeOracle(_deployProxy(deployer, address(dummyImpl)));
metaRegistry = MetaRegistry(_deployProxy(deployer, address(dummyImpl)));
additionalBondRegistry = AdditionalBondRegistry(_deployProxy(deployer, address(dummyImpl)));

FeeDistributor feeDistributorImpl = new FeeDistributor({
stETH: locator.lido(),
Expand Down Expand Up @@ -309,7 +318,10 @@ abstract contract DeployBase is Script {
moduleProxy.proxy__changeAdmin(config.proxyAdmin);
}

MetaRegistry metaRegistryImpl = new MetaRegistry(address(curatedModule));
MetaRegistry metaRegistryImpl = new MetaRegistry({
module: address(curatedModule),
additionalBondRegistry: address(additionalBondRegistry)
});

{
OssifiableProxy metaRegistryProxy = OssifiableProxy(payable(address(metaRegistry)));
Expand All @@ -320,7 +332,22 @@ abstract contract DeployBase is Script {
metaRegistryProxy.proxy__changeAdmin(config.proxyAdmin);
}

AdditionalBondRegistry additionalBondRegistryImpl = new AdditionalBondRegistry({
module: address(curatedModule),
curveMultiplierCooldown: config.additionalBondRegistryConfig.curveMultiplierCooldown
});

{
OssifiableProxy additionalBondRegistryProxy = OssifiableProxy(payable(address(additionalBondRegistry)));
additionalBondRegistryProxy.proxy__upgradeToAndCall(
address(additionalBondRegistryImpl),
abi.encodeCall(AdditionalBondRegistry.initialize, (deployer))
);
additionalBondRegistryProxy.proxy__changeAdmin(config.proxyAdmin);
}

accounting.grantRole(accounting.MANAGE_BOND_CURVES_ROLE(), address(deployer));
accounting.grantRole(accounting.SET_BOND_CURVE_MULTIPLIER_ROLE(), address(additionalBondRegistry));
metaRegistry.grantRole(metaRegistry.SET_BOND_CURVE_WEIGHT_ROLE(), deployer);

for (uint256 i = 0; i < gatesCount; i++) {
Expand Down Expand Up @@ -520,6 +547,9 @@ abstract contract DeployBase is Script {
metaRegistry.grantRole(metaRegistry.DEFAULT_ADMIN_ROLE(), config.aragonAgent);
metaRegistry.revokeRole(metaRegistry.DEFAULT_ADMIN_ROLE(), deployer);

additionalBondRegistry.grantRole(additionalBondRegistry.DEFAULT_ADMIN_ROLE(), config.aragonAgent);
additionalBondRegistry.revokeRole(additionalBondRegistry.DEFAULT_ADMIN_ROLE(), deployer);

verifier.grantRole(verifier.DEFAULT_ADMIN_ROLE(), config.aragonAgent);
verifier.revokeRole(verifier.DEFAULT_ADMIN_ROLE(), deployer);

Expand All @@ -544,6 +574,8 @@ abstract contract DeployBase is Script {
deployJson.set("CuratedModuleImpl", address(curatedModuleImpl));
deployJson.set("MetaRegistry", address(metaRegistry));
deployJson.set("MetaRegistryImpl", address(metaRegistryImpl));
deployJson.set("AdditionalBondRegistry", address(additionalBondRegistry));
deployJson.set("AdditionalBondRegistryImpl", address(additionalBondRegistryImpl));
deployJson.set("ParametersRegistry", address(parametersRegistry));
deployJson.set("ParametersRegistryImpl", address(parametersRegistryImpl));
deployJson.set("Accounting", address(accounting));
Expand Down Expand Up @@ -639,6 +671,7 @@ abstract contract DeployBase is Script {
hashConsensus.grantRole(hashConsensus.DEFAULT_ADMIN_ROLE(), config.secondAdminAddress);
parametersRegistry.grantRole(parametersRegistry.DEFAULT_ADMIN_ROLE(), config.secondAdminAddress);
metaRegistry.grantRole(metaRegistry.DEFAULT_ADMIN_ROLE(), config.secondAdminAddress);
additionalBondRegistry.grantRole(additionalBondRegistry.DEFAULT_ADMIN_ROLE(), config.secondAdminAddress);
for (uint256 i = 0; i < curatedGateInstances.length; i++) {
CuratedGate gate = CuratedGate(curatedGateInstances[i]);
gate.grantRole(gate.DEFAULT_ADMIN_ROLE(), config.secondAdminAddress);
Expand Down
6 changes: 5 additions & 1 deletion script/curated/DeployHoodi.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

pragma solidity 0.8.33;

import { DeployBase, CuratedGateConfig } from "./DeployBase.s.sol";
import { DeployBase, CuratedGateConfig, AdditionalBondRegistryConfig } from "./DeployBase.s.sol";
import { GIndices } from "../constants/GIndices.sol";

contract DeployHoodi is DeployBase {
Expand Down Expand Up @@ -198,6 +198,10 @@ contract DeployHoodi is DeployBase {
config.resealManager = 0x05172CbCDb7307228F781436b327679e4DAE166B;

config.secondAdminAddress = 0x4AF43Ee34a6fcD1fEcA1e1F832124C763561dA53; // Dev team EOA

// CurveMultiplier
config.additionalBondRegistryConfig.curveMultiplierCooldown = 7 days;

_setUp();
}
}
6 changes: 5 additions & 1 deletion script/curated/DeployLocalDevNet.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

pragma solidity 0.8.33;

import { DeployBase, CuratedGateConfig } from "./DeployBase.s.sol";
import { DeployBase, CuratedGateConfig, AdditionalBondRegistryConfig } from "./DeployBase.s.sol";
import { GIndices } from "../constants/GIndices.sol";

contract DeployLocalDevNet is DeployBase {
Expand Down Expand Up @@ -31,6 +31,7 @@ contract DeployLocalDevNet is DeployBase {
config.gIFirstWithdrawal = GIndices.FIRST_WITHDRAWAL_ELECTRA;
config.gIFirstValidator = GIndices.FIRST_VALIDATOR_ELECTRA;
config.gIFirstHistoricalSummary = GIndices.FIRST_HISTORICAL_SUMMARY_ELECTRA; // prettier-ignore
config.gIFirstBalanceNode = GIndices.FIRST_BALANCE_NODE_ELECTRA;
config.verifierFirstSupportedSlot = vm.envUint("DEVNET_ELECTRA_EPOCH") * config.slotsPerEpoch;
config.capellaSlot = vm.envUint("DEVNET_CAPELLA_EPOCH") * config.slotsPerEpoch;
config.minWithdrawalRatio = 9950;
Expand Down Expand Up @@ -186,6 +187,9 @@ contract DeployLocalDevNet is DeployBase {

config.secondAdminAddress = vm.envOr("CSM_SECOND_ADMIN_ADDRESS", address(0));

// CurveMultiplier
config.additionalBondRegistryConfig.curveMultiplierCooldown = 1 days;

_setUp();
}
}
6 changes: 5 additions & 1 deletion script/curated/DeployMainnet.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

pragma solidity 0.8.33;

import { DeployBase, CuratedGateConfig } from "./DeployBase.s.sol";
import { DeployBase, CuratedGateConfig, AdditionalBondRegistryConfig } from "./DeployBase.s.sol";
import { GIndices } from "../constants/GIndices.sol";

contract DeployMainnet is DeployBase {
Expand Down Expand Up @@ -193,6 +193,10 @@ contract DeployMainnet is DeployBase {

// DG
config.resealManager = 0x7914b5a1539b97Bd0bbd155757F25FD79A522d24;

// CurveMultiplier
config.additionalBondRegistryConfig.curveMultiplierCooldown = 7 days;

_setUp();
}
}
84 changes: 61 additions & 23 deletions src/Accounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ contract Accounting is

bytes32 public constant MANAGE_BOND_CURVES_ROLE = keccak256("MANAGE_BOND_CURVES_ROLE");
bytes32 public constant SET_BOND_CURVE_ROLE = keccak256("SET_BOND_CURVE_ROLE");
bytes32 public constant SET_BOND_CURVE_MULTIPLIER_ROLE = keccak256("SET_BOND_CURVE_MULTIPLIER_ROLE");
IBaseModule public immutable MODULE;
IFeeDistributor public immutable FEE_DISTRIBUTOR;

Expand Down Expand Up @@ -153,6 +154,16 @@ contract Accounting is
MODULE.updateDepositInfo(nodeOperatorId);
}

/// @inheritdoc IAccounting
function setBondCurveMultiplier(
uint256 nodeOperatorId,
uint256 multiplier
) external onlyRole(SET_BOND_CURVE_MULTIPLIER_ROLE) {
_onlyExistingNodeOperator(nodeOperatorId);
BondCurve._setBondCurveMultiplier(nodeOperatorId, multiplier);
MODULE.updateDepositInfo(nodeOperatorId);
}

/// @inheritdoc IAccounting
function depositETH(address from, uint256 nodeOperatorId) external payable whenResumed onlyModule {
BondCore._depositETH(from, nodeOperatorId);
Expand Down Expand Up @@ -277,12 +288,6 @@ contract Accounting is
released = true;
}

/// @inheritdoc IAccounting
function unlockExpiredLock(uint256 nodeOperatorId) public {
BondLock._unlockExpiredLock(nodeOperatorId);
MODULE.updateDepositableValidatorsCount(nodeOperatorId);
}

/// @inheritdoc IAccounting
function compensateLockedBond(uint256 nodeOperatorId) external onlyModule returns (uint256 compensatedAmount) {
uint256 lockedAmount = BondLock.getLockedBond(nodeOperatorId);
Expand Down Expand Up @@ -405,6 +410,15 @@ contract Accounting is
return _sharesByEth(getRequiredBondForNextKeys(nodeOperatorId, additionalKeys));
}

/// @inheritdoc IAccounting
function getRequiredBondForNextKeysWstETH(
uint256 nodeOperatorId,
uint256 additionalKeys,
uint256 multiplier
) external view returns (uint256) {
return _sharesByEth(getRequiredBondForNextKeys(nodeOperatorId, additionalKeys, multiplier));
}

/// @inheritdoc IAccounting
function getClaimableBondShares(uint256 nodeOperatorId) external view returns (uint256) {
return _getClaimableBondShares(nodeOperatorId);
Expand All @@ -431,28 +445,50 @@ contract Accounting is
/// @inheritdoc IAccounting
function getNodeOperatorBondInfo(uint256 nodeOperatorId) external view returns (NodeOperatorBondInfo memory info) {
Comment thread
dgusakov marked this conversation as resolved.
info.currentBond = BondCore.getBond(nodeOperatorId);
info.requiredBond = _getRequiredBond(nodeOperatorId, 0);
info.requiredBond = _getRequiredBond(nodeOperatorId, 0, BondCurve.getBondCurveMultiplier(nodeOperatorId));
info.lockedBond = BondLock.getLockedBond(nodeOperatorId);
info.bondDebt = BondCore.getBondDebt(nodeOperatorId);
info.pendingSharesToSplit = FeeSplits.getPendingSharesToSplit(nodeOperatorId);
}

/// @inheritdoc IAccounting
function unlockExpiredLock(uint256 nodeOperatorId) public {
BondLock._unlockExpiredLock(nodeOperatorId);
MODULE.updateDepositableValidatorsCount(nodeOperatorId);
}

/// @inheritdoc IAccounting
function getBondSummary(uint256 nodeOperatorId) public view returns (uint256 current, uint256 required) {
current = BondCore.getBond(nodeOperatorId);
required = _getRequiredBond(nodeOperatorId, 0);
required = _getRequiredBond(nodeOperatorId, 0, BondCurve.getBondCurveMultiplier(nodeOperatorId));
}

/// @inheritdoc IAccounting
function getBondSummaryShares(uint256 nodeOperatorId) public view returns (uint256 current, uint256 required) {
current = BondCore.getBondShares(nodeOperatorId);
required = _getRequiredBondShares(nodeOperatorId, 0);
required = _sharesByEth(_getRequiredBond(nodeOperatorId, 0, BondCurve.getBondCurveMultiplier(nodeOperatorId)));
}

/// @inheritdoc IAccounting
function getRequiredBondForNextKeys(uint256 nodeOperatorId, uint256 additionalKeys) public view returns (uint256) {
uint256 current = BondCore.getBond(nodeOperatorId);
uint256 totalRequired = _getRequiredBond(nodeOperatorId, additionalKeys);
uint256 totalRequired = _getRequiredBond(
nodeOperatorId,
additionalKeys,
BondCurve.getBondCurveMultiplier(nodeOperatorId)
);

return Math.saturatingSub(totalRequired, current);
}

/// @inheritdoc IAccounting
function getRequiredBondForNextKeys(
uint256 nodeOperatorId,
uint256 additionalKeys,
uint256 multiplier
) public view returns (uint256) {
uint256 current = BondCore.getBond(nodeOperatorId);
uint256 totalRequired = _getRequiredBond(nodeOperatorId, additionalKeys, multiplier);

return Math.saturatingSub(totalRequired, current);
}
Expand Down Expand Up @@ -526,18 +562,19 @@ contract Accounting is
return Math.saturatingSub(currentShares, requiredShares);
}

function _getRequiredBond(uint256 nodeOperatorId, uint256 additionalKeys) internal view returns (uint256) {
uint256 curveId = BondCurve.getBondCurveId(nodeOperatorId);
uint256 nonWithdrawnKeys = MODULE.getNodeOperatorNonWithdrawnKeys(nodeOperatorId);
uint256 requiredBondForKeys = BondCurve.getBondAmountByKeysCount(nonWithdrawnKeys + additionalKeys, curveId);
uint256 lockedBond = BondLock.getLockedBond(nodeOperatorId);
uint256 bondDebt = BondCore.getBondDebt(nodeOperatorId);

return requiredBondForKeys + lockedBond + bondDebt;
}

function _getRequiredBondShares(uint256 nodeOperatorId, uint256 additionalKeys) internal view returns (uint256) {
return _sharesByEth(_getRequiredBond(nodeOperatorId, additionalKeys));
function _getRequiredBond(
uint256 nodeOperatorId,
uint256 additionalKeys,
uint256 mul
) internal view returns (uint256) {
return
BondCurve.getBondAmountByKeysCount(
MODULE.getNodeOperatorNonWithdrawnKeys(nodeOperatorId) + additionalKeys,
BondCurve.getBondCurveId(nodeOperatorId),
mul
) +
BondLock.getLockedBond(nodeOperatorId) +
BondCore.getBondDebt(nodeOperatorId);
}

/// @dev Unbonded stands for the amount of keys not fully covered with bond
Expand All @@ -564,7 +601,8 @@ contract Accounting is
// Should be sufficient for ~ 40 years
uint256 bondedKeys = BondCurve.getKeysCountByBondAmount(
currentBond + 10 wei,
BondCurve.getBondCurveId(nodeOperatorId)
BondCurve.getBondCurveId(nodeOperatorId),
BondCurve.getBondCurveMultiplier(nodeOperatorId)
);
return Math.saturatingSub(nonWithdrawnKeys, bondedKeys);
}
Expand Down
Loading
Loading