Skip to main content

CSAccounting

CSAccounting.sol is a supplementary contract responsible for the management of bond, rewards, and penalties. It stores bond tokens as stETH shares, provides information about the bond required, and provides interfaces for the penalties. Node Operators claim rewards and top-up bonds using this contract.

Changes in v2:

  • User-facing methods for reward claims and bond top-ups are moved to CSAccounting.sol from CSModule.sol;
  • Public methods to get claimable bond amounts and rewards are added;

Upgradability

The contract uses OssifiableProxy for upgradability.

State Variables

PAUSE_ROLE

bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE");

RESUME_ROLE

bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE");

MANAGE_BOND_CURVES_ROLE

bytes32 public constant MANAGE_BOND_CURVES_ROLE = keccak256("MANAGE_BOND_CURVES_ROLE");

SET_BOND_CURVE_ROLE

bytes32 public constant SET_BOND_CURVE_ROLE = keccak256("SET_BOND_CURVE_ROLE");

RECOVERER_ROLE

bytes32 public constant RECOVERER_ROLE = keccak256("RECOVERER_ROLE");

MODULE

ICSModule public immutable MODULE;

FEE_DISTRIBUTOR

ICSFeeDistributor public immutable FEE_DISTRIBUTOR;

LIDO_LOCATOR

ILidoLocator public immutable LIDO_LOCATOR;

LIDO

ILido public immutable LIDO;

WITHDRAWAL_QUEUE

IWithdrawalQueue public immutable WITHDRAWAL_QUEUE;

WSTETH

IWstETH public immutable WSTETH;

MIN_CURVE_LENGTH

uint256 public constant MIN_CURVE_LENGTH = 1;

DEFAULT_BOND_CURVE_ID

uint256 public constant DEFAULT_BOND_CURVE_ID = 0;

MAX_CURVE_LENGTH

uint256 public constant MAX_CURVE_LENGTH = 100;

MIN_BOND_LOCK_PERIOD

uint256 public immutable MIN_BOND_LOCK_PERIOD;

MAX_BOND_LOCK_PERIOD

uint256 public immutable MAX_BOND_LOCK_PERIOD;

chargePenaltyRecipient

address public chargePenaltyRecipient;

Functions

resume

Resume reward claims and deposits

function resume() external onlyRole(RESUME_ROLE);

pauseFor

Pause reward claims and deposits for duration seconds

Must be called together with CSModule.pauseFor

function pauseFor(uint256 duration) external onlyRole(PAUSE_ROLE);

Parameters

NameTypeDescription
durationuint256Duration of the pause in seconds

setChargePenaltyRecipient

Set charge recipient address

function setChargePenaltyRecipient(address _chargePenaltyRecipient) external onlyRole(DEFAULT_ADMIN_ROLE);

Parameters

NameTypeDescription
_chargePenaltyRecipientaddressCharge recipient address

setBondLockPeriod

Set bond lock period

function setBondLockPeriod(uint256 period) external onlyRole(DEFAULT_ADMIN_ROLE);

Parameters

NameTypeDescription
perioduint256Period in seconds to retain bond lock

addBondCurve

Add a new bond curve

function addBondCurve(BondCurveIntervalInput[] calldata bondCurve)
external
onlyRole(MANAGE_BOND_CURVES_ROLE)
returns (uint256 id);

Parameters

NameTypeDescription
bondCurveBondCurveIntervalInput[]Bond curve definition to add

Returns

NameTypeDescription
iduint256Id of the added curve

updateBondCurve

Update existing bond curve

If the curve is updated to a curve with higher values for any point, Extensive checks and actions should be performed by the method caller to avoid inconsistency in the keys accounting. A manual update of the depositable validators count in CSM might be required to ensure that the keys pointers are consistent.

function updateBondCurve(uint256 curveId, BondCurveIntervalInput[] calldata bondCurve)
external
onlyRole(MANAGE_BOND_CURVES_ROLE);

Parameters

NameTypeDescription
curveIduint256Bond curve ID to update
bondCurveBondCurveIntervalInput[]Bond curve definition

setBondCurve

Set the bond curve for the given Node Operator

Updates depositable validators count in CSM to ensure key pointers consistency

function setBondCurve(uint256 nodeOperatorId, uint256 curveId) external onlyRole(SET_BOND_CURVE_ROLE);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
curveIduint256ID of the bond curve to set

depositETH

Stake user's ETH with Lido and deposit stETH to the bond

Called by CSM exclusively. CSM should check node operator existence and update depositable validators count

function depositETH(address from, uint256 nodeOperatorId) external payable whenResumed onlyModule;

Parameters

NameTypeDescription
fromaddressAddress to stake ETH and deposit stETH from
nodeOperatorIduint256ID of the Node Operator

depositETH

Stake user's ETH with Lido and deposit stETH to the bond

Called by CSM exclusively. CSM should check node operator existence and update depositable validators count

function depositETH(uint256 nodeOperatorId) external payable whenResumed;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

depositStETH

Deposit user's stETH to the bond for the given Node Operator

Called by CSM exclusively. CSM should check node operator existence and update depositable validators count

function depositStETH(address from, uint256 nodeOperatorId, uint256 stETHAmount, PermitInput calldata permit)
external
whenResumed
onlyModule;

Parameters

NameTypeDescription
fromaddressAddress to deposit stETH from.
nodeOperatorIduint256ID of the Node Operator
stETHAmountuint256Amount of stETH to deposit
permitPermitInputstETH permit for the contract

depositStETH

Deposit user's stETH to the bond for the given Node Operator

Called by CSM exclusively. CSM should check node operator existence and update depositable validators count

function depositStETH(uint256 nodeOperatorId, uint256 stETHAmount, PermitInput calldata permit) external whenResumed;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
stETHAmountuint256Amount of stETH to deposit
permitPermitInputstETH permit for the contract

depositWstETH

Unwrap the user's wstETH and deposit stETH to the bond for the given Node Operator

Called by CSM exclusively. CSM should check node operator existence and update depositable validators count

function depositWstETH(address from, uint256 nodeOperatorId, uint256 wstETHAmount, PermitInput calldata permit)
external
whenResumed
onlyModule;

Parameters

NameTypeDescription
fromaddressAddress to unwrap wstETH from
nodeOperatorIduint256ID of the Node Operator
wstETHAmountuint256Amount of wstETH to deposit
permitPermitInputwstETH permit for the contract

depositWstETH

Unwrap the user's wstETH and deposit stETH to the bond for the given Node Operator

Called by CSM exclusively. CSM should check node operator existence and update depositable validators count

function depositWstETH(uint256 nodeOperatorId, uint256 wstETHAmount, PermitInput calldata permit)
external
whenResumed;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
wstETHAmountuint256Amount of wstETH to deposit
permitPermitInputwstETH permit for the contract

claimRewardsStETH

Claim full reward (fee + bond) in stETH for the given Node Operator with desirable value. rewardsProof and cumulativeFeeShares might be empty in order to claim only excess bond

It's impossible to use single-leaf proof via this method, so this case should be treated carefully by off-chain tooling, e.g. to make sure a tree has at least 2 leafs.

function claimRewardsStETH(
uint256 nodeOperatorId,
uint256 stETHAmount,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external whenResumed returns (uint256 claimedShares);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
stETHAmountuint256Amount of stETH to claim
cumulativeFeeSharesuint256Cumulative fee stETH shares for the Node Operator
rewardsProofbytes32[]Merkle proof of the rewards

Returns

NameTypeDescription
claimedSharesuint256shares Amount of stETH shares claimed

claimRewardsWstETH

Claim full reward (fee + bond) in wstETH for the given Node Operator available for this moment. rewardsProof and cumulativeFeeShares might be empty in order to claim only excess bond

It's impossible to use single-leaf proof via this method, so this case should be treated carefully by off-chain tooling, e.g. to make sure a tree has at least 2 leafs.

function claimRewardsWstETH(
uint256 nodeOperatorId,
uint256 wstETHAmount,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external whenResumed returns (uint256 claimedWstETH);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
wstETHAmountuint256Amount of wstETH to claim
cumulativeFeeSharesuint256Cumulative fee stETH shares for the Node Operator
rewardsProofbytes32[]Merkle proof of the rewards

Returns

NameTypeDescription
claimedWstETHuint256claimedWstETHAmount Amount of wstETH claimed

claimRewardsUnstETH

Request full reward (fee + bond) in Withdrawal NFT (unstETH) for the given Node Operator available for this moment. rewardsProof and cumulativeFeeShares might be empty in order to claim only excess bond

Reverts if amount isn't between MIN_STETH_WITHDRAWAL_AMOUNT and MAX_STETH_WITHDRAWAL_AMOUNT

function claimRewardsUnstETH(
uint256 nodeOperatorId,
uint256 stETHAmount,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external whenResumed returns (uint256 requestId);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
stETHAmountuint256Amount of ETH to request
cumulativeFeeSharesuint256Cumulative fee stETH shares for the Node Operator
rewardsProofbytes32[]Merkle proof of the rewards

Returns

NameTypeDescription
requestIduint256Withdrawal NFT ID

lockBondETH

Lock bond in ETH for the given Node Operator

Called by CSM exclusively

function lockBondETH(uint256 nodeOperatorId, uint256 amount) external onlyModule;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
amountuint256Amount to lock in ETH (stETH)

releaseLockedBondETH

Release locked bond in ETH for the given Node Operator

Called by CSM exclusively

function releaseLockedBondETH(uint256 nodeOperatorId, uint256 amount) external onlyModule;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
amountuint256Amount to release in ETH (stETH)

compensateLockedBondETH

Compensate locked bond ETH for the given Node Operator

Called by CSM exclusively

function compensateLockedBondETH(uint256 nodeOperatorId) external payable onlyModule;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

settleLockedBondETH

Settle locked bond ETH for the given Node Operator

Called by CSM exclusively

function settleLockedBondETH(uint256 nodeOperatorId) external onlyModule returns (bool applied);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

penalize

Penalize bond by burning stETH shares of the given Node Operator

Penalty application has a priority over the locked bond. Method call can result in the remaining bond being lower than the locked bond.

function penalize(uint256 nodeOperatorId, uint256 amount) external onlyModule;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
amountuint256Amount to penalize in ETH (stETH)

chargeFee

Charge fee from bond by transferring stETH shares of the given Node Operator to the charge recipient

Charge confiscation has a priority over the locked bond. Method call can result in the remaining bond being lower than the locked bond.

function chargeFee(uint256 nodeOperatorId, uint256 amount) external onlyModule;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
amountuint256Amount to charge in ETH (stETH)

pullFeeRewards

Pull fees from CSFeeDistributor to the Node Operator's bond

Permissionless method. Can be called before penalty application to ensure that rewards are also penalized

function pullFeeRewards(uint256 nodeOperatorId, uint256 cumulativeFeeShares, bytes32[] calldata rewardsProof)
external;

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
cumulativeFeeSharesuint256Cumulative fee stETH shares for the Node Operator
rewardsProofbytes32[]Merkle proof of the rewards

recoverERC20

Allows sender to recover ERC20 tokens held by the contract

function recoverERC20(address token, uint256 amount) external override;

Parameters

NameTypeDescription
tokenaddressThe address of the ERC20 token to recover
amountuint256The amount of the ERC20 token to recover Emits an ERC20Recovered event upon success Optionally, the inheriting contract can override this function to add additional restrictions

recoverStETHShares

Recover all stETH shares from the contract

Accounts for the bond funds stored during recovery

function recoverStETHShares() external;

renewBurnerAllowance

Service method to update allowance to Burner in case it has changed

function renewBurnerAllowance() external;

getInitializedVersion

Get the initialized version of the contract

function getInitializedVersion() external view returns (uint64);

totalBondShares

Get total bond shares (stETH) stored on the contract

function totalBondShares() public view returns (uint256);

Returns

NameTypeDescription
<none>uint256Total bond shares (stETH)

getBondShares

Get bond shares (stETH) for the given Node Operator

function getBondShares(uint256 nodeOperatorId) public view returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>uint256Bond in stETH shares

getBond

Get bond amount in ETH (stETH) for the given Node Operator

function getBond(uint256 nodeOperatorId) public view returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>uint256Bond amount in ETH (stETH)

getCurvesCount

function getCurvesCount() external view returns (uint256);

getCurveInfo

Return bond curve for the given curve id

Reverts if curveId is invalid

function getCurveInfo(uint256 curveId) external view returns (BondCurve memory);

Parameters

NameTypeDescription
curveIduint256Curve id to get bond curve for

Returns

NameTypeDescription
<none>BondCurveBond curve

getBondCurve

Get bond curve for the given Node Operator

function getBondCurve(uint256 nodeOperatorId) external view returns (BondCurve memory);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>BondCurveBond curve

getBondCurveId

Get bond curve ID for the given Node Operator

function getBondCurveId(uint256 nodeOperatorId) public view returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>uint256Bond curve ID

getBondAmountByKeysCount

Get required bond in ETH for the given number of keys for default bond curve

To calculate the amount for the new keys 2 calls are required: getBondAmountByKeysCount(newTotal) - getBondAmountByKeysCount(currentTotal)

function getBondAmountByKeysCount(uint256 keys, uint256 curveId) public view returns (uint256);

Parameters

NameTypeDescription
keysuint256Number of keys to get required bond for
curveIduint256Id of the curve to perform calculations against

Returns

NameTypeDescription
<none>uint256Amount for particular keys count

getKeysCountByBondAmount

Get keys count for the given bond amount with default bond curve

function getKeysCountByBondAmount(uint256 amount, uint256 curveId) public view returns (uint256);

Parameters

NameTypeDescription
amountuint256Bond amount in ETH (stETH)to get keys count for
curveIduint256Id of the curve to perform calculations against

Returns

NameTypeDescription
<none>uint256Keys count

getBondLockPeriod

Get default bond lock period

function getBondLockPeriod() external view returns (uint256);

Returns

NameTypeDescription
<none>uint256period Default bond lock period

getLockedBondInfo

Get information about the locked bond for the given Node Operator

function getLockedBondInfo(uint256 nodeOperatorId) external view returns (BondLock memory);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>BondLockLocked bond info

getActualLockedBond

Get amount of the locked bond in ETH (stETH) by the given Node Operator

function getActualLockedBond(uint256 nodeOperatorId) public view returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>uint256Amount of the actual locked bond

getBondSummary

Get current and required bond amounts in ETH (stETH) for the given Node Operator

To calculate excess bond amount subtract required from current value. To calculate missed bond amount subtract current from required value

function getBondSummary(uint256 nodeOperatorId) external view returns (uint256 current, uint256 required);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
currentuint256Current bond amount in ETH
requireduint256Required bond amount in ETH

getUnbondedKeysCount

Get the number of the unbonded keys

function getUnbondedKeysCount(uint256 nodeOperatorId) external view returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>uint256Unbonded keys count

getUnbondedKeysCountToEject

Get the number of the unbonded keys to be ejected using a forcedTargetLimit Locked bond is not considered for this calculation to allow Node Operators to compensate the locked bond via compensateLockedBondETH method before the ejection happens

function getUnbondedKeysCountToEject(uint256 nodeOperatorId) external view returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>uint256Unbonded keys count

getBondAmountByKeysCountWstETH

Get the bond amount in wstETH required for the keysCount keys using the default bond curve

function getBondAmountByKeysCountWstETH(uint256 keysCount, uint256 curveId) external view returns (uint256);

Parameters

NameTypeDescription
keysCountuint256Keys count to calculate the required bond amount
curveIduint256Id of the curve to perform calculations against

Returns

NameTypeDescription
<none>uint256wstETH amount required for the keysCount

getRequiredBondForNextKeysWstETH

Get the required bond in wstETH (inc. missed and excess) for the given Node Operator to upload new keys

function getRequiredBondForNextKeysWstETH(uint256 nodeOperatorId, uint256 additionalKeys)
external
view
returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
additionalKeysuint256Number of new keys to add

Returns

NameTypeDescription
<none>uint256Required bond in wstETH

getClaimableBondShares

Get current claimable bond in stETH shares for the given Node Operator

function getClaimableBondShares(uint256 nodeOperatorId) external view returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
<none>uint256Current claimable bond in stETH shares

getClaimableRewardsAndBondShares

Get current claimable bond in stETH shares for the given Node Operator Includes potential rewards distributed by the Fee Distributor

function getClaimableRewardsAndBondShares(
uint256 nodeOperatorId,
uint256 cumulativeFeeShares,
bytes32[] calldata rewardsProof
) external view returns (uint256 claimableShares);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
cumulativeFeeSharesuint256Cumulative fee stETH shares for the Node Operator
rewardsProofbytes32[]Merkle proof of the rewards

Returns

NameTypeDescription
claimableSharesuint256Current claimable bond in stETH shares

feeDistributor

function feeDistributor() external view returns (ICSFeeDistributor);

getBondSummaryShares

Get current and required bond amounts in stETH shares for the given Node Operator

To calculate excess bond amount subtract required from current value. To calculate missed bond amount subtract current from required value

function getBondSummaryShares(uint256 nodeOperatorId) public view returns (uint256 current, uint256 required);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator

Returns

NameTypeDescription
currentuint256Current bond amount in stETH shares
requireduint256Required bond amount in stETH shares

getRequiredBondForNextKeys

Get the required bond in ETH (inc. missed and excess) for the given Node Operator to upload new deposit data

function getRequiredBondForNextKeys(uint256 nodeOperatorId, uint256 additionalKeys) public view returns (uint256);

Parameters

NameTypeDescription
nodeOperatorIduint256ID of the Node Operator
additionalKeysuint256Number of new keys to add

Returns

NameTypeDescription
<none>uint256Required bond amount in ETH

Events

BondLockCompensated

event BondLockCompensated(uint256 indexed nodeOperatorId, uint256 amount);

ChargePenaltyRecipientSet

event ChargePenaltyRecipientSet(address chargePenaltyRecipient);

BondDepositedETH

event BondDepositedETH(uint256 indexed nodeOperatorId, address from, uint256 amount);

BondDepositedStETH

event BondDepositedStETH(uint256 indexed nodeOperatorId, address from, uint256 amount);

BondDepositedWstETH

event BondDepositedWstETH(uint256 indexed nodeOperatorId, address from, uint256 amount);

BondClaimedUnstETH

event BondClaimedUnstETH(uint256 indexed nodeOperatorId, address to, uint256 amount, uint256 requestId);

BondClaimedStETH

event BondClaimedStETH(uint256 indexed nodeOperatorId, address to, uint256 amount);

BondClaimedWstETH

event BondClaimedWstETH(uint256 indexed nodeOperatorId, address to, uint256 amount);

BondBurned

event BondBurned(uint256 indexed nodeOperatorId, uint256 amountToBurn, uint256 burnedAmount);

BondCharged

event BondCharged(uint256 indexed nodeOperatorId, uint256 toChargeAmount, uint256 chargedAmount);

BondCurveAdded

event BondCurveAdded(uint256 indexed curveId, BondCurveIntervalInput[] bondCurveIntervals);

BondCurveUpdated

event BondCurveUpdated(uint256 indexed curveId, BondCurveIntervalInput[] bondCurveIntervals);

BondCurveSet

event BondCurveSet(uint256 indexed nodeOperatorId, uint256 curveId);

BondLockChanged

event BondLockChanged(uint256 indexed nodeOperatorId, uint256 newAmount, uint256 until);

BondLockRemoved

event BondLockRemoved(uint256 indexed nodeOperatorId);

BondLockPeriodChanged

event BondLockPeriodChanged(uint256 period);