Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(evm): EVM fungible token protobufs and encoding tests #1936

Merged
merged 9 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1914](https://github.com/NibiruChain/nibiru/pull/1914) - refactor(evm): Remove dead code and document non-EVM ante handler- [#1917](https://github.com/NibiruChain/nibiru/pull/1917) - test(e2e-evm): TypeScript support. Type generation from compiled contracts. Formatter for TS code.
- [#1917](https://github.com/NibiruChain/nibiru/pull/1917) - test(e2e-evm): TypeScript support. Type generation from compiled contracts. Formatter for TS code.
- [#1922](https://github.com/NibiruChain/nibiru/pull/1922) - feat(evm): tracer option is read from the config.
- [#1936](https://github.com/NibiruChain/nibiru/pull/1936) - feat(evm): EVM fungible token protobufs and encoding tests
Unique-Divine marked this conversation as resolved.
Show resolved Hide resolved

#### Dapp modules: perp, spot, oracle, etc

Expand Down
7 changes: 3 additions & 4 deletions e2e/evm/test/contract_send_nibi.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { describe, it, expect, beforeAll } from "bun:test" // eslint-disable-line import/no-unresolved
import { AddressLike, ethers } from "ethers"
import { describe, it, expect } from "bun:test" // eslint-disable-line import/no-unresolved
import { ethers } from "ethers"
import { account, provider, deployContract } from "./setup"
import { SendNibiCompiled } from "../types/ethers-contracts"
import { TypedContractMethod } from "../types/ethers-contracts/common"

type SendMethod = TypedContractMethod<[_to: AddressLike], [void], "payable">
type SendMethod = SendNibiCompiled["sendViaCall"]

const doContractSend = async (sendMethod: SendMethod) => {
const recipientAddress = ethers.Wallet.createRandom().address
Expand Down
128 changes: 128 additions & 0 deletions eth/hex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package eth

import (
"encoding/json"
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
gethcommon "github.com/ethereum/go-ethereum/common"
)

/////////// HexAddr

// HexAddr: An ERC55-compliant hexadecimal-encoded string representing the 20
// byte address of an Ethereum account.
type HexAddr string

var _ sdk.CustomProtobufType = (*HexAddr)(nil)

func NewHexAddr(addr EthAddr) HexAddr {
return HexAddr(addr.Hex())
}

func NewHexAddrFromStr(addr string) (HexAddr, error) {
hexAddr := HexAddr(gethcommon.HexToAddress(addr).Hex())
if !gethcommon.IsHexAddress(addr) {
return hexAddr, fmt.Errorf(
"%s: input \"%s\" is not an ERC55-compliant, 20 byte hex address",
HexAddrError, addr,
)
}
return hexAddr, hexAddr.Valid()
Comment on lines +23 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Robust error handling in NewHexAddrFromStr

The function NewHexAddrFromStr includes comprehensive error checking to ensure that the input string is a valid ERC55-compliant address. This is crucial for avoiding issues related to invalid address formats being used throughout the system.

Consider adding more detailed error messages or logging to assist in debugging if invalid addresses are provided.

- return hexAddr, fmt.Errorf(
-   "%s: input \"%s\" is not an ERC55-compliant, 20 byte hex address",
-   HexAddrError, addr,
- )
+ return hexAddr, fmt.Errorf(
+   "%s: provided input \"%s\" is not a valid ERC55-compliant Ethereum address. Please check the address format and length.",
+   HexAddrError, addr,
+ )
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func NewHexAddrFromStr(addr string) (HexAddr, error) {
hexAddr := HexAddr(gethcommon.HexToAddress(addr).Hex())
if !gethcommon.IsHexAddress(addr) {
return hexAddr, fmt.Errorf(
"%s: input \"%s\" is not an ERC55-compliant, 20 byte hex address",
HexAddrError, addr,
)
}
return hexAddr, hexAddr.Valid()
func NewHexAddrFromStr(addr string) (HexAddr, error) {
hexAddr := HexAddr(gethcommon.HexToAddress(addr).Hex())
if !gethcommon.IsHexAddress(addr) {
return hexAddr, fmt.Errorf(
"%s: provided input \"%s\" is not a valid ERC55-compliant Ethereum address. Please check the address format and length.",
HexAddrError, addr,
)
}
return hexAddr, hexAddr.Valid()

}

// MustNewHexAddrFromStr is the same as [NewHexAddrFromStr], except it panics
// when there's an error.
func MustNewHexAddrFromStr(addr string) HexAddr {
hexAddr, err := NewHexAddrFromStr(addr)
if err != nil {
panic(err)
}
return hexAddr
}

const HexAddrError = "HexAddrError"

func (h HexAddr) Valid() error {
// Check address encoding bijectivity
wantAddr := h.ToAddr().Hex() // gethcommon.Address.Hex()
haveAddr := string(h) // should be equivalent to ↑

if !gethcommon.IsHexAddress(haveAddr) || haveAddr != wantAddr {
return fmt.Errorf(
"%s: Ethereum address is not represented as expected. We have encoding \"%s\" and instead need \"%s\" (gethcommon.Address.Hex)",
HexAddrError, haveAddr, wantAddr,
)
}

return nil
}

func (h HexAddr) ToAddr() EthAddr {
return gethcommon.HexToAddress(string(h))
}

// ToBytes gets the string representation of the underlying address.
func (h HexAddr) ToBytes() []byte {
return h.ToAddr().Bytes()
}

func (h HexAddr) String() string { return h.ToAddr().Hex() }

// Marshal implements the gogo proto custom type interface.
// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md
func (h HexAddr) Marshal() ([]byte, error) {
return []byte(h), nil
}

// MarshalJSON returns the [HexAddr] as JSON bytes.
// Implements the gogo proto custom type interface.
// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md
func (h HexAddr) MarshalJSON() ([]byte, error) {
return []byte("\"" + h.String() + "\""), nil // a string is already JSON
}

// MarshalTo serializes a pre-allocated byte slice ("data") directly into the
// [HexAddr] value, avoiding unnecessary memory allocations.
// MarshalTo implements the gogo proto custom type interface.
// Implements the gogo proto custom type interface.
// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md
func (h *HexAddr) MarshalTo(data []byte) (n int, err error) {
bz := []byte{}
copy(data, bz)
hexAddr, err := NewHexAddrFromStr(string(bz))
*h = hexAddr
return h.Size(), err
}

// Unmarshal implements the gogo proto custom type interface.
// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md
func (h *HexAddr) Unmarshal(data []byte) error {
hexAddr, err := NewHexAddrFromStr(string(data))
*h = hexAddr
return err
}

// UnmarshalJSON implements the gogo proto custom type interface.
// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md
func (h *HexAddr) UnmarshalJSON(bz []byte) error {
text := new(string)
if err := json.Unmarshal(bz, text); err != nil {
return err
}

hexAddr, err := NewHexAddrFromStr(*text)
if err != nil {
return err
}

*h = hexAddr

return nil
}

// Size implements the gogo proto custom type interface.
// Ref: https://github.com/cosmos/gogoproto/blob/v1.5.0/custom_types.md
func (h HexAddr) Size() int {
return len(h)
}
Loading
Loading