diff --git a/chainio/clients/elcontracts/error.go b/chainio/clients/elcontracts/error.go new file mode 100644 index 00000000..ec63ee8f --- /dev/null +++ b/chainio/clients/elcontracts/error.go @@ -0,0 +1,64 @@ +package elcontracts + +import "fmt" + +type Error struct { + code int + message string + description string + cause error +} + +func (e Error) Error() string { + if e.cause != nil { + return fmt.Sprintf("%s(%d) - %s: %s", e.message, e.code, e.description, e.cause.Error()) + } + return fmt.Sprintf("%s(%d) - %s", e.message, e.code, e.description) +} + +func (e Error) Unwrap() error { + return e.cause +} + +func BindingError(bindingName string, errorCause error) Error { + errDescription := fmt.Sprintf("Error happened while calling %s", bindingName) + return Error{ + code: 0, + message: "Binding error", + description: errDescription, + cause: errorCause, + } +} + +func MissingContractError(contractName string) Error { + errDescription := fmt.Sprintf("%s contract not provided", contractName) + return Error{ + code: 1, + message: "Missing needed contract", + description: errDescription, + cause: nil, + } +} + +func NestedError(functionName string, errorCause error) Error { + errDescription := fmt.Sprintf("Error happened while calling %s", functionName) + return Error{ + code: 2, + message: "Nested error", + description: errDescription, + cause: errorCause, + } +} + +func OtherError(errDescription string, errorCause error) Error { + return Error{ + code: 3, + message: "Other error", + description: errDescription, + cause: errorCause, + } +} + +func CommonErrorMissingContract(contractName string) string { + return fmt.Sprintf("Missing needed contract(1) - %s contract not provided", contractName) +} diff --git a/chainio/clients/elcontracts/reader.go b/chainio/clients/elcontracts/reader.go index 1f46ab3d..72f43805 100644 --- a/chainio/clients/elcontracts/reader.go +++ b/chainio/clients/elcontracts/reader.go @@ -2,7 +2,6 @@ package elcontracts import ( "context" - "errors" "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -19,7 +18,6 @@ import ( strategymanager "github.com/Layr-Labs/eigensdk-go/contracts/bindings/StrategyManager" "github.com/Layr-Labs/eigensdk-go/logging" "github.com/Layr-Labs/eigensdk-go/types" - "github.com/Layr-Labs/eigensdk-go/utils" ) type Config struct { @@ -40,7 +38,7 @@ type ChainReader struct { ethClient eth.HttpBackend } -var errLegacyAVSsNotSupported = errors.New("method not supported for legacy AVSs") +var errLegacyAVSsNotSupported = OtherError("Method not supported for legacy AVSs", nil) func NewChainReader( delegationManager *delegationmanager.ContractDelegationManager, @@ -77,7 +75,8 @@ func NewReaderFromConfig( logger, ) if err != nil { - return nil, err + wrappedError := NestedError("NewBindingsFromConfig", err) + return nil, wrappedError } return NewChainReader( elContractBindings.DelegationManager, @@ -96,10 +95,20 @@ func (r *ChainReader) IsOperatorRegistered( operator types.Operator, ) (bool, error) { if r.delegationManager == nil { - return false, errors.New("DelegationManager contract not provided") + wrappedError := MissingContractError("DelegationManager") + return false, wrappedError } - return r.delegationManager.IsOperator(&bind.CallOpts{Context: ctx}, gethcommon.HexToAddress(operator.Address)) + isRegistered, err := r.delegationManager.IsOperator( + &bind.CallOpts{Context: ctx}, + gethcommon.HexToAddress(operator.Address), + ) + if err != nil { + wrappedError := BindingError("DelegationManager.isOperator", err) + return false, wrappedError + } + + return isRegistered, nil } // GetStakerShares returns the amount of shares that a staker has in all of the strategies in which they have nonzero @@ -109,9 +118,17 @@ func (r *ChainReader) GetStakerShares( stakerAddress gethcommon.Address, ) ([]gethcommon.Address, []*big.Int, error) { if r.delegationManager == nil { - return nil, nil, errors.New("DelegationManager contract not provided") + wrappedError := MissingContractError("DelegationManager") + return nil, nil, wrappedError + } + + addresses, shares, err := r.delegationManager.GetDepositedShares(&bind.CallOpts{Context: ctx}, stakerAddress) + if err != nil { + wrappedError := BindingError("DelegationManager.getDepositedShares", err) + return nil, nil, wrappedError } - return r.delegationManager.GetDepositedShares(&bind.CallOpts{Context: ctx}, stakerAddress) + + return addresses, shares, nil } // GetDelegatedOperator returns the operator that a staker has delegated to @@ -121,9 +138,17 @@ func (r *ChainReader) GetDelegatedOperator( blockNumber *big.Int, ) (gethcommon.Address, error) { if r.delegationManager == nil { - return gethcommon.Address{}, errors.New("DelegationManager contract not provided") + wrappedError := MissingContractError("DelegationManager") + return gethcommon.Address{}, wrappedError } - return r.delegationManager.DelegatedTo(&bind.CallOpts{Context: ctx}, stakerAddress) + + delegatedOperator, err := r.delegationManager.DelegatedTo(&bind.CallOpts{Context: ctx}, stakerAddress) + if err != nil { + wrappedError := BindingError("DelegationManager.delegatedTo", err) + return gethcommon.Address{}, wrappedError + } + + return delegatedOperator, nil } func (r *ChainReader) GetOperatorDetails( @@ -131,7 +156,8 @@ func (r *ChainReader) GetOperatorDetails( operator types.Operator, ) (types.Operator, error) { if r.delegationManager == nil { - return types.Operator{}, errors.New("DelegationManager contract not provided") + wrappedError := MissingContractError("DelegationManager") + return types.Operator{}, wrappedError } delegationManagerAddress, err := r.delegationManager.DelegationApprover( @@ -140,9 +166,11 @@ func (r *ChainReader) GetOperatorDetails( ) // This call should not fail since it's a getter if err != nil { - return types.Operator{}, err + wrappedError := BindingError("DelegationManager.delegationApprover", err) + return types.Operator{}, wrappedError } + // Should we check if (allocationManager != nil)? isSet, delay, err := r.allocationManager.GetAllocationDelay( &bind.CallOpts{ Context: ctx, @@ -151,7 +179,8 @@ func (r *ChainReader) GetOperatorDetails( ) // This call should not fail if err != nil { - return types.Operator{}, err + wrappedError := BindingError("AllocationManager.getAllocationDelay", err) + return types.Operator{}, wrappedError } var allocationDelay uint32 @@ -176,11 +205,13 @@ func (r *ChainReader) GetStrategyAndUnderlyingToken( contractStrategy, err := strategy.NewContractIStrategy(strategyAddr, 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) + wrappedError := BindingError("strategy contract", err) + return nil, gethcommon.Address{}, wrappedError } underlyingTokenAddr, err := contractStrategy.UnderlyingToken(&bind.CallOpts{Context: ctx}) if err != nil { - return nil, gethcommon.Address{}, utils.WrapError("Failed to fetch token contract", err) + wrappedError := BindingError("token contract", err) + return nil, gethcommon.Address{}, wrappedError } return contractStrategy, underlyingTokenAddr, nil } @@ -194,16 +225,19 @@ func (r *ChainReader) GetStrategyAndUnderlyingERC20Token( contractStrategy, err := strategy.NewContractIStrategy(strategyAddr, 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) + wrappedError := BindingError("strategy contract", err) + return nil, nil, gethcommon.Address{}, wrappedError } underlyingTokenAddr, err := contractStrategy.UnderlyingToken(&bind.CallOpts{Context: ctx}) if err != nil { - return nil, nil, gethcommon.Address{}, utils.WrapError("Failed to fetch token contract", err) + wrappedError := BindingError("token contract", err) + return nil, nil, gethcommon.Address{}, wrappedError } 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) + wrappedError := BindingError("erc20 token contract", err) + return nil, nil, gethcommon.Address{}, wrappedError } return contractStrategy, contractUnderlyingToken, underlyingTokenAddr, nil } @@ -214,14 +248,21 @@ func (r *ChainReader) GetOperatorSharesInStrategy( strategyAddr gethcommon.Address, ) (*big.Int, error) { if r.delegationManager == nil { - return &big.Int{}, errors.New("DelegationManager contract not provided") + wrappedError := MissingContractError("DelegationManager") + return &big.Int{}, wrappedError } - return r.delegationManager.OperatorShares( + shares, err := r.delegationManager.OperatorShares( &bind.CallOpts{Context: ctx}, operatorAddr, strategyAddr, ) + if err != nil { + wrappedError := BindingError("DelegationManager.operatorShares", err) + return &big.Int{}, wrappedError + } + + return shares, nil } func (r *ChainReader) CalculateDelegationApprovalDigestHash( @@ -233,10 +274,11 @@ func (r *ChainReader) CalculateDelegationApprovalDigestHash( expiry *big.Int, ) ([32]byte, error) { if r.delegationManager == nil { - return [32]byte{}, errors.New("DelegationManager contract not provided") + wrappedError := MissingContractError("DelegationManager") + return [32]byte{}, wrappedError } - return r.delegationManager.CalculateDelegationApprovalDigestHash( + digestHash, err := r.delegationManager.CalculateDelegationApprovalDigestHash( &bind.CallOpts{Context: ctx}, staker, operator, @@ -244,6 +286,12 @@ func (r *ChainReader) CalculateDelegationApprovalDigestHash( approverSalt, expiry, ) + if err != nil { + wrappedError := BindingError("DelegationManager.calculateDelegationApprovalDigestHash", err) + return [32]byte{}, wrappedError + } + + return digestHash, nil } func (r *ChainReader) CalculateOperatorAVSRegistrationDigestHash( @@ -254,44 +302,70 @@ func (r *ChainReader) CalculateOperatorAVSRegistrationDigestHash( expiry *big.Int, ) ([32]byte, error) { if r.avsDirectory == nil { - return [32]byte{}, errors.New("AVSDirectory contract not provided") + wrappedError := MissingContractError("AVSDirectory") + return [32]byte{}, wrappedError } - return r.avsDirectory.CalculateOperatorAVSRegistrationDigestHash( + digestHash, err := r.avsDirectory.CalculateOperatorAVSRegistrationDigestHash( &bind.CallOpts{Context: ctx}, operator, avs, salt, expiry, ) + if err != nil { + wrappedError := BindingError("AvsDirectory.calculateOperatorAVSRegistrationDigestHash", err) + return [32]byte{}, wrappedError + } + + return digestHash, nil } func (r *ChainReader) GetDistributionRootsLength(ctx context.Context) (*big.Int, error) { if r.rewardsCoordinator == nil { - return nil, errors.New("RewardsCoordinator contract not provided") + wrappedError := MissingContractError("RewardsCoordinator") + return &big.Int{}, wrappedError + } + + distributionRootsLength, err := r.rewardsCoordinator.GetDistributionRootsLength(&bind.CallOpts{Context: ctx}) + if err != nil { + wrappedError := BindingError("RewardsCoordinator.getDistributionRootsLength", err) + return &big.Int{}, wrappedError } - return r.rewardsCoordinator.GetDistributionRootsLength(&bind.CallOpts{Context: ctx}) + return distributionRootsLength, nil } func (r *ChainReader) CurrRewardsCalculationEndTimestamp(ctx context.Context) (uint32, error) { if r.rewardsCoordinator == nil { - return 0, errors.New("RewardsCoordinator contract not provided") + wrappedError := MissingContractError("RewardsCoordinator") + return 0, wrappedError } - return r.rewardsCoordinator.CurrRewardsCalculationEndTimestamp(&bind.CallOpts{Context: ctx}) + endTimestamp, err := r.rewardsCoordinator.CurrRewardsCalculationEndTimestamp(&bind.CallOpts{Context: ctx}) + if err != nil { + wrappedError := BindingError("RewardsCoordinator.currRewardsCalculationEndTimestamp", err) + return 0, wrappedError + } + + return endTimestamp, nil } func (r *ChainReader) GetCurrentClaimableDistributionRoot( ctx context.Context, ) (rewardscoordinator.IRewardsCoordinatorTypesDistributionRoot, error) { if r.rewardsCoordinator == nil { - return rewardscoordinator.IRewardsCoordinatorTypesDistributionRoot{}, errors.New( - "RewardsCoordinator contract not provided", - ) + wrappedError := MissingContractError("RewardsCoordinator") + return rewardscoordinator.IRewardsCoordinatorTypesDistributionRoot{}, wrappedError } - return r.rewardsCoordinator.GetCurrentClaimableDistributionRoot(&bind.CallOpts{Context: ctx}) + distributionRoot, err := r.rewardsCoordinator.GetCurrentClaimableDistributionRoot(&bind.CallOpts{Context: ctx}) + if err != nil { + wrappedError := BindingError("RewardsCoordinator.getCurrentClaimableDistributionRoot", err) + return rewardscoordinator.IRewardsCoordinatorTypesDistributionRoot{}, wrappedError + } + + return distributionRoot, nil } func (r *ChainReader) GetRootIndexFromHash( @@ -299,10 +373,17 @@ func (r *ChainReader) GetRootIndexFromHash( rootHash [32]byte, ) (uint32, error) { if r.rewardsCoordinator == nil { - return 0, errors.New("RewardsCoordinator contract not provided") + wrappedError := MissingContractError("RewardsCoordinator") + return 0, wrappedError } - return r.rewardsCoordinator.GetRootIndexFromHash(&bind.CallOpts{Context: ctx}, rootHash) + rootIndex, err := r.rewardsCoordinator.GetRootIndexFromHash(&bind.CallOpts{Context: ctx}, rootHash) + if err != nil { + wrappedError := BindingError("RewardsCoordinator.getRootIndexFromHash", err) + return 0, wrappedError + } + + return rootIndex, nil } func (r *ChainReader) GetCumulativeClaimed( @@ -311,10 +392,17 @@ func (r *ChainReader) GetCumulativeClaimed( token gethcommon.Address, ) (*big.Int, error) { if r.rewardsCoordinator == nil { - return nil, errors.New("RewardsCoordinator contract not provided") + wrappedError := MissingContractError("RewardsCoordinator") + return nil, wrappedError + } + + cumulativeClaimed, err := r.rewardsCoordinator.CumulativeClaimed(&bind.CallOpts{Context: ctx}, earner, token) + if err != nil { + wrappedError := BindingError("RewardsCoordinator.cumulativeClaimed", err) + return nil, wrappedError } - return r.rewardsCoordinator.CumulativeClaimed(&bind.CallOpts{Context: ctx}, earner, token) + return cumulativeClaimed, nil } func (r *ChainReader) CheckClaim( @@ -322,10 +410,17 @@ func (r *ChainReader) CheckClaim( claim rewardscoordinator.IRewardsCoordinatorTypesRewardsMerkleClaim, ) (bool, error) { if r.rewardsCoordinator == nil { - return false, errors.New("RewardsCoordinator contract not provided") + wrappedError := MissingContractError("RewardsCoordinator") + return false, wrappedError + } + + claimChecked, err := r.rewardsCoordinator.CheckClaim(&bind.CallOpts{Context: ctx}, claim) + if err != nil { + wrappedError := BindingError("RewardsCoordinator.checkClaim", err) + return false, wrappedError } - return r.rewardsCoordinator.CheckClaim(&bind.CallOpts{Context: ctx}, claim) + return claimChecked, nil } func (r *ChainReader) GetOperatorAVSSplit( @@ -334,10 +429,17 @@ func (r *ChainReader) GetOperatorAVSSplit( avs gethcommon.Address, ) (uint16, error) { if r.rewardsCoordinator == nil { - return 0, errors.New("RewardsCoordinator contract not provided") + wrappedError := MissingContractError("RewardsCoordinator") + return 0, wrappedError } - return r.rewardsCoordinator.GetOperatorAVSSplit(&bind.CallOpts{Context: ctx}, operator, avs) + operatorSplit, err := r.rewardsCoordinator.GetOperatorAVSSplit(&bind.CallOpts{Context: ctx}, operator, avs) + if err != nil { + wrappedError := BindingError("RewardsCoordinator.getOperatorAVSSplit", err) + return 0, wrappedError + } + + return operatorSplit, nil } func (r *ChainReader) GetOperatorPISplit( @@ -345,10 +447,17 @@ func (r *ChainReader) GetOperatorPISplit( operator gethcommon.Address, ) (uint16, error) { if r.rewardsCoordinator == nil { - return 0, errors.New("RewardsCoordinator contract not provided") + wrappedError := MissingContractError("RewardsCoordinator") + return 0, wrappedError } - return r.rewardsCoordinator.GetOperatorPISplit(&bind.CallOpts{Context: ctx}, operator) + operatorSplit, err := r.rewardsCoordinator.GetOperatorPISplit(&bind.CallOpts{Context: ctx}, operator) + if err != nil { + wrappedError := BindingError("RewardsCoordinator.getOperatorPISplit", err) + return 0, wrappedError + } + + return operatorSplit, nil } func (r *ChainReader) GetAllocatableMagnitude( @@ -357,10 +466,21 @@ func (r *ChainReader) GetAllocatableMagnitude( strategyAddress gethcommon.Address, ) (uint64, error) { if r.allocationManager == nil { - return 0, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return 0, wrappedError } - return r.allocationManager.GetAllocatableMagnitude(&bind.CallOpts{Context: ctx}, operatorAddress, strategyAddress) + allocatableMagnitude, err := r.allocationManager.GetAllocatableMagnitude( + &bind.CallOpts{Context: ctx}, + operatorAddress, + strategyAddress, + ) + if err != nil { + wrappedError := BindingError("AllocationManager.getAllocatableMagnitude", err) + return 0, wrappedError + } + + return allocatableMagnitude, nil } func (r *ChainReader) GetMaxMagnitudes( @@ -369,10 +489,21 @@ func (r *ChainReader) GetMaxMagnitudes( strategyAddresses []gethcommon.Address, ) ([]uint64, error) { if r.allocationManager == nil { - return []uint64{}, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return []uint64{}, wrappedError } - return r.allocationManager.GetMaxMagnitudes0(&bind.CallOpts{Context: ctx}, operatorAddress, strategyAddresses) + maxMagnitudes, err := r.allocationManager.GetMaxMagnitudes0( + &bind.CallOpts{Context: ctx}, + operatorAddress, + strategyAddresses, + ) + if err != nil { + wrappedError := BindingError("AllocationManager.getMaxMagnitudes0", err) + return []uint64{}, wrappedError + } + + return maxMagnitudes, nil } func (r *ChainReader) GetAllocationInfo( @@ -381,7 +512,8 @@ func (r *ChainReader) GetAllocationInfo( strategyAddress gethcommon.Address, ) ([]AllocationInfo, error) { if r.allocationManager == nil { - return nil, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return nil, wrappedError } opSets, allocationInfo, err := r.allocationManager.GetStrategyAllocations( @@ -391,7 +523,8 @@ func (r *ChainReader) GetAllocationInfo( ) // This call should not fail since it's a getter if err != nil { - return nil, err + wrappedError := BindingError("AllocationManager.getStrategyAllocations", err) + return nil, wrappedError } allocationsInfo := make([]AllocationInfo, len(opSets)) @@ -414,12 +547,19 @@ func (r *ChainReader) GetOperatorShares( strategyAddresses []gethcommon.Address, ) ([]*big.Int, error) { if r.delegationManager == nil { - return nil, errors.New("DelegationManager contract not provided") + wrappedError := MissingContractError("DelegationManager") + return nil, wrappedError } - return r.delegationManager.GetOperatorShares(&bind.CallOpts{ + operatorShares, err := r.delegationManager.GetOperatorShares(&bind.CallOpts{ Context: ctx, }, operatorAddress, strategyAddresses) + if err != nil { + wrappedError := BindingError("DelegationManager.getOperatorShares", err) + return nil, wrappedError + } + + return operatorShares, nil } func (r *ChainReader) GetOperatorsShares( @@ -428,9 +568,21 @@ func (r *ChainReader) GetOperatorsShares( strategyAddresses []gethcommon.Address, ) ([][]*big.Int, error) { if r.delegationManager == nil { - return nil, errors.New("DelegationManager contract not provided") + wrappedError := MissingContractError("DelegationManager") + return nil, wrappedError + } + + operatorsShares, err := r.delegationManager.GetOperatorsShares( + &bind.CallOpts{Context: ctx}, + operatorAddresses, + strategyAddresses, + ) + if err != nil { + wrappedError := BindingError("DelegationManager.getOperatorsShares", err) + return nil, wrappedError } - return r.delegationManager.GetOperatorsShares(&bind.CallOpts{Context: ctx}, operatorAddresses, strategyAddresses) + + return operatorsShares, nil } // GetNumOperatorSetsForOperator returns the number of operator sets that an operator is part of @@ -440,11 +592,13 @@ func (r *ChainReader) GetNumOperatorSetsForOperator( operatorAddress gethcommon.Address, ) (*big.Int, error) { if r.allocationManager == nil { - return nil, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return nil, wrappedError } opSets, err := r.allocationManager.GetAllocatedSets(&bind.CallOpts{Context: ctx}, operatorAddress) if err != nil { - return nil, err + wrappedError := BindingError("AllocationManager.getAllocatedSets", err) + return nil, wrappedError } return big.NewInt(int64(len(opSets))), nil } @@ -456,11 +610,18 @@ func (r *ChainReader) GetOperatorSetsForOperator( operatorAddress gethcommon.Address, ) ([]allocationmanager.OperatorSet, error) { if r.allocationManager == nil { - return nil, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return nil, wrappedError } // TODO: we're fetching max int64 operatorSets here. What's the practical limit for timeout by RPC? do we need to // paginate? - return r.allocationManager.GetAllocatedSets(&bind.CallOpts{Context: ctx}, operatorAddress) + allocatedSets, err := r.allocationManager.GetAllocatedSets(&bind.CallOpts{Context: ctx}, operatorAddress) + if err != nil { + wrappedError := BindingError("AllocationManager.getAllocatedSets", err) + return nil, wrappedError + } + + return allocatedSets, nil } // IsOperatorRegisteredWithOperatorSet returns if an operator is registered with a specific operator set @@ -472,24 +633,28 @@ func (r *ChainReader) IsOperatorRegisteredWithOperatorSet( if operatorSet.Id == 0 { // this is an M2 AVS if r.avsDirectory == nil { - return false, errors.New("AVSDirectory contract not provided") + wrappedError := MissingContractError("AVSDirectory") + return false, wrappedError } status, err := r.avsDirectory.AvsOperatorStatus(&bind.CallOpts{Context: ctx}, operatorSet.Avs, operatorAddress) // This call should not fail since it's a getter if err != nil { - return false, err + wrappedError := BindingError("AvsDirectory.avsOperatorStatus", err) + return false, wrappedError } return status == 1, nil } else { if r.allocationManager == nil { - return false, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return false, wrappedError } registeredOperatorSets, err := r.allocationManager.GetRegisteredSets(&bind.CallOpts{Context: ctx}, operatorAddress) // This call should not fail since it's a getter if err != nil { - return false, err + wrappedError := BindingError("AllocationManager.getRegisteredSets", err) + return false, wrappedError } for _, registeredOperatorSet := range registeredOperatorSets { if registeredOperatorSet.Id == operatorSet.Id && registeredOperatorSet.Avs == operatorSet.Avs { @@ -511,10 +676,17 @@ func (r *ChainReader) GetOperatorsForOperatorSet( return nil, errLegacyAVSsNotSupported } else { if r.allocationManager == nil { - return nil, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return nil, wrappedError } - return r.allocationManager.GetMembers(&bind.CallOpts{Context: ctx}, operatorSet) + members, err := r.allocationManager.GetMembers(&bind.CallOpts{Context: ctx}, operatorSet) + if err != nil { + wrappedError := BindingError("AllocationManager.getMembers", err) + return nil, wrappedError + } + + return members, nil } } @@ -527,10 +699,17 @@ func (r *ChainReader) GetNumOperatorsForOperatorSet( return nil, errLegacyAVSsNotSupported } else { if r.allocationManager == nil { - return nil, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return nil, wrappedError + } + + memberCount, err := r.allocationManager.GetMemberCount(&bind.CallOpts{Context: ctx}, operatorSet) + if err != nil { + wrappedError := BindingError("AllocationManager.getMemberCount", err) + return nil, wrappedError } - return r.allocationManager.GetMemberCount(&bind.CallOpts{Context: ctx}, operatorSet) + return memberCount, nil } } @@ -544,10 +723,17 @@ func (r *ChainReader) GetStrategiesForOperatorSet( return nil, errLegacyAVSsNotSupported } else { if r.allocationManager == nil { - return nil, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return nil, wrappedError } - return r.allocationManager.GetStrategiesInOperatorSet(&bind.CallOpts{Context: ctx}, operatorSet) + strategiesInSet, err := r.allocationManager.GetStrategiesInOperatorSet(&bind.CallOpts{Context: ctx}, operatorSet) + if err != nil { + wrappedError := BindingError("AllocationManager.getStrategiesInOperatorSet", err) + return nil, wrappedError + } + + return strategiesInSet, nil } } @@ -558,13 +744,15 @@ func (r *ChainReader) GetSlashableShares( strategies []gethcommon.Address, ) (map[gethcommon.Address]*big.Int, error) { if r.allocationManager == nil { - return nil, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return nil, wrappedError } currentBlock, err := r.ethClient.BlockNumber(ctx) // This call should not fail since it's a getter if err != nil { - return nil, err + wrappedError := BindingError("EthClient.blockNumber", err) + return nil, wrappedError } slashableShares, err := r.allocationManager.GetMinimumSlashableStake( @@ -576,10 +764,12 @@ func (r *ChainReader) GetSlashableShares( ) // This call should not fail since it's a getter if err != nil { - return nil, err + wrappedError := BindingError("AllocationManager.getMinimumSlashableStake", err) + return nil, wrappedError } if len(slashableShares) == 0 { - return nil, errors.New("no slashable shares found for operator") + wrappedError := OtherError("No slashable shares found for operator", err) + return nil, wrappedError } slashableShareStrategyMap := make(map[gethcommon.Address]*big.Int) @@ -601,9 +791,17 @@ func (r *ChainReader) GetSlashableSharesForOperatorSets( currentBlock, err := r.ethClient.BlockNumber(ctx) // This call should not fail since it's a getter if err != nil { - return nil, err + wrappedError := BindingError("EthClient.blockNumber", err) + return nil, wrappedError } - return r.GetSlashableSharesForOperatorSetsBefore(ctx, operatorSets, uint32(currentBlock)) + + operatorSetStakes, err := r.GetSlashableSharesForOperatorSetsBefore(ctx, operatorSets, uint32(currentBlock)) + if err != nil { + wrappedError := NestedError("GetSlashableSharesForOperatorSetsBefore", err) + return nil, wrappedError + } + + return operatorSetStakes, nil } // GetSlashableSharesForOperatorSetsBefore returns the strategies the operatorSets take into account, their @@ -620,13 +818,15 @@ func (r *ChainReader) GetSlashableSharesForOperatorSetsBefore( for i, operatorSet := range operatorSets { operators, err := r.GetOperatorsForOperatorSet(ctx, operatorSet) if err != nil { - return nil, err + wrappedError := NestedError("GetOperatorsForOperatorSet", err) + return nil, wrappedError } strategies, err := r.GetStrategiesForOperatorSet(ctx, operatorSet) // If operator setId is 0 will fail on if above if err != nil { - return nil, err + wrappedError := NestedError("GetStrategiesForOperatorSet", err) + return nil, wrappedError } slashableShares, err := r.allocationManager.GetMinimumSlashableStake( @@ -641,7 +841,8 @@ func (r *ChainReader) GetSlashableSharesForOperatorSetsBefore( ) // This call should not fail since it's a getter if err != nil { - return nil, err + wrappedError := BindingError("AllocationManager.getMinimumSlashableStake", err) + return nil, wrappedError } operatorSetStakes[i] = OperatorSetStakes{ @@ -660,15 +861,18 @@ func (r *ChainReader) GetAllocationDelay( operatorAddress gethcommon.Address, ) (uint32, error) { if r.allocationManager == nil { - return 0, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return 0, wrappedError } isSet, delay, err := r.allocationManager.GetAllocationDelay(&bind.CallOpts{Context: ctx}, operatorAddress) // This call should not fail since it's a getter if err != nil { - return 0, err + wrappedError := BindingError("AllocationManager.getAllocationDelay", err) + return 0, wrappedError } if !isSet { - return 0, errors.New("allocation delay not set") + wrappedError := OtherError("Allocation delay not set", err) + return 0, wrappedError } return delay, nil } @@ -678,9 +882,17 @@ func (r *ChainReader) GetRegisteredSets( operatorAddress gethcommon.Address, ) ([]allocationmanager.OperatorSet, error) { if r.allocationManager == nil { - return nil, errors.New("AllocationManager contract not provided") + wrappedError := MissingContractError("AllocationManager") + return nil, wrappedError + } + + registeredSets, err := r.allocationManager.GetRegisteredSets(&bind.CallOpts{Context: ctx}, operatorAddress) + if err != nil { + wrappedError := BindingError("AllocationManager.getRegisteredSets", err) + return nil, wrappedError } - return r.allocationManager.GetRegisteredSets(&bind.CallOpts{Context: ctx}, operatorAddress) + + return registeredSets, nil } func (r *ChainReader) CanCall( @@ -690,6 +902,11 @@ func (r *ChainReader) CanCall( target gethcommon.Address, selector [4]byte, ) (bool, error) { + if r.permissionController == nil { + wrappedError := MissingContractError("PermissionController") + return false, wrappedError + } + canCall, err := r.permissionController.CanCall( &bind.CallOpts{Context: ctx}, accountAddress, @@ -699,7 +916,8 @@ func (r *ChainReader) CanCall( ) // This call should not fail since it's a getter if err != nil { - return false, utils.WrapError("call to permission controller failed", err) + wrappedError := BindingError("PermissionController.canCall", err) + return false, wrappedError } return canCall, nil } @@ -710,6 +928,11 @@ func (r *ChainReader) ListAppointees( target gethcommon.Address, selector [4]byte, ) ([]gethcommon.Address, error) { + if r.permissionController == nil { + wrappedError := MissingContractError("PermissionController") + return nil, wrappedError + } + appointees, err := r.permissionController.GetAppointees( &bind.CallOpts{Context: ctx}, accountAddress, @@ -718,7 +941,8 @@ func (r *ChainReader) ListAppointees( ) // This call should not fail since it's a getter if err != nil { - return nil, utils.WrapError("call to permission controller failed", err) + wrappedError := BindingError("PermissionController.getAppointees", err) + return nil, wrappedError } return appointees, nil } @@ -728,6 +952,11 @@ func (r *ChainReader) ListAppointeePermissions( accountAddress gethcommon.Address, appointeeAddress gethcommon.Address, ) ([]gethcommon.Address, [][4]byte, error) { + if r.permissionController == nil { + wrappedError := MissingContractError("PermissionController") + return nil, nil, wrappedError + } + targets, selectors, err := r.permissionController.GetAppointeePermissions( &bind.CallOpts{Context: ctx}, accountAddress, @@ -735,7 +964,8 @@ func (r *ChainReader) ListAppointeePermissions( ) // This call should not fail since it's a getter if err != nil { - return nil, nil, utils.WrapError("call to permission controller failed", err) + wrappedError := BindingError("PermissionController.getAppointeePermissions", err) + return nil, nil, wrappedError } return targets, selectors, nil } @@ -744,10 +974,16 @@ func (r *ChainReader) ListPendingAdmins( ctx context.Context, accountAddress gethcommon.Address, ) ([]gethcommon.Address, error) { + if r.permissionController == nil { + wrappedError := MissingContractError("PermissionController") + return nil, wrappedError + } + pendingAdmins, err := r.permissionController.GetPendingAdmins(&bind.CallOpts{Context: ctx}, accountAddress) // This call should not fail since it's a getter if err != nil { - return nil, utils.WrapError("call to permission controller failed", err) + wrappedError := BindingError("PermissionController.getPendingAdmins", err) + return nil, wrappedError } return pendingAdmins, nil } @@ -756,10 +992,16 @@ func (r *ChainReader) ListAdmins( ctx context.Context, accountAddress gethcommon.Address, ) ([]gethcommon.Address, error) { + if r.permissionController == nil { + wrappedError := MissingContractError("PermissionController") + return nil, wrappedError + } + pendingAdmins, err := r.permissionController.GetAdmins(&bind.CallOpts{Context: ctx}, accountAddress) // This call should not fail since it's a getter if err != nil { - return nil, utils.WrapError("call to permission controller failed", err) + wrappedError := BindingError("PermissionController.getAdmins", err) + return nil, wrappedError } return pendingAdmins, nil } @@ -769,6 +1011,11 @@ func (r *ChainReader) IsPendingAdmin( accountAddress gethcommon.Address, pendingAdminAddress gethcommon.Address, ) (bool, error) { + if r.permissionController == nil { + wrappedError := MissingContractError("PermissionController") + return false, wrappedError + } + isPendingAdmin, err := r.permissionController.IsPendingAdmin( &bind.CallOpts{Context: ctx}, accountAddress, @@ -776,7 +1023,8 @@ func (r *ChainReader) IsPendingAdmin( ) // This call should not fail since it's a getter if err != nil { - return false, utils.WrapError("call to permission controller failed", err) + wrappedError := BindingError("PermissionController.isPendingAdmin", err) + return false, wrappedError } return isPendingAdmin, nil } @@ -786,10 +1034,16 @@ func (r *ChainReader) IsAdmin( accountAddress gethcommon.Address, adminAddress gethcommon.Address, ) (bool, error) { + if r.permissionController == nil { + wrappedError := MissingContractError("PermissionController") + return false, wrappedError + } + isAdmin, err := r.permissionController.IsAdmin(&bind.CallOpts{Context: ctx}, accountAddress, adminAddress) // This call should not fail since it's a getter if err != nil { - return false, utils.WrapError("call to permission controller failed", err) + wrappedError := BindingError("PermissionController.isAdmin", err) + return false, wrappedError } return isAdmin, nil } diff --git a/chainio/clients/elcontracts/reader_test.go b/chainio/clients/elcontracts/reader_test.go index 89ac7b17..101002ef 100644 --- a/chainio/clients/elcontracts/reader_test.go +++ b/chainio/clients/elcontracts/reader_test.go @@ -348,7 +348,10 @@ func TestGetRootIndexFromRootHash(t *testing.T) { root, ) assert.Error(t, err) - assert.Equal(t, err.Error(), "execution reverted: custom error 0x504570e3", + assert.ErrorContains( + t, + err, + "execution reverted: custom error 0x504570e3", "GetRootIndexFromHash should return an InvalidRoot() error", ) assert.Zero(t, root_index) @@ -771,13 +774,21 @@ func TestContractErrorCases(t *testing.T) { t.Run("GetStrategyAndUnderlyingToken", func(t *testing.T) { _, _, err := chainReader.GetStrategyAndUnderlyingToken(ctx, strategyAddr) assert.Error(t, err) - assert.Equal(t, err.Error(), "Failed to fetch token contract: no contract code at given address") + assert.ErrorContains( + t, + err, + "no contract code at given address", + ) }) t.Run("GetStrategyAndUnderlyingERC20Token", func(t *testing.T) { _, _, _, err := chainReader.GetStrategyAndUnderlyingERC20Token(ctx, strategyAddr) assert.Error(t, err) - assert.Equal(t, err.Error(), "Failed to fetch token contract: no contract code at given address") + assert.ErrorContains( + t, + err, + "no contract code at given address", + ) }) } @@ -804,12 +815,14 @@ func TestInvalidConfig(t *testing.T) { // IsOperatorRegistered needs a correct DelegationManagerAddress _, err := chainReader.IsOperatorRegistered(context.Background(), operator) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("DelegationManager")) }) t.Run("get operator details with invalid config", func(t *testing.T) { // GetOperatorDetails needs a correct DelegationManagerAddress _, err := chainReader.GetOperatorDetails(context.Background(), operator) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("DelegationManager")) }) t.Run("get operator avs", func(t *testing.T) { @@ -819,9 +832,11 @@ func TestInvalidConfig(t *testing.T) { common.MaxAddress, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) _, err = chainReader.GetOperatorPISplit(context.Background(), common.HexToAddress(operatorAddr)) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) }) t.Run("try to get strategy and underlying token with wrong strategy address", func(t *testing.T) { @@ -832,10 +847,16 @@ func TestInvalidConfig(t *testing.T) { // GetOperatorSharesInStrategy needs a correct DelegationManagerAddress _, err := chainReader.GetOperatorSharesInStrategy(context.Background(), operatorAddr, strategyAddr) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("DelegationManager")) // GetStrategyAndUnderlyingToken needs a correct StrategyAddress _, _, err = chainReader.GetStrategyAndUnderlyingToken(context.Background(), strategyAddr) require.Error(t, err) + assert.ErrorContains( + t, + err, + "no contract code at given address", + ) _, _, _, err = chainReader.GetStrategyAndUnderlyingERC20Token(context.Background(), strategyAddr) require.Error(t, err) @@ -857,6 +878,7 @@ func TestInvalidConfig(t *testing.T) { expiry, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("DelegationManager")) // CalculateOperatorAVSRegistrationDigestHash needs a correct AvsDirectoryAddress _, err = chainReader.CalculateOperatorAVSRegistrationDigestHash(context.Background(), @@ -864,19 +886,23 @@ func TestInvalidConfig(t *testing.T) { staker, approverSalt, expiry) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AVSDirectory")) }) t.Run("get root with invalid config", func(t *testing.T) { // GetDistributionRootsLength needs a correct RewardsCoordinatorAddress _, err := chainReader.GetDistributionRootsLength(context.Background()) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) // GetRootIndexFromHash needs a correct RewardsCoordinatorAddress _, err = chainReader.GetRootIndexFromHash(context.Background(), [32]byte{}) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) _, err = chainReader.GetCurrentClaimableDistributionRoot(context.Background()) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) }) t.Run("get magnitudes, rewards and claims with invalid config", func(t *testing.T) { @@ -885,6 +911,7 @@ func TestInvalidConfig(t *testing.T) { _, err = chainReader.GetCurrentClaimableDistributionRoot(context.Background()) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) _, err := chainReader.GetCumulativeClaimed( context.Background(), @@ -892,6 +919,7 @@ func TestInvalidConfig(t *testing.T) { common.HexToAddress(testutils.ANVIL_SECOND_ADDRESS), ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) _, err = chainReader.GetMaxMagnitudes( context.Background(), @@ -899,6 +927,7 @@ func TestInvalidConfig(t *testing.T) { []common.Address{strategyAddr}, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) _, err = chainReader.GetAllocatableMagnitude( context.Background(), @@ -906,27 +935,33 @@ func TestInvalidConfig(t *testing.T) { strategyAddr, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) _, err = chainReader.GetAllocationInfo(context.Background(), common.HexToAddress(operatorAddr), strategyAddr) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) _, err = chainReader.GetAllocationDelay(context.Background(), common.HexToAddress(operatorAddr)) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) _, err = chainReader.CheckClaim( context.Background(), rewardscoordinator.IRewardsCoordinatorTypesRewardsMerkleClaim{}, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) _, err = chainReader.CurrRewardsCalculationEndTimestamp(context.Background()) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("RewardsCoordinator")) }) 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)) - require.Error(t, err) + assert.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("DelegationManager")) }) t.Run("try to get the delegated operator shares with invalid config", func(t *testing.T) { @@ -937,18 +972,21 @@ func TestInvalidConfig(t *testing.T) { big.NewInt(0), ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("DelegationManager")) }) t.Run("try to get the number of operator sets for an operator with invalid config", func(t *testing.T) { // GetNumOperatorSetsForOperator needs a correct AllocationManagerAddress _, err := chainReader.GetNumOperatorSetsForOperator(context.Background(), common.HexToAddress(operator.Address)) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) }) t.Run("try to get the operator sets for an operator with invalid config", func(t *testing.T) { // GetOperatorSetsForOperator needs a correct AllocationManagerAddress _, err := chainReader.GetOperatorSetsForOperator(context.Background(), common.HexToAddress(operator.Address)) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) }) t.Run("try to check if the operator is registered in an operator set with set id 0 and an invalid config", @@ -966,6 +1004,7 @@ func TestInvalidConfig(t *testing.T) { operatorSet, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AVSDirectory")) }, ) @@ -984,6 +1023,7 @@ func TestInvalidConfig(t *testing.T) { operatorSet, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) }, ) @@ -1001,6 +1041,7 @@ func TestInvalidConfig(t *testing.T) { operatorSet, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) }, ) @@ -1018,6 +1059,7 @@ func TestInvalidConfig(t *testing.T) { operatorSet, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) }, ) @@ -1035,6 +1077,7 @@ func TestInvalidConfig(t *testing.T) { operatorSet, ) require.Error(t, err) + assert.Equal(t, err.Error(), elcontracts.CommonErrorMissingContract("AllocationManager")) }, ) } @@ -1222,14 +1265,18 @@ func TestOperatorSetsWithWrongInput(t *testing.T) { require.NoError(t, err) t.Run("test operator set with invalid id", func(t *testing.T) { + legacyAVSsNotSupportedErrorMsg := elcontracts.OtherError("Method not supported for legacy AVSs", nil).Error() _, err := chainReader.GetOperatorsForOperatorSet(ctx, operatorSet) require.Error(t, err) + assert.EqualError(t, err, legacyAVSsNotSupportedErrorMsg) _, err = chainReader.GetNumOperatorsForOperatorSet(ctx, operatorSet) require.Error(t, err) + assert.EqualError(t, err, legacyAVSsNotSupportedErrorMsg) _, err = chainReader.GetStrategiesForOperatorSet(ctx, operatorSet) require.Error(t, err) + assert.EqualError(t, err, legacyAVSsNotSupportedErrorMsg) strategies := []common.Address{contractAddrs.Erc20MockStrategy} @@ -1240,6 +1287,8 @@ func TestOperatorSetsWithWrongInput(t *testing.T) { strategies, ) require.Error(t, err) + // This is the returned error, but i don't think it's the inteded + // assert.Equal(t, err.Error(), "Missing needed contract(1) - AllocationManager contract not provided") }) t.Run("get slashable shares with invalid operatorSet", func(t *testing.T) { @@ -1254,6 +1303,11 @@ func TestOperatorSetsWithWrongInput(t *testing.T) { _, err = chainReader.GetSlashableSharesForOperatorSetsBefore(context.Background(), operatorSets, 10) require.Error(t, err) + assert.ErrorContains( + t, + err, + "Method not supported for legacy AVSs", + ) }) } @@ -1313,7 +1367,7 @@ func TestFailingNetwork(t *testing.T) { contractAddrs.Erc20MockStrategy, ) assert.Error(t, err) - assert.Zero(t, shares) + assert.Zero(t, shares.Int64()) }) t.Run("calculate delegation approval digest hash", func(t *testing.T) { @@ -1423,5 +1477,10 @@ func TestCreateRederFromConfig(t *testing.T) { _, err = elcontracts.NewReaderFromConfig(config, ethHttpClient, logger) require.Error(t, err) + assert.ErrorContains( + t, + err, + "no contract code at given address", + ) }) }