Skip to content

Commit 7a5292a

Browse files
committed
remaining cross-cluster and staking explicit-EB flows, and update the vUnits scenario
1 parent 468762e commit 7a5292a

2 files changed

Lines changed: 410 additions & 0 deletions

File tree

test/integration/SSVNetwork/staking.test.ts

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,159 @@ describe("SSVNetwork Integration - Staking (Enhanced)", () => {
730730
});
731731
});
732732

733+
describe("Explicit EB staking revenue checks", async function() {
734+
it("liquidating an explicit EB=64 cluster stops further staking revenue accrual", async function() {
735+
const { network, views, ssvToken } = await networkHelpers.loadFixture(deployFullSSVNetworkFixture);
736+
737+
await ssvToken.mint(staker.address, STAKE_AMOUNT);
738+
await ssvToken.connect(staker).approve(await network.getAddress(), STAKE_AMOUNT);
739+
await network.connect(staker).stake(STAKE_AMOUNT);
740+
741+
const operatorIds = await registerOperators(network, operatorOwner, 4);
742+
await whitelistAddresses(network, operatorOwner, operatorIds, [clusterOwner.address]);
743+
await network.connect(clusterOwner).registerValidator(
744+
makePublicKey(9101),
745+
operatorIds,
746+
DEFAULT_SHARES,
747+
EMPTY_CLUSTER,
748+
{ value: DEFAULT_ETH_REGISTER_VALUE }
749+
);
750+
751+
const allSigners = await connection.ethers.getSigners();
752+
const oracles = allSigners.slice(10, 14);
753+
for (let i = 0; i < 4; i++) {
754+
await network.replaceOracle(i + 1, oracles[i].address);
755+
}
756+
757+
const clusterId = computeClusterId(clusterOwner.address, operatorIds);
758+
const ebBlock = Number(await connection.ethers.provider.getBlockNumber());
759+
const { root, proofs } = generateMerkleForClusterEB(connection, [
760+
{ clusterId, effectiveBalance: 64 },
761+
]);
762+
await commitEBRoot(network, root, ebBlock, oracles);
763+
764+
const clusterBeforeUpdate = await getCurrentClusterState(connection, network, clusterOwner.address, operatorIds);
765+
await network.updateClusterBalance(
766+
ebBlock,
767+
clusterOwner.address,
768+
operatorIds.map((id) => BigInt(id)),
769+
{
770+
validatorCount: Number(clusterBeforeUpdate.validatorCount),
771+
networkFeeIndex: BigInt(clusterBeforeUpdate.networkFeeIndex),
772+
index: BigInt(clusterBeforeUpdate.index),
773+
active: clusterBeforeUpdate.active,
774+
balance: BigInt(clusterBeforeUpdate.balance),
775+
},
776+
64,
777+
proofs[clusterId],
778+
);
779+
780+
const earningsBefore = await views.getNetworkEarnings();
781+
await connection.networkHelpers.mine(100n);
782+
const earningsBeforeLiquidation = await views.getNetworkEarnings();
783+
const preLiquidationDelta = earningsBeforeLiquidation - earningsBefore;
784+
expect(preLiquidationDelta).to.be.greaterThan(0n);
785+
786+
const clusterBeforeLiquidation = await getCurrentClusterState(connection, network, clusterOwner.address, operatorIds);
787+
await network.connect(clusterOwner).liquidate(
788+
clusterOwner.address,
789+
operatorIds,
790+
{
791+
validatorCount: Number(clusterBeforeLiquidation.validatorCount),
792+
networkFeeIndex: BigInt(clusterBeforeLiquidation.networkFeeIndex),
793+
index: BigInt(clusterBeforeLiquidation.index),
794+
active: clusterBeforeLiquidation.active,
795+
balance: BigInt(clusterBeforeLiquidation.balance),
796+
},
797+
);
798+
799+
const earningsImmediatelyAfterLiquidation = await views.getNetworkEarnings();
800+
await connection.networkHelpers.mine(100n);
801+
const earningsAfterLiquidation = await views.getNetworkEarnings();
802+
const postLiquidationDelta = earningsAfterLiquidation - earningsImmediatelyAfterLiquidation;
803+
expect(postLiquidationDelta).to.equal(0n);
804+
});
805+
806+
it("staking revenue doubles when explicit EB increases from 64 to 128", async function() {
807+
const { network, views, ssvToken } = await networkHelpers.loadFixture(deployFullSSVNetworkFixture);
808+
809+
await ssvToken.mint(staker.address, STAKE_AMOUNT);
810+
await ssvToken.connect(staker).approve(await network.getAddress(), STAKE_AMOUNT);
811+
await network.connect(staker).stake(STAKE_AMOUNT);
812+
813+
const operatorIds = await registerOperators(network, operatorOwner, 4);
814+
await whitelistAddresses(network, operatorOwner, operatorIds, [clusterOwner.address]);
815+
await network.connect(clusterOwner).registerValidator(
816+
makePublicKey(9102),
817+
operatorIds,
818+
DEFAULT_SHARES,
819+
EMPTY_CLUSTER,
820+
{ value: DEFAULT_ETH_REGISTER_VALUE }
821+
);
822+
823+
const allSigners = await connection.ethers.getSigners();
824+
const oracles = allSigners.slice(10, 14);
825+
for (let i = 0; i < 4; i++) {
826+
await network.replaceOracle(i + 1, oracles[i].address);
827+
}
828+
829+
await network.updateMinBlocksBetweenUpdates(1n);
830+
831+
const clusterId = computeClusterId(clusterOwner.address, operatorIds);
832+
const eb64Block = Number(await connection.ethers.provider.getBlockNumber());
833+
const merkle64 = generateMerkleForClusterEB(connection, [{ clusterId, effectiveBalance: 64 }]);
834+
await commitEBRoot(network, merkle64.root, eb64Block, oracles);
835+
const clusterBefore64 = await getCurrentClusterState(connection, network, clusterOwner.address, operatorIds);
836+
await network.updateClusterBalance(
837+
eb64Block,
838+
clusterOwner.address,
839+
operatorIds.map((id) => BigInt(id)),
840+
{
841+
validatorCount: Number(clusterBefore64.validatorCount),
842+
networkFeeIndex: BigInt(clusterBefore64.networkFeeIndex),
843+
index: BigInt(clusterBefore64.index),
844+
active: clusterBefore64.active,
845+
balance: BigInt(clusterBefore64.balance),
846+
},
847+
64,
848+
merkle64.proofs[clusterId],
849+
);
850+
851+
const earningsBefore64 = await views.getNetworkEarnings();
852+
await connection.networkHelpers.mine(100n);
853+
const earningsAfter64 = await views.getNetworkEarnings();
854+
const delta64 = earningsAfter64 - earningsBefore64;
855+
856+
await connection.networkHelpers.mine(1n);
857+
const eb128Block = Number(await connection.ethers.provider.getBlockNumber());
858+
const merkle128 = generateMerkleForClusterEB(connection, [{ clusterId, effectiveBalance: 128 }]);
859+
await commitEBRoot(network, merkle128.root, eb128Block, oracles);
860+
const clusterBefore128 = await getCurrentClusterState(connection, network, clusterOwner.address, operatorIds);
861+
await network.updateClusterBalance(
862+
eb128Block,
863+
clusterOwner.address,
864+
operatorIds.map((id) => BigInt(id)),
865+
{
866+
validatorCount: Number(clusterBefore128.validatorCount),
867+
networkFeeIndex: BigInt(clusterBefore128.networkFeeIndex),
868+
index: BigInt(clusterBefore128.index),
869+
active: clusterBefore128.active,
870+
balance: BigInt(clusterBefore128.balance),
871+
},
872+
128,
873+
merkle128.proofs[clusterId],
874+
);
875+
876+
const earningsBefore128 = await views.getNetworkEarnings();
877+
await connection.networkHelpers.mine(100n);
878+
const earningsAfter128 = await views.getNetworkEarnings();
879+
const delta128 = earningsAfter128 - earningsBefore128;
880+
881+
expect(delta64).to.be.greaterThan(0n);
882+
expect(delta128).to.equal(delta64 * 2n);
883+
});
884+
});
885+
733886
describe("Multisig Accounts", async function() {
734887

735888
it("Multisig contract stakes SSV tokens", async function() {

0 commit comments

Comments
 (0)