github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/eth/stakingpool.go (about)

     1  package eth
     2  
     3  import (
     4  	"math/big"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/ethereum/go-ethereum/common/hexutil"
     9  	"github.com/golang/glog"
    10  	"github.com/juju/errors"
    11  	"github.com/trezor/blockbook/bchain"
    12  )
    13  
    14  func (b *EthereumRPC) initStakingPools(coinShortcut string) error {
    15  	// for now only single staking pool
    16  	envVar := strings.ToUpper(coinShortcut) + "_STAKING_POOL_CONTRACT"
    17  	envValue := os.Getenv(envVar)
    18  	if envValue != "" {
    19  		parts := strings.Split(envValue, "/")
    20  		if len(parts) != 2 {
    21  			glog.Errorf("Wrong format of environment variable %s=%s, expecting value '<pool name>/<pool contract>', staking pools not enabled", envVar, envValue)
    22  			return nil
    23  		}
    24  		b.supportedStakingPools = []string{envValue}
    25  		b.stakingPoolNames = []string{parts[0]}
    26  		b.stakingPoolContracts = []string{parts[1]}
    27  		glog.Info("Support of staking pools enabled with these pools: ", b.supportedStakingPools)
    28  	}
    29  	return nil
    30  }
    31  
    32  func (b *EthereumRPC) EthereumTypeGetSupportedStakingPools() []string {
    33  	return b.supportedStakingPools
    34  }
    35  
    36  func (b *EthereumRPC) EthereumTypeGetStakingPoolsData(addrDesc bchain.AddressDescriptor) ([]bchain.StakingPoolData, error) {
    37  	// for now only single staking pool - Everstake
    38  	addr := hexutil.Encode(addrDesc)[2:]
    39  	if len(b.supportedStakingPools) == 1 {
    40  		data, err := b.everstakePoolData(addr, b.stakingPoolContracts[0], b.stakingPoolNames[0])
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  		if data != nil {
    45  			return []bchain.StakingPoolData{*data}, nil
    46  		}
    47  	}
    48  	return nil, nil
    49  }
    50  
    51  const everstakePendingBalanceOfMethodSignature = "0x59b8c763"          // pendingBalanceOf(address)
    52  const everstakePendingDepositedBalanceOfMethodSignature = "0x80f14ecc" // pendingDepositedBalanceOf(address)
    53  const everstakeDepositedBalanceOfMethodSignature = "0x68b48254"        // depositedBalanceOf(address)
    54  const everstakeWithdrawRequestMethodSignature = "0x14cbc46a"           // withdrawRequest(address)
    55  const everstakeRestakedRewardOfMethodSignature = "0x0c98929a"          // restakedRewardOf(address)
    56  const everstakeAutocompoundBalanceOfMethodSignature = "0x2fec7966"     // autocompoundBalanceOf(address)
    57  
    58  func isZeroBigInt(b *big.Int) bool {
    59  	return len(b.Bits()) == 0
    60  }
    61  
    62  func (b *EthereumRPC) everstakeBalanceTypeContractCall(signature, addr, contract string) (string, error) {
    63  	req := signature + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr):] + addr
    64  	return b.ethCall(req, contract)
    65  }
    66  
    67  func (b *EthereumRPC) everstakeContractCallSimpleNumeric(signature, addr, contract string) (*big.Int, error) {
    68  	data, err := b.everstakeBalanceTypeContractCall(signature, addr, contract)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	r := parseSimpleNumericProperty(data)
    73  	if r == nil {
    74  		return nil, errors.New("Invalid balance")
    75  	}
    76  	return r, nil
    77  }
    78  
    79  func (b *EthereumRPC) everstakePoolData(addr, contract, name string) (*bchain.StakingPoolData, error) {
    80  	poolData := bchain.StakingPoolData{
    81  		Contract: contract,
    82  		Name:     name,
    83  	}
    84  	allZeros := true
    85  
    86  	value, err := b.everstakeContractCallSimpleNumeric(everstakePendingBalanceOfMethodSignature, addr, contract)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	poolData.PendingBalance = *value
    91  	allZeros = allZeros && isZeroBigInt(value)
    92  
    93  	value, err = b.everstakeContractCallSimpleNumeric(everstakePendingDepositedBalanceOfMethodSignature, addr, contract)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	poolData.PendingDepositedBalance = *value
    98  	allZeros = allZeros && isZeroBigInt(value)
    99  
   100  	value, err = b.everstakeContractCallSimpleNumeric(everstakeDepositedBalanceOfMethodSignature, addr, contract)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	poolData.DepositedBalance = *value
   105  	allZeros = allZeros && isZeroBigInt(value)
   106  
   107  	data, err := b.everstakeBalanceTypeContractCall(everstakeWithdrawRequestMethodSignature, addr, contract)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	value = parseSimpleNumericProperty(data)
   112  	if value == nil {
   113  		return nil, errors.New("Invalid balance")
   114  	}
   115  	poolData.WithdrawTotalAmount = *value
   116  	allZeros = allZeros && isZeroBigInt(value)
   117  	value = parseSimpleNumericProperty(data[64+2:])
   118  	if value == nil {
   119  		return nil, errors.New("Invalid balance")
   120  	}
   121  	poolData.ClaimableAmount = *value
   122  	allZeros = allZeros && isZeroBigInt(value)
   123  
   124  	value, err = b.everstakeContractCallSimpleNumeric(everstakeRestakedRewardOfMethodSignature, addr, contract)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	poolData.RestakedReward = *value
   129  	allZeros = allZeros && isZeroBigInt(value)
   130  
   131  	value, err = b.everstakeContractCallSimpleNumeric(everstakeAutocompoundBalanceOfMethodSignature, addr, contract)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	poolData.AutocompoundBalance = *value
   136  	allZeros = allZeros && isZeroBigInt(value)
   137  
   138  	if allZeros {
   139  		return nil, nil
   140  	}
   141  	return &poolData, nil
   142  }