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  }