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

refactor(evm): embeds #1984

Merged
merged 17 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 14 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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1976](https://github.com/NibiruChain/nibiru/pull/1976) - refactor(evm): unique chain ids for all networks
- [#1977](https://github.com/NibiruChain/nibiru/pull/1977) - fix(localnet): rolled back change of evm validator address with cosmos derivation path
- [#1981](https://github.com/NibiruChain/nibiru/pull/1981) - fix(evm): remove isCheckTx() short circuit on `AnteDecVerifyEthAcc`
- [#1979](https://github.com/NibiruChain/nibiru/pull/1979) -refactor(db): use pebbledb as the default db in integration tests
- [#1979](https://github.com/NibiruChain/nibiru/pull/1979) - refactor(db): use pebbledb as the default db in integration tests
- [#1984](https://github.com/NibiruChain/nibiru/pull/1984) - refactor(evm): embeds

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

Expand Down
4 changes: 1 addition & 3 deletions eth/rpc/rpcapi/eth_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ func (s *TestSuite) SetupSuite() {

s.network = network
s.ethClient = network.Validators[0].JSONRPCClient

s.contractData, err = embeds.SmartContract_TestERC20.Load()
s.Require().NoError(err)
s.contractData = embeds.SmartContract_TestERC20
Copy link
Contributor

Choose a reason for hiding this comment

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

Reintroduce error handling for contract data initialization.

Removing the error handling for contractData initialization can lead to potential issues if the contract data fails to load correctly. Ensure that the contract data is valid before it is used in subsequent tests.

-  s.contractData = embeds.SmartContract_TestERC20
+  var err error
+  s.contractData, err = embeds.SmartContract_TestERC20.Load()
+  s.Require().NoError(err)
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
s.contractData = embeds.SmartContract_TestERC20
var err error
s.contractData, err = embeds.SmartContract_TestERC20.Load()
s.Require().NoError(err)


testAccPrivateKey, _ := crypto.GenerateKey()
s.fundedAccPrivateKey = testAccPrivateKey
Expand Down
6 changes: 3 additions & 3 deletions x/evm/embeds/.gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
node_modules
.env

contracts
ignition
bun.lockb
package-lock.json

# Hardhat files
/cache
/artifacts
artifacts/@openzeppelin
artifacts/build-info
*.dbg.json

# TypeChain files
/typechain
Expand Down
13 changes: 3 additions & 10 deletions x/evm/embeds/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
# Sample Hardhat Project

This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a Hardhat Ignition module that deploys that contract.

Try running some of the following tasks:
# Nibiru Contract Embeds

```shell
npx hardhat help
npx hardhat test
REPORT_GAS=true npx hardhat test
npx hardhat node
npx hardhat ignition deploy ./ignition/modules/Lock.js
npm install
npx hardhat compile
```

Large diffs are not rendered by default.

286 changes: 286 additions & 0 deletions x/evm/embeds/artifacts/contracts/TestERC20.sol/TestERC20.json

Large diffs are not rendered by default.

42 changes: 0 additions & 42 deletions x/evm/embeds/compile.sh

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;
pragma solidity >=0.8.19;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
Expand Down
File renamed without changes.
16 changes: 16 additions & 0 deletions x/evm/embeds/contracts/TestERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// contracts/TestERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract TestERC20 is ERC20 {

// Define the supply of TestERC20: 1,000,000
uint256 constant initialSupply = 1000000 * (10**18);

// Constructor will be called on contract creation
constructor() ERC20("TestERC20", "FOO") {
_mint(msg.sender, initialSupply);
}
}
197 changes: 42 additions & 155 deletions x/evm/embeds/embeds.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,192 +9,79 @@
// program (smart contracts).
_ "embed"
"encoding/json"
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"strings"

gethabi "github.com/ethereum/go-ethereum/accounts/abi"
gethcommon "github.com/ethereum/go-ethereum/common"
)

var (
// Contract_ERC20Minter: The default ERC20 contract deployed during the
// creation of a `FunToken` mapping from a bank coin.
Contract_ERC20Minter CompiledEvmContract

//go:embed ERC20MinterCompiled.json
//go:embed artifacts/contracts/ERC20Minter.sol/ERC20Minter.json
erc20MinterContractJSON []byte

// Contract_Funtoken: Precompile contract interface for
// "IFunToken.sol". This precompile enables transfers of ERC20 tokens
// to non-EVM accounts. Only the ABI is used.
Contract_Funtoken CompiledEvmContract
//go:embed IFunTokenCompiled.json
//go:embed artifacts/contracts/IFunToken.sol/IFunToken.json
funtokenContractJSON []byte
//go:embed artifacts/contracts/TestERC20.sol/TestERC20.json
testErc20Json []byte
)

func init() {
Contract_ERC20Minter = SmartContract_ERC20Minter.MustLoad()
Contract_Funtoken = SmartContract_FunToken.MustLoad()
}

var (
SmartContract_TestERC20 = SmartContractFixture{
Name: "TestERC20.sol",
FixtureType: FixtueType_Test,
// Contract_ERC20Minter: The default ERC20 contract deployed during the
// creation of a `FunToken` mapping from a bank coin.
SmartContract_ERC20Minter = CompiledEvmContract{
Name: "ERC20Minter.sol",
EmbedJSON: erc20MinterContractJSON,
}

SmartContract_ERC20Minter = SmartContractFixture{
Name: "ERC20Minter.sol",
FixtureType: FixtueType_Prod,
EmbedJSON: &erc20MinterContractJSON,
}
SmartContract_FunToken = SmartContractFixture{
Name: "FunToken.sol",
FixtureType: FixtueType_Prod,
EmbedJSON: &funtokenContractJSON,
// SmartContract_Funtoken: Precompile contract interface for
// "IFunToken.sol". This precompile enables transfers of ERC20 tokens
// to non-EVM accounts. Only the ABI is used.
SmartContract_FunToken = CompiledEvmContract{
Name: "FunToken.sol",
EmbedJSON: funtokenContractJSON,
}
)

// CompiledEvmContract: EVM contract that can be deployed into the EVM state and
// used as a valid precompile.
type CompiledEvmContract struct {
ABI gethabi.ABI `json:"abi"`
Bytecode []byte `json:"bytecode"`
}

type SmartContractFixture struct {
Name string
FixtureType ContractFixtureType
EmbedJSON *[]byte
}

// ContractFixtureType: Enum type for embedded smart contracts. This type
// expresses whether a contract is used in production or only for testing.
type ContractFixtureType string

const (
FixtueType_Prod = "prod"
FixtueType_Test = "test"
SmartContract_TestERC20 = CompiledEvmContract{
Name: "TestERC20.sol",
EmbedJSON: testErc20Json,
}
)

// HardhatOutput: Expected format for smart contract test fixtures.
type HardhatOutput struct {
ABI json.RawMessage `json:"abi"`
Bytecode HexString `json:"bytecode"`
func init() {
SmartContract_ERC20Minter.MustLoad()
SmartContract_FunToken.MustLoad()
SmartContract_TestERC20.MustLoad()
}

// HexString: Hexadecimal-encoded string
type HexString string
type CompiledEvmContract struct {
Name string
EmbedJSON []byte

func (h HexString) Bytes() []byte {
return gethcommon.Hex2Bytes(
strings.TrimPrefix(string(h), "0x"),
)
}
func (h HexString) String() string { return string(h) }
func (h HexString) FromBytes(bz []byte) HexString {
return HexString(gethcommon.Bytes2Hex(bz))
// filled in post-load
ABI *gethabi.ABI `json:"abi"`
Bytecode []byte `json:"bytecode"`
}

func NewHardhatOutputFromJson(
jsonBz []byte,
) (out HardhatOutput, err error) {
rawJsonBz := make(map[string]json.RawMessage)
err = json.Unmarshal(jsonBz, &rawJsonBz)
if err != nil {
return
func (sc *CompiledEvmContract) MustLoad() {
if sc.EmbedJSON == nil {
panic("missing compiled contract embed")

Check warning on line 65 in x/evm/embeds/embeds.go

View check run for this annotation

Codecov / codecov/patch

x/evm/embeds/embeds.go#L65

Added line #L65 was not covered by tests
}
var rawBytecodeBz HexString
err = json.Unmarshal(rawJsonBz["bytecode"], &rawBytecodeBz)
if err != nil {
return
}

return HardhatOutput{
ABI: rawJsonBz["abi"],
Bytecode: rawBytecodeBz,
}, err
}

func (jsonObj HardhatOutput) EvmContract() (out CompiledEvmContract, err error) {
newAbi := new(gethabi.ABI)
err = newAbi.UnmarshalJSON(jsonObj.ABI)
rawJsonBz := make(map[string]json.RawMessage)
err := json.Unmarshal(sc.EmbedJSON, &rawJsonBz)
if err != nil {
return
panic(err)

Check warning on line 71 in x/evm/embeds/embeds.go

View check run for this annotation

Codecov / codecov/patch

x/evm/embeds/embeds.go#L71

Added line #L71 was not covered by tests
}

return CompiledEvmContract{
ABI: *newAbi,
Bytecode: jsonObj.Bytecode.Bytes(),
}, err
}

func (sc SmartContractFixture) MustLoad() (out CompiledEvmContract) {
out, err := sc.Load()
abi := new(gethabi.ABI)
err = abi.UnmarshalJSON(rawJsonBz["abi"])
if err != nil {
panic(err)
}
return out
}

func (sc SmartContractFixture) Load() (out CompiledEvmContract, err error) {
var jsonBz []byte

// Locate the contracts directory.
switch sc.FixtureType {
case FixtueType_Prod:
if sc.EmbedJSON == nil {
return out, fmt.Errorf("missing compiled contract embed")
}
jsonBz = *sc.EmbedJSON
case FixtueType_Test:
contractsDirPath, err := pathToE2EContracts()
if err != nil {
return out, err
}
baseName := strings.TrimSuffix(sc.Name, ".sol")
compiledPath := fmt.Sprintf("%s/%sCompiled.json", contractsDirPath, baseName)

jsonBz, err = os.ReadFile(compiledPath)
if err != nil {
return out, err
}
default:
panic(fmt.Errorf("unexpected case type \"%s\"", sc.FixtureType))
}

compiledJson, err := NewHardhatOutputFromJson(jsonBz)
var bytecodeStr string
err = json.Unmarshal(rawJsonBz["bytecode"], &bytecodeStr)
if err != nil {
return
}

return compiledJson.EvmContract()
}

// pathToE2EContracts: Returns the absolute path to the E2E test contract
// directory located at path, "NibiruChain/nibiru/e2e/evm/contracts".
func pathToE2EContracts() (thePath string, err error) {
dirEvmTest, _ := GetPackageDir()
dirOfRepo := path.Dir(path.Dir(path.Dir(dirEvmTest)))
dirEvmE2e := path.Join(dirOfRepo, "e2e/evm")
if path.Base(dirEvmE2e) != "evm" {
return thePath, fmt.Errorf("failed to locate the e2e/evm directory")
panic(err)

Check warning on line 83 in x/evm/embeds/embeds.go

View check run for this annotation

Codecov / codecov/patch

x/evm/embeds/embeds.go#L83

Added line #L83 was not covered by tests
}
return dirEvmE2e + "/contracts", nil
}

// GetPackageDir: Returns the absolute path of the Golang package that
// calls this function.
func GetPackageDir() (string, error) {
// Get the import path of the current package
_, filename, _, _ := runtime.Caller(0)
pkgDir := path.Dir(filename)
pkgPath := path.Join(path.Base(pkgDir), "..")

// Get the directory path of the package
return filepath.Abs(pkgPath)
sc.Bytecode = gethcommon.FromHex(bytecodeStr)
sc.ABI = abi
}
18 changes: 7 additions & 11 deletions x/evm/embeds/embeds_test.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
package embeds_test

import (
_ "embed"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/NibiruChain/nibiru/x/evm/embeds"
)

func TestLoadContracts(t *testing.T) {
for _, tc := range []embeds.SmartContractFixture{
embeds.SmartContract_TestERC20,
embeds.SmartContract_ERC20Minter,
embeds.SmartContract_FunToken,
} {
t.Run(tc.Name, func(t *testing.T) {
_, err := tc.Load()
assert.NoError(t, err)
})
}
require.NotPanics(t, func() {
embeds.SmartContract_ERC20Minter.MustLoad()
embeds.SmartContract_FunToken.MustLoad()
embeds.SmartContract_TestERC20.MustLoad()
})
}
2 changes: 1 addition & 1 deletion x/evm/embeds/hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.19",
solidity: "0.8.24",
};
Loading
Loading