github.com/InjectiveLabs/sdk-go@v1.53.0/chain/types/account.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "strings" 8 9 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 10 "gopkg.in/yaml.v2" 11 12 "cosmossdk.io/errors" 13 codectypes "github.com/cosmos/cosmos-sdk/codec/types" 14 sdk "github.com/cosmos/cosmos-sdk/types" 15 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 16 17 ethcmn "github.com/ethereum/go-ethereum/common" 18 ethcrypto "github.com/ethereum/go-ethereum/crypto" 19 ) 20 21 var ( 22 _ sdk.AccountI = (*EthAccount)(nil) 23 _ authtypes.GenesisAccount = (*EthAccount)(nil) 24 _ codectypes.UnpackInterfacesMessage = (*EthAccount)(nil) 25 ) 26 27 var EmptyCodeHash = ethcrypto.Keccak256(nil) 28 29 // ---------------------------------------------------------------------------- 30 // Main Ethermint account 31 // ---------------------------------------------------------------------------- 32 33 // ProtoAccount defines the prototype function for BaseAccount used for an 34 // AccountKeeper. 35 func ProtoAccount() sdk.AccountI { 36 return &EthAccount{ 37 BaseAccount: &authtypes.BaseAccount{}, 38 CodeHash: ethcrypto.Keccak256(nil), 39 } 40 } 41 42 // EthAddress returns the account address ethereum format. 43 func (acc EthAccount) EthAddress() ethcmn.Address { 44 return ethcmn.BytesToAddress(acc.GetAddress().Bytes()) 45 } 46 47 type ethAccountPretty struct { 48 Address string `json:"address" yaml:"address"` 49 EthAddress string `json:"eth_address" yaml:"eth_address"` 50 PubKey string `json:"public_key" yaml:"public_key"` 51 AccountNumber uint64 `json:"account_number" yaml:"account_number"` 52 Sequence uint64 `json:"sequence" yaml:"sequence"` 53 CodeHash string `json:"code_hash" yaml:"code_hash"` 54 } 55 56 // MarshalYAML returns the YAML representation of an account. 57 func (acc EthAccount) MarshalYAML() (interface{}, error) { 58 alias := ethAccountPretty{ 59 Address: acc.Address, 60 EthAddress: acc.EthAddress().String(), 61 AccountNumber: acc.AccountNumber, 62 Sequence: acc.Sequence, 63 CodeHash: ethcmn.Bytes2Hex(acc.CodeHash), 64 PubKey: "", 65 } 66 67 var err error 68 69 bz, err := yaml.Marshal(alias) 70 if err != nil { 71 return nil, err 72 } 73 74 return string(bz), err 75 } 76 77 // MarshalJSON returns the JSON representation of an EthAccount. 78 func (acc EthAccount) MarshalJSON() ([]byte, error) { 79 var ethAddress = "" 80 81 if acc.BaseAccount != nil && acc.Address == "" { 82 ethAddress = acc.EthAddress().String() 83 } 84 85 alias := ethAccountPretty{ 86 Address: acc.Address, 87 EthAddress: ethAddress, 88 AccountNumber: acc.AccountNumber, 89 Sequence: acc.Sequence, 90 CodeHash: ethcmn.Bytes2Hex(acc.CodeHash), 91 PubKey: "", 92 } 93 94 return json.Marshal(alias) 95 } 96 97 // UnmarshalJSON unmarshals raw JSON bytes into an EthAccount. 98 func (acc *EthAccount) UnmarshalJSON(bz []byte) error { 99 var ( 100 alias ethAccountPretty 101 err error 102 ) 103 104 if err := json.Unmarshal(bz, &alias); err != nil { 105 return err 106 } 107 108 switch { 109 case alias.Address != "" && alias.EthAddress != "": 110 // Both addresses provided. Verify correctness 111 ethAddress := ethcmn.HexToAddress(alias.EthAddress) 112 113 var address sdk.AccAddress 114 address, err = sdk.AccAddressFromBech32(alias.Address) 115 if err != nil { 116 return err 117 } 118 119 ethAddressFromAccAddress := ethcmn.BytesToAddress(address.Bytes()) 120 121 if !bytes.Equal(ethAddress.Bytes(), address.Bytes()) { 122 err = errors.Wrapf( 123 sdkerrors.ErrInvalidAddress, 124 "expected %s, got %s", 125 ethAddressFromAccAddress.String(), ethAddress.String(), 126 ) 127 } 128 129 case alias.Address != "" && alias.EthAddress == "": 130 // unmarshal sdk.AccAddress only. Do nothing here 131 case alias.Address == "" && alias.EthAddress != "": 132 // retrieve sdk.AccAddress from ethereum address 133 ethAddress := ethcmn.HexToAddress(alias.EthAddress) 134 alias.Address = sdk.AccAddress(ethAddress.Bytes()).String() 135 case alias.Address == "" && alias.EthAddress == "": 136 err = errors.Wrapf( 137 sdkerrors.ErrInvalidAddress, 138 "account must contain address in Ethereum Hex or Cosmos Bech32 format", 139 ) 140 } 141 142 if err != nil { 143 return err 144 } 145 146 acc.BaseAccount = &authtypes.BaseAccount{ 147 Address: alias.Address, 148 AccountNumber: alias.AccountNumber, 149 Sequence: alias.Sequence, 150 } 151 152 acc.CodeHash = ethcmn.HexToHash(alias.CodeHash).Bytes() 153 154 return nil 155 } 156 157 // String implements the fmt.Stringer interface 158 func (acc EthAccount) String() string { 159 out, _ := yaml.Marshal(acc) 160 return string(out) 161 } 162 163 func CosmosAddressToEthAddress(addr string) (ethcmn.Address, error) { 164 if strings.HasPrefix(addr, sdk.GetConfig().GetBech32AccountAddrPrefix()) { 165 // Check to see if address is Cosmos bech32 formatted 166 toAddr, err := sdk.AccAddressFromBech32(addr) 167 if err != nil { 168 return ethcmn.Address{}, errors.Wrap(err, "must provide a valid Bech32 address") 169 } 170 ethAddr := ethcmn.BytesToAddress(toAddr.Bytes()) 171 return ethAddr, nil 172 } 173 174 if !strings.HasPrefix(addr, "0x") { 175 addr = "0x" + addr 176 } 177 178 valid := ethcmn.IsHexAddress(addr) 179 if !valid { 180 return ethcmn.Address{}, fmt.Errorf("%s is not a valid Ethereum or Cosmos address", addr) 181 } 182 183 ethAddr := ethcmn.HexToAddress(addr) 184 185 return ethAddr, nil 186 }