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 }