diff --git a/chainio/clients/elcontracts/reader_test.go b/chainio/clients/elcontracts/reader_test.go index cb9e26b4..92d59164 100644 --- a/chainio/clients/elcontracts/reader_test.go +++ b/chainio/clients/elcontracts/reader_test.go @@ -7,12 +7,14 @@ import ( "github.com/Layr-Labs/eigensdk-go/chainio/clients/elcontracts" erc20 "github.com/Layr-Labs/eigensdk-go/contracts/bindings/IERC20" + rewardscoordinator "github.com/Layr-Labs/eigensdk-go/contracts/bindings/IRewardsCoordinator" "github.com/Layr-Labs/eigensdk-go/testutils" "github.com/Layr-Labs/eigensdk-go/testutils/testclients" "github.com/Layr-Labs/eigensdk-go/types" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -114,6 +116,316 @@ func TestChainReader(t *testing.T) { assert.NoError(t, err) assert.NotEmpty(t, digest) }) + + t.Run("get staker shares", func(t *testing.T) { + strategies, shares, err := read_clients.ElChainReader.GetStakerShares( + ctx, + common.HexToAddress(operator.Address), + ) + assert.NotZero(t, len(strategies), "Strategies has at least one element") + assert.NotZero(t, len(shares), "Shares has at least one element") + assert.Equal(t, len(strategies), len(shares), "Strategies has the same ammount of elements as shares") + assert.NoError(t, err) + }) + + t.Run("get delegated operator", func(t *testing.T) { + val := big.NewInt(0) + address, err := read_clients.ElChainReader.GetDelegatedOperator( + ctx, + common.HexToAddress(operator.Address), + val, + ) + + assert.NoError(t, err) + // The delegated operator of an operator is the operator itself + assert.Equal(t, address.String(), operator.Address) + }) + +} + +func TestGetCurrentClaimableDistributionRoot(t *testing.T) { + // Verifies GetCurrentClaimableDistributionRoot returns 0 if no root and the root if there's one + _, anvilHttpEndpoint := testclients.BuildTestClients(t) + ctx := context.Background() + + contractAddrs := testutils.GetContractAddressesFromContractRegistry(anvilHttpEndpoint) + + root := [32]byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + } + + rewardsCoordinatorAddr := contractAddrs.RewardsCoordinator + config := elcontracts.Config{ + DelegationManagerAddress: contractAddrs.DelegationManager, + RewardsCoordinatorAddress: rewardsCoordinatorAddr, + } + + chainReader, err := testclients.NewTestChainReaderFromConfig(anvilHttpEndpoint, config) + require.NoError(t, err) + + // Create and configure rewards coordinator + ethClient, err := ethclient.Dial(anvilHttpEndpoint) + require.NoError(t, err) + rewardsCoordinator, err := rewardscoordinator.NewContractIRewardsCoordinator(rewardsCoordinatorAddr, ethClient) + require.NoError(t, err) + + ecdsaPrivKeyHex := testutils.ANVIL_FIRST_PRIVATE_KEY + + // Set delay to zero to inmediatly operate with coordinator + receipt, err := setTestRewardsCoordinatorActivationDelay(anvilHttpEndpoint, ecdsaPrivKeyHex, uint32(0)) + require.NoError(t, err) + require.Equal(t, receipt.Status, gethtypes.ReceiptStatusSuccessful) + + // Create txManager to send transactions to the Ethereum node + txManager, err := testclients.NewTestTxManager(anvilHttpEndpoint, ecdsaPrivKeyHex) + require.NoError(t, err) + noSendTxOpts, err := txManager.GetNoSendTxOpts() + require.NoError(t, err) + + rewardsUpdater := common.HexToAddress(testutils.ANVIL_FIRST_ADDRESS) + + // Change the rewards updater to be able to submit the new root + tx, err := rewardsCoordinator.SetRewardsUpdater(noSendTxOpts, rewardsUpdater) + require.NoError(t, err) + + waitForReceipt := true + _, err = txManager.Send(context.Background(), tx, waitForReceipt) + require.NoError(t, err) + + // Check that if there is no root submitted the result is zero + distr_root, err := chainReader.GetCurrentClaimableDistributionRoot( + ctx, + ) + assert.NoError(t, err) + assert.Zero(t, distr_root.Root) + + currRewardsCalculationEndTimestamp, err := chainReader.CurrRewardsCalculationEndTimestamp(context.Background()) + require.NoError(t, err) + + tx, err = rewardsCoordinator.SubmitRoot(noSendTxOpts, root, currRewardsCalculationEndTimestamp+1) + require.NoError(t, err) + + _, err = txManager.Send(context.Background(), tx, waitForReceipt) + require.NoError(t, err) + + // Check that if there is a root submitted the result is that root + distr_root, err = chainReader.GetCurrentClaimableDistributionRoot( + ctx, + ) + assert.NoError(t, err) + assert.Equal(t, distr_root.Root, root) +} + +func TestGetRootIndexFromRootHash(t *testing.T) { + _, anvilHttpEndpoint := testclients.BuildTestClients(t) + ctx := context.Background() + + contractAddrs := testutils.GetContractAddressesFromContractRegistry(anvilHttpEndpoint) + + rewardsCoordinatorAddr := contractAddrs.RewardsCoordinator + config := elcontracts.Config{ + DelegationManagerAddress: contractAddrs.DelegationManager, + RewardsCoordinatorAddress: rewardsCoordinatorAddr, + } + + chainReader, err := testclients.NewTestChainReaderFromConfig(anvilHttpEndpoint, config) + require.NoError(t, err) + + // Create and configure rewards coordinator + ethClient, err := ethclient.Dial(anvilHttpEndpoint) + require.NoError(t, err) + rewardsCoordinator, err := rewardscoordinator.NewContractIRewardsCoordinator(rewardsCoordinatorAddr, ethClient) + require.NoError(t, err) + ecdsaPrivKeyHex := testutils.ANVIL_FIRST_PRIVATE_KEY + + // Set delay to zero to inmediatly operate with coordinator + receipt, err := setTestRewardsCoordinatorActivationDelay(anvilHttpEndpoint, ecdsaPrivKeyHex, uint32(0)) + require.NoError(t, err) + require.Equal(t, receipt.Status, gethtypes.ReceiptStatusSuccessful) + + // Create txManager to send transactions to the Ethereum node + txManager, err := testclients.NewTestTxManager(anvilHttpEndpoint, ecdsaPrivKeyHex) + require.NoError(t, err) + noSendTxOpts, err := txManager.GetNoSendTxOpts() + require.NoError(t, err) + + rewardsUpdater := common.HexToAddress(testutils.ANVIL_FIRST_ADDRESS) + + // Change the rewards updater to be able to submit the new root + tx, err := rewardsCoordinator.SetRewardsUpdater(noSendTxOpts, rewardsUpdater) + require.NoError(t, err) + + waitForReceipt := true + _, err = txManager.Send(context.Background(), tx, waitForReceipt) + require.NoError(t, err) + + root := [32]byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + } + + // Check that if there is no root submitted the result is an InvalidRoot error + root_index, err := chainReader.GetRootIndexFromHash( + ctx, + root, + ) + assert.Error(t, err) + assert.Equal(t, err.Error(), "execution reverted: custom error 0x504570e3", + "GetRootIndexFromHash should return an InvalidRoot() error", + ) + assert.Zero(t, root_index) + + currRewardsCalculationEndTimestamp, err := chainReader.CurrRewardsCalculationEndTimestamp(context.Background()) + require.NoError(t, err) + + tx, err = rewardsCoordinator.SubmitRoot(noSendTxOpts, root, currRewardsCalculationEndTimestamp+1) + require.NoError(t, err) + + _, err = txManager.Send(context.Background(), tx, waitForReceipt) + require.NoError(t, err) + + root2 := [32]byte{ + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + } + + currRewardsCalculationEndTimestamp2, err := chainReader.CurrRewardsCalculationEndTimestamp(context.Background()) + require.NoError(t, err) + + tx, err = rewardsCoordinator.SubmitRoot(noSendTxOpts, root2, currRewardsCalculationEndTimestamp2+1) + require.NoError(t, err) + + _, err = txManager.Send(context.Background(), tx, waitForReceipt) + require.NoError(t, err) + + // Check that the first root inserted is the first indexed (zero) + root_index, err = chainReader.GetRootIndexFromHash( + ctx, + root, + ) + assert.NoError(t, err) + assert.Equal(t, root_index, uint32(0)) + + // Check that the second root inserted is the second indexed (zero) + root_index, err = chainReader.GetRootIndexFromHash( + ctx, + root2, + ) + assert.NoError(t, err) + assert.Equal(t, root_index, uint32(1)) +} + +func TestGetCumulativeClaimedRewards(t *testing.T) { + clients, anvilHttpEndpoint := testclients.BuildTestClients(t) + ctx := context.Background() + + contractAddrs := testutils.GetContractAddressesFromContractRegistry(anvilHttpEndpoint) + + rewardsCoordinatorAddr := contractAddrs.RewardsCoordinator + config := elcontracts.Config{ + DelegationManagerAddress: contractAddrs.DelegationManager, + RewardsCoordinatorAddress: rewardsCoordinatorAddr, + } + privateKeyHex := testutils.ANVIL_FIRST_PRIVATE_KEY + + // Create ChainWriter + chainWriter, err := testclients.NewTestChainWriterFromConfig(anvilHttpEndpoint, privateKeyHex, config) + require.NoError(t, err) + + chainReader, err := testclients.NewTestChainReaderFromConfig(anvilHttpEndpoint, config) + require.NoError(t, err) + + activationDelay := uint32(0) + // Set activation delay to zero so that the earnings can be claimed right after submitting the root + receipt, err := setTestRewardsCoordinatorActivationDelay(anvilHttpEndpoint, privateKeyHex, activationDelay) + require.NoError(t, err) + require.True(t, receipt.Status == gethtypes.ReceiptStatusSuccessful) + + strategyAddr := contractAddrs.Erc20MockStrategy + strategy, contractUnderlyingToken, underlyingTokenAddr, err := clients.ElChainReader.GetStrategyAndUnderlyingERC20Token( + ctx, + strategyAddr, + ) + assert.NoError(t, err) + assert.NotNil(t, strategy) + assert.NotEqual(t, common.Address{}, underlyingTokenAddr) + assert.NotNil(t, contractUnderlyingToken) + + anvil_address := common.HexToAddress(testutils.ANVIL_FIRST_ADDRESS) + + // This tests that without claims result is zero + claimed, err := chainReader.GetCumulativeClaimed(ctx, anvil_address, underlyingTokenAddr) + assert.Zero(t, claimed.Cmp(big.NewInt(0))) + assert.NoError(t, err) + + cumulativeEarnings := int64(45) + claim, err := newTestClaim(chainReader, anvilHttpEndpoint, cumulativeEarnings, privateKeyHex) + require.NoError(t, err) + + receipt, err = chainWriter.ProcessClaim(context.Background(), *claim, rewardsCoordinatorAddr, true) + require.NoError(t, err) + require.True(t, receipt.Status == gethtypes.ReceiptStatusSuccessful) + + // This tests that with a claim result is cumulativeEarnings + claimed, err = chainReader.GetCumulativeClaimed(ctx, anvil_address, underlyingTokenAddr) + assert.Equal(t, claimed, big.NewInt(cumulativeEarnings)) + assert.NoError(t, err) +} + +func TestCheckClaim(t *testing.T) { + clients, anvilHttpEndpoint := testclients.BuildTestClients(t) + ctx := context.Background() + + contractAddrs := testutils.GetContractAddressesFromContractRegistry(anvilHttpEndpoint) + + rewardsCoordinatorAddr := contractAddrs.RewardsCoordinator + config := elcontracts.Config{ + DelegationManagerAddress: contractAddrs.DelegationManager, + RewardsCoordinatorAddress: rewardsCoordinatorAddr, + } + privateKeyHex := testutils.ANVIL_FIRST_PRIVATE_KEY + + // Create ChainWriter and chain reader + chainWriter, err := testclients.NewTestChainWriterFromConfig(anvilHttpEndpoint, privateKeyHex, config) + require.NoError(t, err) + + chainReader, err := testclients.NewTestChainReaderFromConfig(anvilHttpEndpoint, config) + require.NoError(t, err) + + activationDelay := uint32(0) + // Set activation delay to zero so that the earnings can be claimed right after submitting the root + receipt, err := setTestRewardsCoordinatorActivationDelay(anvilHttpEndpoint, privateKeyHex, activationDelay) + require.NoError(t, err) + require.True(t, receipt.Status == gethtypes.ReceiptStatusSuccessful) + + cumulativeEarnings := int64(45) + claim, err := newTestClaim(chainReader, anvilHttpEndpoint, cumulativeEarnings, privateKeyHex) + require.NoError(t, err) + + receipt, err = chainWriter.ProcessClaim(context.Background(), *claim, rewardsCoordinatorAddr, true) + require.NoError(t, err) + require.True(t, receipt.Status == gethtypes.ReceiptStatusSuccessful) + + strategyAddr := contractAddrs.Erc20MockStrategy + strategy, contractUnderlyingToken, underlyingTokenAddr, err := clients.ElChainReader.GetStrategyAndUnderlyingERC20Token( + ctx, + strategyAddr, + ) + assert.NoError(t, err) + assert.NotNil(t, strategy) + assert.NotEqual(t, common.Address{}, underlyingTokenAddr) + assert.NotNil(t, contractUnderlyingToken) + + checked, err := chainReader.CheckClaim(ctx, *claim) + require.NoError(t, err) + assert.True(t, checked) } func TestAdminFunctions(t *testing.T) {