github.com/0xsequence/ethkit@v1.25.0/cmd/ethkit/balance.go (about) 1 package main 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math/big" 8 "net/url" 9 "strconv" 10 11 "github.com/spf13/cobra" 12 13 "github.com/0xsequence/ethkit/ethrpc" 14 "github.com/0xsequence/ethkit/go-ethereum/common" 15 "github.com/0xsequence/ethkit/go-ethereum/params" 16 ) 17 18 const ( 19 flagBalanceBlock = "block" 20 flagBalanceEther = "ether" 21 flagBalanceRpcUrl = "rpc-url" 22 ) 23 24 func init() { 25 rootCmd.AddCommand(NewBalanceCmd()) 26 } 27 28 func NewBalanceCmd() *cobra.Command { 29 c := &balance{} 30 cmd := &cobra.Command{ 31 Use: "balance [account]", 32 Short: "Get the balance of an account", 33 Aliases: []string{"b"}, 34 Args: cobra.ExactArgs(1), 35 RunE: c.Run, 36 } 37 38 cmd.Flags().StringP(flagBalanceBlock, "B", "latest", "The block height to query at") 39 cmd.Flags().BoolP(flagBalanceEther, "e", false, "Format the balance in ether") 40 cmd.Flags().StringP(flagBalanceRpcUrl, "r", "", "The RPC endpoint to the blockchain node to interact with") 41 42 return cmd 43 } 44 45 type balance struct { 46 } 47 48 func (c *balance) Run(cmd *cobra.Command, args []string) error { 49 fAccount := cmd.Flags().Args()[0] 50 fBlock, err := cmd.Flags().GetString(flagBalanceBlock) 51 if err != nil { 52 return err 53 } 54 fEther, err := cmd.Flags().GetBool(flagBalanceEther) 55 if err != nil { 56 return err 57 } 58 fRpc, err := cmd.Flags().GetString(flagBalanceRpcUrl) 59 if err != nil { 60 return err 61 } 62 63 if !common.IsHexAddress(fAccount) { 64 return errors.New("error: please provide a valid account address (e.g. 0x213a286A1AF3Ac010d4F2D66A52DeAf762dF7742)") 65 } 66 67 if _, err = url.ParseRequestURI(fRpc); err != nil { 68 return errors.New("error: please provide a valid rpc url (e.g. https://nodes.sequence.app/mainnet)") 69 } 70 71 provider, err := ethrpc.NewProvider(fRpc) 72 if err != nil { 73 return err 74 } 75 76 block, err := strconv.ParseUint(fBlock, 10, 64) 77 if err != nil { 78 // TODO: implement support for all tags: earliest, latest, pending, finalized, safe 79 if fBlock == "latest" { 80 bh, err := provider.BlockNumber(context.Background()) 81 if err != nil { 82 return err 83 } 84 block = bh 85 } else { 86 return errors.New("error: invalid block height") 87 } 88 } 89 90 wei, err := provider.BalanceAt(context.Background(), common.HexToAddress(fAccount), big.NewInt(int64(block))) 91 if err != nil { 92 return err 93 } 94 95 if fEther { 96 bal := weiToEther(wei) 97 fmt.Fprintln(cmd.OutOrStdout(), bal, "ether") 98 } else { 99 fmt.Fprintln(cmd.OutOrStdout(), wei, "wei") 100 } 101 102 return nil 103 } 104 105 // https://github.com/ethereum/go-ethereum/issues/21221 106 func weiToEther(wei *big.Int) *big.Float { 107 f := new(big.Float) 108 f.SetPrec(236) // IEEE 754 octuple-precision binary floating-point format: binary256 109 f.SetMode(big.ToNearestEven) 110 fWei := new(big.Float) 111 fWei.SetPrec(236) // IEEE 754 octuple-precision binary floating-point format: binary256 112 fWei.SetMode(big.ToNearestEven) 113 114 return f.Quo(fWei.SetInt(wei), big.NewFloat(params.Ether)) 115 }