From a64c72042cdcfb12bf9bb1102548ee90b2e25b73 Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Fri, 31 Jan 2025 11:31:17 -0300 Subject: [PATCH 1/8] Create OperatorRequest and OperatorRegisterResponse structs --- chainio/clients/elcontracts/reader.go | 16 ++++++++++++---- chainio/clients/elcontracts/types.go | 12 ++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/chainio/clients/elcontracts/reader.go b/chainio/clients/elcontracts/reader.go index 1f46ab3d7..06be2ee63 100644 --- a/chainio/clients/elcontracts/reader.go +++ b/chainio/clients/elcontracts/reader.go @@ -93,13 +93,21 @@ func NewReaderFromConfig( func (r *ChainReader) IsOperatorRegistered( ctx context.Context, - operator types.Operator, -) (bool, error) { + request OperatorRequest, +) (OperatorRegisterResponse, error) { if r.delegationManager == nil { - return false, errors.New("DelegationManager contract not provided") + return OperatorRegisterResponse{}, errors.New("DelegationManager contract not provided") + } + + isRegistered, err := r.delegationManager.IsOperator( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + gethcommon.HexToAddress(request.Operator.Address), + ) + if err != nil { + return OperatorRegisterResponse{}, utils.WrapError("failed to check if operator is registered", err) } - return r.delegationManager.IsOperator(&bind.CallOpts{Context: ctx}, gethcommon.HexToAddress(operator.Address)) + return OperatorRegisterResponse{IsRegistered: isRegistered}, nil } // GetStakerShares returns the amount of shares that a staker has in all of the strategies in which they have nonzero diff --git a/chainio/clients/elcontracts/types.go b/chainio/clients/elcontracts/types.go index 21dd8ea4e..6cd4407b5 100644 --- a/chainio/clients/elcontracts/types.go +++ b/chainio/clients/elcontracts/types.go @@ -5,6 +5,7 @@ import ( allocationmanager "github.com/Layr-Labs/eigensdk-go/contracts/bindings/AllocationManager" "github.com/Layr-Labs/eigensdk-go/crypto/bls" + "github.com/Layr-Labs/eigensdk-go/types" "github.com/ethereum/go-ethereum/common" ) @@ -81,3 +82,14 @@ type RemovePendingAdminRequest struct { AdminAddress common.Address WaitForReceipt bool } + +// READER TYPES + +type OperatorRequest struct { + BlockNumber *big.Int + Operator types.Operator +} + +type OperatorRegisterResponse struct { + IsRegistered bool +} From 3edabbc3c5741a02d8a40b5350a767a98036aba1 Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Fri, 31 Jan 2025 11:45:18 -0300 Subject: [PATCH 2/8] Create req-res structs --- chainio/clients/elcontracts/reader.go | 63 +++++++++++++++++---------- chainio/clients/elcontracts/types.go | 22 +++++++++- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/chainio/clients/elcontracts/reader.go b/chainio/clients/elcontracts/reader.go index 06be2ee63..ab8c550d0 100644 --- a/chainio/clients/elcontracts/reader.go +++ b/chainio/clients/elcontracts/reader.go @@ -101,8 +101,7 @@ func (r *ChainReader) IsOperatorRegistered( isRegistered, err := r.delegationManager.IsOperator( &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, - gethcommon.HexToAddress(request.Operator.Address), - ) + request.OperatorAddress) if err != nil { return OperatorRegisterResponse{}, utils.WrapError("failed to check if operator is registered", err) } @@ -114,52 +113,70 @@ func (r *ChainReader) IsOperatorRegistered( // shares func (r *ChainReader) GetStakerShares( ctx context.Context, - stakerAddress gethcommon.Address, -) ([]gethcommon.Address, []*big.Int, error) { + request StakerRequest, +) (StakerSharesResponse, error) { if r.delegationManager == nil { - return nil, nil, errors.New("DelegationManager contract not provided") + return StakerSharesResponse{}, errors.New("DelegationManager contract not provided") + } + + strategies, shares, err := r.delegationManager.GetDepositedShares( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.StakerAddress, + ) + if err != nil { + return StakerSharesResponse{}, utils.WrapError("failed to get staker shares", err) } - return r.delegationManager.GetDepositedShares(&bind.CallOpts{Context: ctx}, stakerAddress) + + return StakerSharesResponse{StrategiesAddresses: strategies, Shares: shares}, nil } // GetDelegatedOperator returns the operator that a staker has delegated to func (r *ChainReader) GetDelegatedOperator( ctx context.Context, - stakerAddress gethcommon.Address, - blockNumber *big.Int, -) (gethcommon.Address, error) { + request StakerRequest, +) (DelegateOperatorResponse, error) { if r.delegationManager == nil { - return gethcommon.Address{}, errors.New("DelegationManager contract not provided") + return DelegateOperatorResponse{}, errors.New("DelegationManager contract not provided") + } + + operatorAddress, err := r.delegationManager.DelegatedTo( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.StakerAddress, + ) + if err != nil { + return DelegateOperatorResponse{}, utils.WrapError("failed to get delegated operator", err) } - return r.delegationManager.DelegatedTo(&bind.CallOpts{Context: ctx}, stakerAddress) + + return DelegateOperatorResponse{OperatorAddress: operatorAddress}, nil } func (r *ChainReader) GetOperatorDetails( ctx context.Context, - operator types.Operator, -) (types.Operator, error) { + request OperatorRequest, +) (OperatorResponse, error) { if r.delegationManager == nil { - return types.Operator{}, errors.New("DelegationManager contract not provided") + return OperatorResponse{}, errors.New("DelegationManager contract not provided") } delegationManagerAddress, err := r.delegationManager.DelegationApprover( &bind.CallOpts{Context: ctx}, - gethcommon.HexToAddress(operator.Address), + request.OperatorAddress, ) // This call should not fail since it's a getter if err != nil { - return types.Operator{}, err + return OperatorResponse{}, err } isSet, delay, err := r.allocationManager.GetAllocationDelay( &bind.CallOpts{ - Context: ctx, + Context: ctx, + BlockNumber: request.BlockNumber, }, - gethcommon.HexToAddress(operator.Address), + request.OperatorAddress, ) // This call should not fail if err != nil { - return types.Operator{}, err + return OperatorResponse{}, err } var allocationDelay uint32 @@ -169,11 +186,13 @@ func (r *ChainReader) GetOperatorDetails( allocationDelay = 0 } - return types.Operator{ - Address: operator.Address, + operatorDetails := types.Operator{ + Address: request.OperatorAddress.Hex(), DelegationApproverAddress: delegationManagerAddress.Hex(), AllocationDelay: allocationDelay, - }, nil + } + + return OperatorResponse{Operator: operatorDetails}, nil } // GetStrategyAndUnderlyingToken returns the strategy contract and the underlying token address diff --git a/chainio/clients/elcontracts/types.go b/chainio/clients/elcontracts/types.go index 6cd4407b5..2685e195e 100644 --- a/chainio/clients/elcontracts/types.go +++ b/chainio/clients/elcontracts/types.go @@ -86,10 +86,28 @@ type RemovePendingAdminRequest struct { // READER TYPES type OperatorRequest struct { - BlockNumber *big.Int - Operator types.Operator + BlockNumber *big.Int + OperatorAddress common.Address +} + +type OperatorResponse struct { + Operator types.Operator } type OperatorRegisterResponse struct { IsRegistered bool } + +type StakerRequest struct { + BlockNumber *big.Int + StakerAddress common.Address +} + +type StakerSharesResponse struct { + StrategiesAddresses []common.Address + Shares []*big.Int +} + +type DelegateOperatorResponse struct { + OperatorAddress common.Address +} From 5515315f8fc8cc17af3edb4019bc740675523a9e Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Fri, 31 Jan 2025 11:57:57 -0300 Subject: [PATCH 3/8] Implement new interface in methods --- chainio/clients/elcontracts/reader.go | 55 ++++++++++++++++----------- chainio/clients/elcontracts/types.go | 28 ++++++++++++++ 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/chainio/clients/elcontracts/reader.go b/chainio/clients/elcontracts/reader.go index ab8c550d0..d85cc84d4 100644 --- a/chainio/clients/elcontracts/reader.go +++ b/chainio/clients/elcontracts/reader.go @@ -198,57 +198,68 @@ func (r *ChainReader) GetOperatorDetails( // GetStrategyAndUnderlyingToken returns the strategy contract and the underlying token address func (r *ChainReader) GetStrategyAndUnderlyingToken( ctx context.Context, - strategyAddr gethcommon.Address, -) (*strategy.ContractIStrategy, gethcommon.Address, error) { - contractStrategy, err := strategy.NewContractIStrategy(strategyAddr, r.ethClient) + request StrategyRequest, +) (StrategyTokenResponse, error) { + contractStrategy, err := strategy.NewContractIStrategy(request.StrategyAddress, r.ethClient) // This call should not fail since it's an init if err != nil { - return nil, gethcommon.Address{}, utils.WrapError("Failed to fetch strategy contract", err) + return StrategyTokenResponse{}, utils.WrapError("Failed to fetch strategy contract", err) } - underlyingTokenAddr, err := contractStrategy.UnderlyingToken(&bind.CallOpts{Context: ctx}) + underlyingTokenAddr, err := contractStrategy.UnderlyingToken( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + ) if err != nil { - return nil, gethcommon.Address{}, utils.WrapError("Failed to fetch token contract", err) + return StrategyTokenResponse{}, utils.WrapError("Failed to fetch token contract", err) } - return contractStrategy, underlyingTokenAddr, nil + return StrategyTokenResponse{StrategyContract: *contractStrategy, TokenAddress: underlyingTokenAddr}, nil } // GetStrategyAndUnderlyingERC20Token returns the strategy contract, the erc20 bindings for the underlying token // and the underlying token address func (r *ChainReader) GetStrategyAndUnderlyingERC20Token( ctx context.Context, - strategyAddr gethcommon.Address, -) (*strategy.ContractIStrategy, erc20.ContractIERC20Methods, gethcommon.Address, error) { - contractStrategy, err := strategy.NewContractIStrategy(strategyAddr, r.ethClient) + request StrategyRequest, +) (StrategyERC20TokenResponse, error) { + contractStrategy, err := strategy.NewContractIStrategy(request.StrategyAddress, r.ethClient) // This call should not fail since it's an init if err != nil { - return nil, nil, gethcommon.Address{}, utils.WrapError("Failed to fetch strategy contract", err) + return StrategyERC20TokenResponse{}, utils.WrapError("Failed to fetch strategy contract", err) } underlyingTokenAddr, err := contractStrategy.UnderlyingToken(&bind.CallOpts{Context: ctx}) if err != nil { - return nil, nil, gethcommon.Address{}, utils.WrapError("Failed to fetch token contract", err) + return StrategyERC20TokenResponse{}, utils.WrapError("Failed to fetch token contract", err) } contractUnderlyingToken, err := erc20.NewContractIERC20(underlyingTokenAddr, r.ethClient) // This call should not fail, if the strategy does not have an underlying token then it would enter the if above if err != nil { - return nil, nil, gethcommon.Address{}, utils.WrapError("Failed to fetch token contract", err) + return StrategyERC20TokenResponse{}, utils.WrapError("Failed to fetch token contract", err) } - return contractStrategy, contractUnderlyingToken, underlyingTokenAddr, nil + + return StrategyERC20TokenResponse{ + StrategyContract: *contractStrategy, + ERC20Bindings: contractUnderlyingToken, + TokenAddress: underlyingTokenAddr, + }, nil } func (r *ChainReader) GetOperatorSharesInStrategy( ctx context.Context, - operatorAddr gethcommon.Address, - strategyAddr gethcommon.Address, -) (*big.Int, error) { + request SharesInStrategyRequest, +) (SharesResponse, error) { if r.delegationManager == nil { - return &big.Int{}, errors.New("DelegationManager contract not provided") + return SharesResponse{}, errors.New("DelegationManager contract not provided") } - return r.delegationManager.OperatorShares( - &bind.CallOpts{Context: ctx}, - operatorAddr, - strategyAddr, + shares, err := r.delegationManager.OperatorShares( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + request.OperatorAddress, + request.StrategyAddress, ) + if err != nil { + return SharesResponse{}, utils.WrapError("failed to get operator shares", err) + } + + return SharesResponse{Shares: shares}, nil } func (r *ChainReader) CalculateDelegationApprovalDigestHash( diff --git a/chainio/clients/elcontracts/types.go b/chainio/clients/elcontracts/types.go index 2685e195e..85a847385 100644 --- a/chainio/clients/elcontracts/types.go +++ b/chainio/clients/elcontracts/types.go @@ -4,6 +4,8 @@ import ( "math/big" allocationmanager "github.com/Layr-Labs/eigensdk-go/contracts/bindings/AllocationManager" + erc20 "github.com/Layr-Labs/eigensdk-go/contracts/bindings/IERC20" + strategy "github.com/Layr-Labs/eigensdk-go/contracts/bindings/IStrategy" "github.com/Layr-Labs/eigensdk-go/crypto/bls" "github.com/Layr-Labs/eigensdk-go/types" @@ -94,6 +96,22 @@ type OperatorResponse struct { Operator types.Operator } +type StrategyRequest struct { + BlockNumber *big.Int + StrategyAddress common.Address +} + +type StrategyTokenResponse struct { + StrategyContract strategy.ContractIStrategy + TokenAddress common.Address +} + +type StrategyERC20TokenResponse struct { + StrategyContract strategy.ContractIStrategy + ERC20Bindings erc20.ContractIERC20Methods + TokenAddress common.Address +} + type OperatorRegisterResponse struct { IsRegistered bool } @@ -111,3 +129,13 @@ type StakerSharesResponse struct { type DelegateOperatorResponse struct { OperatorAddress common.Address } + +type SharesInStrategyRequest struct { + BlockNumber *big.Int + OperatorAddress common.Address + StrategyAddress common.Address +} + +type SharesResponse struct { + Shares *big.Int +} From 15bf080a8ba3c3dceac5669e99c2c343565e9384 Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Fri, 31 Jan 2025 12:06:10 -0300 Subject: [PATCH 4/8] Fix conflicts in other files --- chainio/clients/elcontracts/reader.go | 6 +++--- chainio/clients/elcontracts/types.go | 6 +++--- chainio/clients/elcontracts/writer.go | 14 ++++++-------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/chainio/clients/elcontracts/reader.go b/chainio/clients/elcontracts/reader.go index d85cc84d4..cbe94b9af 100644 --- a/chainio/clients/elcontracts/reader.go +++ b/chainio/clients/elcontracts/reader.go @@ -236,9 +236,9 @@ func (r *ChainReader) GetStrategyAndUnderlyingERC20Token( } return StrategyERC20TokenResponse{ - StrategyContract: *contractStrategy, - ERC20Bindings: contractUnderlyingToken, - TokenAddress: underlyingTokenAddr, + StrategyContract: *contractStrategy, + UnderlyingERC20Contract: contractUnderlyingToken, + UnderlyingTokenAddress: underlyingTokenAddr, }, nil } diff --git a/chainio/clients/elcontracts/types.go b/chainio/clients/elcontracts/types.go index 85a847385..0e64cd22f 100644 --- a/chainio/clients/elcontracts/types.go +++ b/chainio/clients/elcontracts/types.go @@ -107,9 +107,9 @@ type StrategyTokenResponse struct { } type StrategyERC20TokenResponse struct { - StrategyContract strategy.ContractIStrategy - ERC20Bindings erc20.ContractIERC20Methods - TokenAddress common.Address + StrategyContract strategy.ContractIStrategy + UnderlyingERC20Contract erc20.ContractIERC20Methods + UnderlyingTokenAddress common.Address } type OperatorRegisterResponse struct { diff --git a/chainio/clients/elcontracts/writer.go b/chainio/clients/elcontracts/writer.go index fc95ee6c9..466a6a1dc 100644 --- a/chainio/clients/elcontracts/writer.go +++ b/chainio/clients/elcontracts/writer.go @@ -18,9 +18,7 @@ import ( avsdirectory "github.com/Layr-Labs/eigensdk-go/contracts/bindings/AVSDirectory" allocationmanager "github.com/Layr-Labs/eigensdk-go/contracts/bindings/AllocationManager" delegationmanager "github.com/Layr-Labs/eigensdk-go/contracts/bindings/DelegationManager" - erc20 "github.com/Layr-Labs/eigensdk-go/contracts/bindings/IERC20" rewardscoordinator "github.com/Layr-Labs/eigensdk-go/contracts/bindings/IRewardsCoordinator" - strategy "github.com/Layr-Labs/eigensdk-go/contracts/bindings/IStrategy" permissioncontroller "github.com/Layr-Labs/eigensdk-go/contracts/bindings/PermissionController" regcoord "github.com/Layr-Labs/eigensdk-go/contracts/bindings/RegistryCoordinator" strategymanager "github.com/Layr-Labs/eigensdk-go/contracts/bindings/StrategyManager" @@ -33,8 +31,8 @@ import ( type Reader interface { GetStrategyAndUnderlyingERC20Token( - ctx context.Context, strategyAddr gethcommon.Address, - ) (*strategy.ContractIStrategy, erc20.ContractIERC20Methods, gethcommon.Address, error) + ctx context.Context, request StrategyRequest, + ) (StrategyERC20TokenResponse, error) } type ChainWriter struct { @@ -242,15 +240,15 @@ func (w *ChainWriter) DepositERC20IntoStrategy( if err != nil { return nil, err } - _, underlyingTokenContract, underlyingTokenAddr, err := w.elChainReader.GetStrategyAndUnderlyingERC20Token( + response, err := w.elChainReader.GetStrategyAndUnderlyingERC20Token( ctx, - strategyAddr, + StrategyRequest{StrategyAddress: strategyAddr}, ) if err != nil { return nil, err } - tx, err := underlyingTokenContract.Approve(noSendTxOpts, w.strategyManagerAddr, amount) + tx, err := response.UnderlyingERC20Contract.Approve(noSendTxOpts, w.strategyManagerAddr, amount) if err != nil { return nil, errors.Join(errors.New("failed to approve token transfer"), err) } @@ -259,7 +257,7 @@ func (w *ChainWriter) DepositERC20IntoStrategy( return nil, errors.New("failed to send tx with err: " + err.Error()) } - tx, err = w.strategyManager.DepositIntoStrategy(noSendTxOpts, strategyAddr, underlyingTokenAddr, amount) + tx, err = w.strategyManager.DepositIntoStrategy(noSendTxOpts, strategyAddr, response.UnderlyingTokenAddress, amount) if err != nil { return nil, err } From 5ca3d80a688c1573e290969f15b968d01bbe3bd8 Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Fri, 31 Jan 2025 12:52:01 -0300 Subject: [PATCH 5/8] Fix tests --- chainio/clients/elcontracts/reader.go | 6 +- chainio/clients/elcontracts/reader_test.go | 168 +++++++++++++-------- chainio/clients/elcontracts/types.go | 4 +- 3 files changed, 111 insertions(+), 67 deletions(-) diff --git a/chainio/clients/elcontracts/reader.go b/chainio/clients/elcontracts/reader.go index cbe94b9af..4f22cb947 100644 --- a/chainio/clients/elcontracts/reader.go +++ b/chainio/clients/elcontracts/reader.go @@ -211,7 +211,7 @@ func (r *ChainReader) GetStrategyAndUnderlyingToken( if err != nil { return StrategyTokenResponse{}, utils.WrapError("Failed to fetch token contract", err) } - return StrategyTokenResponse{StrategyContract: *contractStrategy, TokenAddress: underlyingTokenAddr}, nil + return StrategyTokenResponse{StrategyContract: *contractStrategy, UnderlyingTokenAddress: underlyingTokenAddr}, nil } // GetStrategyAndUnderlyingERC20Token returns the strategy contract, the erc20 bindings for the underlying token @@ -225,7 +225,9 @@ func (r *ChainReader) GetStrategyAndUnderlyingERC20Token( if err != nil { return StrategyERC20TokenResponse{}, utils.WrapError("Failed to fetch strategy contract", err) } - underlyingTokenAddr, err := contractStrategy.UnderlyingToken(&bind.CallOpts{Context: ctx}) + underlyingTokenAddr, err := contractStrategy.UnderlyingToken( + &bind.CallOpts{Context: ctx, BlockNumber: request.BlockNumber}, + ) if err != nil { return StrategyERC20TokenResponse{}, utils.WrapError("Failed to fetch token contract", err) } diff --git a/chainio/clients/elcontracts/reader_test.go b/chainio/clients/elcontracts/reader_test.go index 9244c6986..83a9d002a 100644 --- a/chainio/clients/elcontracts/reader_test.go +++ b/chainio/clients/elcontracts/reader_test.go @@ -28,34 +28,49 @@ func TestChainReader(t *testing.T) { ctx := context.Background() contractAddrs := testutils.GetContractAddressesFromContractRegistry(anvilHttpEndpoint) + operatorAddress := testutils.ANVIL_FIRST_ADDRESS + + // REMOVE THIS WHEN FINISH WITH THE NEW INTERFACE operator := types.Operator{ - Address: testutils.ANVIL_FIRST_ADDRESS, + Address: operatorAddress, + } + + operatorRequest := elcontracts.OperatorRequest{ + OperatorAddress: common.HexToAddress(operatorAddress), + } + + strategyAddr := contractAddrs.Erc20MockStrategy + strategyRequest := elcontracts.StrategyRequest{ + StrategyAddress: strategyAddr, + } + + stakerRequest := elcontracts.StakerRequest{ + StakerAddress: common.HexToAddress(operatorAddress), } t.Run("is operator registered", func(t *testing.T) { - isOperator, err := read_clients.ElChainReader.IsOperatorRegistered(ctx, operator) + response, err := read_clients.ElChainReader.IsOperatorRegistered(ctx, operatorRequest) assert.NoError(t, err) - assert.Equal(t, isOperator, true) + assert.Equal(t, response.IsRegistered, true) }) t.Run("get operator details", func(t *testing.T) { - operatorDetails, err := read_clients.ElChainReader.GetOperatorDetails(ctx, operator) + response, err := read_clients.ElChainReader.GetOperatorDetails(ctx, operatorRequest) assert.NoError(t, err) - assert.NotNil(t, operatorDetails) - assert.Equal(t, operator.Address, operatorDetails.Address) + assert.NotNil(t, response) + assert.Equal(t, operatorRequest.OperatorAddress.Hex(), response.Operator.Address) }) t.Run("get strategy and underlying token", func(t *testing.T) { - strategyAddr := contractAddrs.Erc20MockStrategy - strategy, underlyingTokenAddr, err := read_clients.ElChainReader.GetStrategyAndUnderlyingToken( + response, err := read_clients.ElChainReader.GetStrategyAndUnderlyingToken( ctx, - strategyAddr, + strategyRequest, ) assert.NoError(t, err) - assert.NotNil(t, strategy) - assert.NotEqual(t, common.Address{}, underlyingTokenAddr) + assert.NotNil(t, response) + assert.NotEqual(t, common.Address{}, response.UnderlyingTokenAddress) - erc20Token, err := erc20.NewContractIERC20(underlyingTokenAddr, read_clients.EthHttpClient) + erc20Token, err := erc20.NewContractIERC20(response.UnderlyingTokenAddress, read_clients.EthHttpClient) assert.NoError(t, err) tokenName, err := erc20Token.Name(&bind.CallOpts{}) @@ -64,29 +79,31 @@ func TestChainReader(t *testing.T) { }) t.Run("get strategy and underlying ERC20 token", func(t *testing.T) { - strategyAddr := contractAddrs.Erc20MockStrategy - strategy, contractUnderlyingToken, underlyingTokenAddr, err := read_clients.ElChainReader.GetStrategyAndUnderlyingERC20Token( + erc20Response, err := read_clients.ElChainReader.GetStrategyAndUnderlyingERC20Token( ctx, - strategyAddr, + strategyRequest, ) assert.NoError(t, err) - assert.NotNil(t, strategy) - assert.NotEqual(t, common.Address{}, underlyingTokenAddr) - assert.NotNil(t, contractUnderlyingToken) + assert.NotNil(t, erc20Response) + assert.NotEqual(t, common.Address{}, erc20Response.UnderlyingTokenAddress) + assert.NotNil(t, erc20Response.UnderlyingERC20Contract) - tokenName, err := contractUnderlyingToken.Name(&bind.CallOpts{}) + tokenName, err := erc20Response.UnderlyingERC20Contract.Name(&bind.CallOpts{}) assert.NoError(t, err) assert.NotEmpty(t, tokenName) }) t.Run("get operator shares in strategy", func(t *testing.T) { - shares, err := read_clients.ElChainReader.GetOperatorSharesInStrategy( + shareRequest := elcontracts.SharesInStrategyRequest{ + OperatorAddress: common.HexToAddress(operatorAddress), + StrategyAddress: strategyAddr, + } + sharesResponse, err := read_clients.ElChainReader.GetOperatorSharesInStrategy( ctx, - common.HexToAddress(operator.Address), - contractAddrs.Erc20MockStrategy, + shareRequest, ) assert.NoError(t, err) - assert.NotZero(t, shares) + assert.NotZero(t, sharesResponse.Shares) }) t.Run("calculate delegation approval digest hash", func(t *testing.T) { @@ -122,27 +139,33 @@ func TestChainReader(t *testing.T) { }) t.Run("get staker shares", func(t *testing.T) { - strategies, shares, err := read_clients.ElChainReader.GetStakerShares( + stakerSharesRequest := elcontracts.StakerRequest{ + StakerAddress: common.HexToAddress(operatorAddress), + } + sharesResponse, err := read_clients.ElChainReader.GetStakerShares( ctx, - common.HexToAddress(operator.Address), + stakerSharesRequest, + ) + assert.NotZero(t, len(sharesResponse.StrategiesAddresses), "Strategies has at least one element") + assert.NotZero(t, len(sharesResponse.Shares), "Shares has at least one element") + assert.Equal( + t, + len(sharesResponse.StrategiesAddresses), + len(sharesResponse.Shares), + "Strategies has the same ammount of elements as shares", ) - 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) { - blockNumber := big.NewInt(0) - address, err := read_clients.ElChainReader.GetDelegatedOperator( + operatorResponse, err := read_clients.ElChainReader.GetDelegatedOperator( ctx, - common.HexToAddress(operator.Address), - blockNumber, + stakerRequest, ) assert.NoError(t, err) // The delegated operator of an operator is the operator itself - assert.Equal(t, address.String(), operator.Address) + assert.Equal(t, operatorResponse.OperatorAddress.String(), operator.Address) }) t.Run("GetOperatorShares", func(t *testing.T) { @@ -408,6 +431,10 @@ func TestGetCumulativeClaimedRewards(t *testing.T) { } privateKeyHex := testutils.ANVIL_FIRST_PRIVATE_KEY + strategyRequest := elcontracts.StrategyRequest{ + StrategyAddress: contractAddrs.Erc20MockStrategy, + } + // Create ChainWriter chainWriter, err := testclients.NewTestChainWriterFromConfig(anvilHttpEndpoint, privateKeyHex, config) require.NoError(t, err) @@ -421,20 +448,19 @@ func TestGetCumulativeClaimedRewards(t *testing.T) { require.NoError(t, err) require.True(t, receipt.Status == gethtypes.ReceiptStatusSuccessful) - strategyAddr := contractAddrs.Erc20MockStrategy - strategy, contractUnderlyingToken, underlyingTokenAddr, err := clients.ElChainReader.GetStrategyAndUnderlyingERC20Token( + response, err := clients.ElChainReader.GetStrategyAndUnderlyingERC20Token( ctx, - strategyAddr, + strategyRequest, ) assert.NoError(t, err) - assert.NotNil(t, strategy) - assert.NotEqual(t, common.Address{}, underlyingTokenAddr) - assert.NotNil(t, contractUnderlyingToken) + assert.NotNil(t, response.StrategyContract) + assert.NotEqual(t, common.Address{}, response.UnderlyingTokenAddress) + assert.NotNil(t, response.UnderlyingERC20Contract) anvil_address := common.HexToAddress(testutils.ANVIL_FIRST_ADDRESS) // This tests that without claims result is zero - claimed, err := chainReader.GetCumulativeClaimed(ctx, anvil_address, underlyingTokenAddr) + claimed, err := chainReader.GetCumulativeClaimed(ctx, anvil_address, response.UnderlyingTokenAddress) assert.Zero(t, claimed.Cmp(big.NewInt(0))) assert.NoError(t, err) @@ -447,7 +473,7 @@ func TestGetCumulativeClaimedRewards(t *testing.T) { require.True(t, receipt.Status == gethtypes.ReceiptStatusSuccessful) // This tests that with a claim result is cumulativeEarnings - claimed, err = chainReader.GetCumulativeClaimed(ctx, anvil_address, underlyingTokenAddr) + claimed, err = chainReader.GetCumulativeClaimed(ctx, anvil_address, response.UnderlyingTokenAddress) assert.Equal(t, claimed, big.NewInt(cumulativeEarnings)) assert.NoError(t, err) } @@ -465,6 +491,10 @@ func TestCheckClaim(t *testing.T) { } privateKeyHex := testutils.ANVIL_FIRST_PRIVATE_KEY + strategyRequest := elcontracts.StrategyRequest{ + StrategyAddress: contractAddrs.Erc20MockStrategy, + } + // Create ChainWriter and chain reader chainWriter, err := testclients.NewTestChainWriterFromConfig(anvilHttpEndpoint, privateKeyHex, config) require.NoError(t, err) @@ -486,15 +516,14 @@ func TestCheckClaim(t *testing.T) { require.NoError(t, err) require.True(t, receipt.Status == gethtypes.ReceiptStatusSuccessful) - strategyAddr := contractAddrs.Erc20MockStrategy - strategy, contractUnderlyingToken, underlyingTokenAddr, err := clients.ElChainReader.GetStrategyAndUnderlyingERC20Token( + response, err := clients.ElChainReader.GetStrategyAndUnderlyingERC20Token( ctx, - strategyAddr, + strategyRequest, ) assert.NoError(t, err) - assert.NotNil(t, strategy) - assert.NotEqual(t, common.Address{}, underlyingTokenAddr) - assert.NotNil(t, contractUnderlyingToken) + assert.NotNil(t, response.StrategyContract) + assert.NotEqual(t, common.Address{}, response.UnderlyingTokenAddress) + assert.NotNil(t, response.UnderlyingERC20Contract) checked, err := chainReader.CheckClaim(ctx, *claim) require.NoError(t, err) @@ -582,15 +611,15 @@ func TestGetAllocatableMagnitudeAndGetMaxMagnitudes(t *testing.T) { assert.Equal(t, maxMagnitudes[0], allocable+allocatable_reduction) // Check that the new allocationDelay is equal to delay - op := types.Operator{ - Address: operatorAddr.String(), + operatorRequest := elcontracts.OperatorRequest{ + OperatorAddress: operatorAddr, } - operatorDetails, err := chainReader.GetOperatorDetails(ctx, op) + response, err := chainReader.GetOperatorDetails(ctx, operatorRequest) assert.NoError(t, err) - assert.NotNil(t, operatorDetails) - assert.Equal(t, op.Address, operatorDetails.Address) - assert.Equal(t, delay, operatorDetails.AllocationDelay) + assert.NotNil(t, response.Operator) + assert.Equal(t, operatorRequest.OperatorAddress.Hex(), response.Operator.Address) + assert.Equal(t, delay, response.Operator.AllocationDelay) } func TestAdminFunctions(t *testing.T) { @@ -767,15 +796,16 @@ func TestContractErrorCases(t *testing.T) { // This address does not belong to a Token contract strategyAddr := common.HexToAddress("34634374736473673643") + strategyRequest := elcontracts.StrategyRequest{StrategyAddress: strategyAddr} t.Run("GetStrategyAndUnderlyingToken", func(t *testing.T) { - _, _, err := chainReader.GetStrategyAndUnderlyingToken(ctx, strategyAddr) + _, err := chainReader.GetStrategyAndUnderlyingToken(ctx, strategyRequest) assert.Error(t, err) assert.Equal(t, err.Error(), "Failed to fetch token contract: no contract code at given address") }) t.Run("GetStrategyAndUnderlyingERC20Token", func(t *testing.T) { - _, _, _, err := chainReader.GetStrategyAndUnderlyingERC20Token(ctx, strategyAddr) + _, err := chainReader.GetStrategyAndUnderlyingERC20Token(ctx, strategyRequest) assert.Error(t, err) assert.Equal(t, err.Error(), "Failed to fetch token contract: no contract code at given address") }) @@ -795,6 +825,7 @@ func TestInvalidConfig(t *testing.T) { operator := types.Operator{ Address: operatorAddr, } + operatorRequest := elcontracts.OperatorRequest{OperatorAddress: common.HexToAddress(operatorAddr)} config := elcontracts.Config{} chainReader, err := testclients.NewTestChainReaderFromConfig(anvilHttpEndpoint, config) @@ -802,13 +833,13 @@ func TestInvalidConfig(t *testing.T) { t.Run("try to check if operator is registered with invalid config", func(t *testing.T) { // IsOperatorRegistered needs a correct DelegationManagerAddress - _, err := chainReader.IsOperatorRegistered(context.Background(), operator) + _, err := chainReader.IsOperatorRegistered(context.Background(), operatorRequest) require.Error(t, err) }) t.Run("get operator details with invalid config", func(t *testing.T) { // GetOperatorDetails needs a correct DelegationManagerAddress - _, err := chainReader.GetOperatorDetails(context.Background(), operator) + _, err := chainReader.GetOperatorDetails(context.Background(), operatorRequest) require.Error(t, err) }) @@ -827,17 +858,23 @@ func TestInvalidConfig(t *testing.T) { t.Run("try to get strategy and underlying token with wrong strategy address", func(t *testing.T) { // Invalid strategy address strategyAddr := common.HexToAddress(testutils.ANVIL_FIRST_ADDRESS) + strategyRequest := elcontracts.StrategyRequest{StrategyAddress: strategyAddr} + operatorAddr := common.HexToAddress(testutils.ANVIL_SECOND_ADDRESS) + sharesStrategyRequest := elcontracts.SharesInStrategyRequest{ + OperatorAddress: operatorAddr, + StrategyAddress: strategyAddr, + } // GetOperatorSharesInStrategy needs a correct DelegationManagerAddress - _, err := chainReader.GetOperatorSharesInStrategy(context.Background(), operatorAddr, strategyAddr) + _, err := chainReader.GetOperatorSharesInStrategy(context.Background(), sharesStrategyRequest) require.Error(t, err) // GetStrategyAndUnderlyingToken needs a correct StrategyAddress - _, _, err = chainReader.GetStrategyAndUnderlyingToken(context.Background(), strategyAddr) + _, err = chainReader.GetStrategyAndUnderlyingToken(context.Background(), strategyRequest) require.Error(t, err) - _, _, _, err = chainReader.GetStrategyAndUnderlyingERC20Token(context.Background(), strategyAddr) + _, err = chainReader.GetStrategyAndUnderlyingERC20Token(context.Background(), strategyRequest) require.Error(t, err) }) @@ -925,16 +962,21 @@ func TestInvalidConfig(t *testing.T) { t.Run("try to get a staker shares with invalid config", func(t *testing.T) { // GetStakerShares needs a correct DelegationManagerAddress - _, _, err := chainReader.GetStakerShares(context.Background(), common.HexToAddress(operator.Address)) + stakerRequest := elcontracts.StakerRequest{ + StakerAddress: common.HexToAddress(testutils.ANVIL_FIRST_ADDRESS), + } + _, err := chainReader.GetStakerShares(context.Background(), stakerRequest) require.Error(t, err) }) t.Run("try to get the delegated operator shares with invalid config", func(t *testing.T) { // GetDelegatedOperator needs a correct DelegationManagerAddress + stakerRequest := elcontracts.StakerRequest{ + StakerAddress: common.HexToAddress(testutils.ANVIL_FIRST_ADDRESS), + } _, err := chainReader.GetDelegatedOperator( context.Background(), - common.HexToAddress(operator.Address), - big.NewInt(0), + stakerRequest, ) require.Error(t, err) }) diff --git a/chainio/clients/elcontracts/types.go b/chainio/clients/elcontracts/types.go index 0e64cd22f..bf3d39a37 100644 --- a/chainio/clients/elcontracts/types.go +++ b/chainio/clients/elcontracts/types.go @@ -102,8 +102,8 @@ type StrategyRequest struct { } type StrategyTokenResponse struct { - StrategyContract strategy.ContractIStrategy - TokenAddress common.Address + StrategyContract strategy.ContractIStrategy + UnderlyingTokenAddress common.Address } type StrategyERC20TokenResponse struct { From 3203accb362196dd4c52d99b4f1916220094255b Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Fri, 31 Jan 2025 15:01:09 -0300 Subject: [PATCH 6/8] Add docs to methods --- chainio/clients/elcontracts/reader.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chainio/clients/elcontracts/reader.go b/chainio/clients/elcontracts/reader.go index 4f22cb947..f33fce91b 100644 --- a/chainio/clients/elcontracts/reader.go +++ b/chainio/clients/elcontracts/reader.go @@ -91,6 +91,7 @@ func NewReaderFromConfig( ), nil } +// IsOperatorRegistered returns if an operator is registered func (r *ChainReader) IsOperatorRegistered( ctx context.Context, request OperatorRequest, @@ -150,6 +151,7 @@ func (r *ChainReader) GetDelegatedOperator( return DelegateOperatorResponse{OperatorAddress: operatorAddress}, nil } +// GetOperatorDetails returns the operator's details func (r *ChainReader) GetOperatorDetails( ctx context.Context, request OperatorRequest, From 4301544501ae80370493ea2d0d06c6d4298794e0 Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Fri, 31 Jan 2025 17:05:42 -0300 Subject: [PATCH 7/8] Add docs to req-res structs --- chainio/clients/elcontracts/types.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/chainio/clients/elcontracts/types.go b/chainio/clients/elcontracts/types.go index bf3d39a37..2fa600d8e 100644 --- a/chainio/clients/elcontracts/types.go +++ b/chainio/clients/elcontracts/types.go @@ -87,55 +87,67 @@ type RemovePendingAdminRequest struct { // READER TYPES +// OperatorRequest represents a request that requires an operator's address type OperatorRequest struct { BlockNumber *big.Int OperatorAddress common.Address } +// OperatorResponse represents the operator information type OperatorResponse struct { Operator types.Operator } +// StrategyRequest represents a request that requires a strategy's address type StrategyRequest struct { BlockNumber *big.Int StrategyAddress common.Address } +// StrategyTokenResponse contains the strategy contract and the underlying token address type StrategyTokenResponse struct { StrategyContract strategy.ContractIStrategy UnderlyingTokenAddress common.Address } +// StrategyERC20TokenResponse contains the strategy contract, the underlying token address and the underlying ERC20 +// contract type StrategyERC20TokenResponse struct { StrategyContract strategy.ContractIStrategy UnderlyingERC20Contract erc20.ContractIERC20Methods UnderlyingTokenAddress common.Address } +// OperatorRegisterResponse indicates if the operator is registered or not type OperatorRegisterResponse struct { IsRegistered bool } +// StakerRequest represents a request that requires a staker's address type StakerRequest struct { BlockNumber *big.Int StakerAddress common.Address } +// StakerSharesResponse contains the staker's shares type StakerSharesResponse struct { StrategiesAddresses []common.Address Shares []*big.Int } +// DelegateOperatorResponse represents the operator's delegate address type DelegateOperatorResponse struct { OperatorAddress common.Address } +// SharesInStrategyRequest represents a request that requires a strategy's address and an operator's address type SharesInStrategyRequest struct { BlockNumber *big.Int OperatorAddress common.Address StrategyAddress common.Address } +// ShareResponse contains the shares type SharesResponse struct { Shares *big.Int } From f2dee95d3bc8cd657725bc02de1135c9cae441a8 Mon Sep 17 00:00:00 2001 From: Damian Ramirez Date: Fri, 31 Jan 2025 17:07:48 -0300 Subject: [PATCH 8/8] Explain about the nil in blockNumber --- chainio/clients/elcontracts/types.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/chainio/clients/elcontracts/types.go b/chainio/clients/elcontracts/types.go index 2fa600d8e..ed6ddaa74 100644 --- a/chainio/clients/elcontracts/types.go +++ b/chainio/clients/elcontracts/types.go @@ -88,6 +88,7 @@ type RemovePendingAdminRequest struct { // READER TYPES // OperatorRequest represents a request that requires an operator's address +// If `BlockNumber` is nil, the latest block will be used type OperatorRequest struct { BlockNumber *big.Int OperatorAddress common.Address @@ -99,6 +100,7 @@ type OperatorResponse struct { } // StrategyRequest represents a request that requires a strategy's address +// If `BlockNumber` is nil, the latest block will be used type StrategyRequest struct { BlockNumber *big.Int StrategyAddress common.Address @@ -124,6 +126,7 @@ type OperatorRegisterResponse struct { } // StakerRequest represents a request that requires a staker's address +// If `BlockNumber` is nil, the latest block will be used type StakerRequest struct { BlockNumber *big.Int StakerAddress common.Address @@ -140,14 +143,15 @@ type DelegateOperatorResponse struct { OperatorAddress common.Address } -// SharesInStrategyRequest represents a request that requires a strategy's address and an operator's address +// SharesInStrategyRequest represents a request that requires both strategy's address and operator's address +// If `BlockNumber` is nil, the latest block will be used type SharesInStrategyRequest struct { BlockNumber *big.Int OperatorAddress common.Address StrategyAddress common.Address } -// ShareResponse contains the shares +// ShareResponse contains the numbers of shares type SharesResponse struct { Shares *big.Int }