github.com/InjectiveLabs/sdk-go@v1.53.0/chain/peggy/types/ethereum.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 8 "cosmossdk.io/errors" 9 "cosmossdk.io/math" 10 sdk "github.com/cosmos/cosmos-sdk/types" 11 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 12 gethcommon "github.com/ethereum/go-ethereum/common" 13 ) 14 15 const ( 16 // PeggyDenomPrefix indicates the prefix for all assests minted by this module 17 PeggyDenomPrefix = ModuleName 18 19 // PeggyDenomSeparator is the separator for peggy denoms 20 PeggyDenomSeparator = "" 21 22 // ETHContractAddressLen is the length of contract address bytes 23 ETHContractAddressLen = 20 24 25 // PeggyDenomLen is the length of the denoms generated by the peggy module 26 PeggyDenomLen = len(PeggyDenomPrefix) + len(PeggyDenomSeparator) + ETHContractAddressLen 27 28 // ZeroAddress is an EthAddress containing the zero ethereum address 29 ZeroAddressString = "0x0000000000000000000000000000000000000000" 30 ) 31 32 // EthAddrLessThan migrates the Ethereum address less than function 33 func EthAddrLessThan(e, o string) bool { 34 return bytes.Compare([]byte(e), []byte(o)) == -1 35 } 36 37 // Returns a new EthAddress with 0x0000000000000000000000000000000000000000 as the wrapped address 38 func ZeroAddress() gethcommon.Address { 39 return gethcommon.HexToAddress(ZeroAddressString) 40 } 41 42 // Creates a new EthAddress from a string, performing validation and returning any validation errors 43 func NewEthAddress(address string) (*gethcommon.Address, error) { 44 if err := ValidateEthAddress(address); err != nil { 45 return nil, errors.Wrap(err, "invalid input address") 46 } 47 48 addr := gethcommon.HexToAddress(address) 49 return &addr, nil 50 } 51 52 // ValidateEthAddress validates the ethereum address strings 53 func ValidateEthAddress(address string) error { 54 if address == "" { 55 return fmt.Errorf("empty") 56 } 57 if !gethcommon.IsHexAddress(address) { 58 return fmt.Errorf("%s is not a valid ETH address", address) 59 } 60 return nil 61 } 62 63 // NewERC20Token returns a new instance of an ERC20 64 func NewERC20Token(amount uint64, contract gethcommon.Address) *ERC20Token { 65 return &ERC20Token{Amount: math.NewIntFromUint64(amount), Contract: contract.Hex()} 66 } 67 68 func NewSDKIntERC20Token(amount math.Int, contract gethcommon.Address) *ERC20Token { 69 return &ERC20Token{Amount: amount, Contract: contract.Hex()} 70 } 71 72 // PeggyCoin returns the peggy representation of an ERC20 token 73 func (e *ERC20Token) PeggyCoin() sdk.Coin { 74 return sdk.NewCoin(PeggyDenomString(gethcommon.HexToAddress(e.Contract)), e.Amount) 75 } 76 77 type PeggyDenom []byte 78 79 func (p PeggyDenom) String() string { 80 contractAddress, err := p.TokenContract() 81 if err != nil { 82 // the case of unparseable peggy denom 83 return fmt.Sprintf("%x(error: %s)", []byte(p), err.Error()) 84 } 85 86 return PeggyDenomString(contractAddress) 87 } 88 89 func (p PeggyDenom) TokenContract() (gethcommon.Address, error) { 90 fullPrefix := []byte(PeggyDenomPrefix + PeggyDenomSeparator) 91 if !bytes.HasPrefix(p, fullPrefix) { 92 err := fmt.Errorf("denom '%x' byte prefix not equal to expected '%x'", []byte(p), fullPrefix) 93 return gethcommon.Address{}, err 94 } 95 96 addressBytes := bytes.TrimPrefix(p, fullPrefix) 97 if len(addressBytes) != ETHContractAddressLen { 98 err := fmt.Errorf("failed to validate Ethereum address bytes: %x", addressBytes) 99 return gethcommon.Address{}, err 100 } 101 102 return gethcommon.BytesToAddress(addressBytes), nil 103 } 104 105 func NewPeggyDenom(tokenContract gethcommon.Address) PeggyDenom { 106 buf := make([]byte, 0, PeggyDenomLen) 107 buf = append(buf, PeggyDenomPrefix+PeggyDenomSeparator...) 108 buf = append(buf, tokenContract.Bytes()...) 109 110 return PeggyDenom(buf) 111 } 112 113 func NewPeggyDenomFromString(denom string) (PeggyDenom, error) { 114 fullPrefix := PeggyDenomPrefix + PeggyDenomSeparator 115 if !strings.HasPrefix(denom, fullPrefix) { 116 err := fmt.Errorf("denom '%s' string prefix not equal to expected '%s'", denom, fullPrefix) 117 return nil, err 118 } 119 120 addressHex := strings.TrimPrefix(denom, fullPrefix) 121 if err := ValidateEthAddress(addressHex); err != nil { 122 return nil, err 123 } 124 125 peggyDenom := NewPeggyDenom(gethcommon.HexToAddress(addressHex)) 126 return peggyDenom, nil 127 } 128 129 func PeggyDenomString(tokenContract gethcommon.Address) string { 130 return fmt.Sprintf("%s%s%s", PeggyDenomPrefix, PeggyDenomSeparator, tokenContract.Hex()) 131 } 132 133 // ValidateBasic permforms stateless validation 134 func (e *ERC20Token) ValidateBasic() error { 135 if err := ValidateEthAddress(e.Contract); err != nil { 136 return errors.Wrap(err, "ethereum address") 137 } 138 139 if !e.PeggyCoin().IsValid() { 140 return errors.Wrap(sdkerrors.ErrInvalidCoins, e.PeggyCoin().String()) 141 } 142 143 if !e.PeggyCoin().IsPositive() { 144 return errors.Wrap(sdkerrors.ErrInvalidCoins, e.PeggyCoin().String()) 145 } 146 147 return nil 148 } 149 150 // Add adds one ERC20 to another 151 func (e *ERC20Token) Add(o *ERC20Token) (*ERC20Token, error) { 152 if e.Contract != o.Contract { 153 return nil, fmt.Errorf("invalid contract address") 154 } 155 156 sum := e.Amount.Add(o.Amount) 157 if !sum.IsUint64() { 158 return nil, fmt.Errorf("invalid amount") 159 } 160 161 return NewERC20Token(sum.Uint64(), gethcommon.HexToAddress(e.Contract)), nil 162 }