Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add public view method getClaimableBondShares #378

Merged
merged 3 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions src/CSAccounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,13 @@ contract CSAccounting is
);
}

/// @inheritdoc ICSAccounting
function getClaimableBondShares(
uint256 nodeOperatorId
) public view returns (uint256) {
return _getClaimableBondShares(nodeOperatorId);
}

function _pullFeeRewards(
uint256 nodeOperatorId,
uint256 cumulativeFeeShares,
Expand Down
7 changes: 7 additions & 0 deletions src/interfaces/ICSAccounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ interface ICSAccounting is
uint256 nodeOperatorId
) external view returns (uint256 current, uint256 required);

/// @notice Get current claimable bond in stETH shares for the given Node Operator
/// @param nodeOperatorId ID of the Node Operator
/// @return Current claimable bond in stETH shares
function getClaimableBondShares(
uint256 nodeOperatorId
) external view returns (uint256);

/// @notice Unwrap the user's wstETH and deposit stETH to the bond for the given Node Operator
/// @dev Called by CSM exclusively
/// @param from Address to unwrap wstETH from
Expand Down
180 changes: 179 additions & 1 deletion test/CSAccounting.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2459,7 +2459,7 @@ contract CSAccountingClaimWstETHRewardsTest is
}
}

contract CSAccountingclaimRewardsUnstETHTest is
contract CSAccountingClaimRewardsUnstETHTest is
CSAccountingClaimRewardsBaseTest
{
function test_default() public override assertInvariants {
Expand Down Expand Up @@ -2870,6 +2870,184 @@ contract CSAccountingclaimRewardsUnstETHTest is
}
}

contract CSAccountingClaimableBondTest is CSAccountingRewardsBaseTest {
function test_default() public override assertInvariants {
madlabman marked this conversation as resolved.
Show resolved Hide resolved
_operator({ ongoing: 16, withdrawn: 0 });
_deposit({ bond: 32 ether });
_rewards({ fee: 0.1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertEq(
claimableBondShares,
0,
"claimable bond shares should be zero"
);
}

function test_WithCurve() public override assertInvariants {
_operator({ ongoing: 16, withdrawn: 0 });
_deposit({ bond: 32 ether });
_rewards({ fee: 0.1 ether });
_curve(defaultCurve);

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertApproxEqAbs(
claimableBondShares,
stETH.getSharesByPooledEth(15 ether),
1 wei,
"claimable bond shares should be equal to the curve discount"
);
}

function test_WithLocked() public override assertInvariants {
_operator({ ongoing: 16, withdrawn: 0 });
_deposit({ bond: 32 ether });
_rewards({ fee: 0.1 ether });
_lock({ id: 0, amount: 1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);
madlabman marked this conversation as resolved.
Show resolved Hide resolved

assertEq(
claimableBondShares,
0,
"claimable bond shares should be zero"
);
}

function test_WithCurveAndLocked() public override assertInvariants {
_operator({ ongoing: 16, withdrawn: 0 });
_deposit({ bond: 32 ether });
_rewards({ fee: 0.1 ether });
_curve(defaultCurve);
_lock({ id: 0, amount: 1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertApproxEqAbs(
claimableBondShares,
stETH.getSharesByPooledEth(14 ether),
1 wei,
"claimable bond shares should be equal to the curve discount minus locked"
);
}

function test_WithOneWithdrawnValidator() public override assertInvariants {
_operator({ ongoing: 16, withdrawn: 1 });
_deposit({ bond: 32 ether });
_rewards({ fee: 0.1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertApproxEqAbs(
claimableBondShares,
stETH.getSharesByPooledEth(2 ether),
1 wei,
"claimable bond shares should be equal to a single validator bond"
);
}

function test_WithBond() public override assertInvariants {
madlabman marked this conversation as resolved.
Show resolved Hide resolved
_operator({ ongoing: 16, withdrawn: 0 });
_deposit({ bond: 32 ether });
_rewards({ fee: 0.1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertEq(
claimableBondShares,
0,
"claimable bond shares should be zero"
);
}

function test_WithBondAndOneWithdrawnValidator()
public
override
assertInvariants
{
_operator({ ongoing: 16, withdrawn: 1 });
_deposit({ bond: 32 ether });
_rewards({ fee: 0.1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertApproxEqAbs(
claimableBondShares,
stETH.getSharesByPooledEth(2 ether),
1 wei,
"claimable bond shares should be equal to a single validator bond"
);
}

function test_WithExcessBond() public override assertInvariants {
_operator({ ongoing: 16, withdrawn: 0 });
_deposit({ bond: 33 ether });
_rewards({ fee: 0.1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertApproxEqAbs(
claimableBondShares,
stETH.getSharesByPooledEth(1 ether),
1 wei,
"claimable bond shares should be equal to the excess bond"
);
}

function test_WithExcessBondAndOneWithdrawnValidator()
public
override
assertInvariants
{
_operator({ ongoing: 16, withdrawn: 1 });
_deposit({ bond: 33 ether });
_rewards({ fee: 0.1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertApproxEqAbs(
claimableBondShares,
stETH.getSharesByPooledEth(3 ether),
1 wei,
"claimable bond shares should be equal to a single validator bond plus the excess bond"
);
}

function test_WithMissingBond() public override assertInvariants {
_operator({ ongoing: 16, withdrawn: 0 });
_deposit({ bond: 16 ether });
_rewards({ fee: 0.1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertEq(
claimableBondShares,
0,
"claimable bond shares should be zero"
);
}

function test_WithMissingBondAndOneWithdrawnValidator()
public
override
assertInvariants
{
_operator({ ongoing: 16, withdrawn: 1 });
_deposit({ bond: 16 ether });
_rewards({ fee: 0.1 ether });

uint256 claimableBondShares = accounting.getClaimableBondShares(0);

assertEq(
claimableBondShares,
0,
"claimable bond shares should be zero"
);
}
}

contract CSAccountingDepositEthTest is CSAccountingBaseTest {
function setUp() public override {
super.setUp();
Expand Down
Loading