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

WIP: feat(evm+precompiles): integrate the new readonly-precompiles and add new precompiles Price Feed #372

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app

import (
"fmt"
"github.com/dymensionxyz/rollapp-evm/x/cpc"
"io"
"net/http"
"os"
Expand Down Expand Up @@ -654,6 +655,10 @@ func NewRollapp(
ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack)
app.IBCKeeper.SetRouter(ibcRouter)

/**** Register custom precompiled contract using keepers ****/
const enablePrecompilesAtVersion = 0 // future precompiles should be enabled at a higher version and x/evm module params should be updated, the goal is to avoid breaking state when replay blocks
evmkeeper.RegisterCustomPrecompiledContract(cpc.NewPreFeedCustomPrecompiledContract(app.EvmKeeper), enablePrecompilesAtVersion)

/**** Module Options ****/

// NOTE: Any module instantiated in the module manager that is later modified
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,9 @@ replace (
github.com/CosmWasm/wasmd => github.com/decentrio/wasmd v0.33.0-sdk46.2
github.com/centrifuge/go-substrate-rpc-client/v4 => github.com/availproject/go-substrate-rpc-client/v4 v4.0.12-avail-1.4.0-rc1-5e286e3
github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0
// use Evmos geth fork
github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26
github.com/evmos/evmos/v12 => github.com/dymensionxyz/evmos/v12 v12.1.6-dymension-v0.4.3
// go-ethereum fork with custom-precompiled-contract support
github.com/ethereum/go-ethereum => github.com/EscanBE/go-ethereum-for-evermint v1.10.28
github.com/evmos/evmos/v12 => github.com/dymensionxyz/evmos/v12 v12.1.7-0.20241202184644-487bdfd58ff9
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
github.com/gorilla/rpc => github.com/dymensionxyz/rpc v1.3.1
github.com/osmosis-labs/osmosis/v15 => github.com/dymensionxyz/osmosis/v15 v15.2.0-dymension-v1.1.2
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/EscanBE/go-ethereum-for-evermint v1.10.28 h1:+Iid0JWaULgPbGAB8lGEn1xqsr+GjeGlFzzTMcjgIIE=
github.com/EscanBE/go-ethereum-for-evermint v1.10.28/go.mod h1:/6CsT5Ceen2WPLI/oCA3xMcZ5sWMF/D46SjM/ayY0Oo=
github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU=
github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
Expand Down Expand Up @@ -581,8 +583,8 @@ github.com/dymensionxyz/dymension-rdk v1.6.1-0.20241119103059-def6322e4345 h1:Fc
github.com/dymensionxyz/dymension-rdk v1.6.1-0.20241119103059-def6322e4345/go.mod h1:y89w1OG4C4aF7yyW8bv9PwV3o1KkCx1hyt34ap04Rnk=
github.com/dymensionxyz/dymint v1.2.0-rc01.0.20241119162335-82f60adeb0cc h1:jltDUyV834R55beeN4K+p5u/y91agApAuLRd/0s4dkk=
github.com/dymensionxyz/dymint v1.2.0-rc01.0.20241119162335-82f60adeb0cc/go.mod h1:ui7okdD4GRAySphS7XNOlbJKXIA2TAH/Wu6S8KV3o0M=
github.com/dymensionxyz/evmos/v12 v12.1.6-dymension-v0.4.3 h1:jSnj5psbuEq5s5MhbwI26I5gKtlb9mi95pv8r0e9ybI=
github.com/dymensionxyz/evmos/v12 v12.1.6-dymension-v0.4.3/go.mod h1:CI6D89pkoiIm4BjoMFNnEaCLdKBEobLuwvhS0c1zh7Y=
github.com/dymensionxyz/evmos/v12 v12.1.7-0.20241202184644-487bdfd58ff9 h1:DgEOLAI18QDrQMSH0DvS+bwwctGZ+roGppDTcVxin8c=
github.com/dymensionxyz/evmos/v12 v12.1.7-0.20241202184644-487bdfd58ff9/go.mod h1:mkPJyB6L8Vciw0nrYMbz8XeNwNPX1Pl9mdS+JJB7Fg8=
github.com/dymensionxyz/gerr-cosmos v1.0.0 h1:oi91rgOkpJWr41oX9JOyjvvBnhGY54tj513x8VlDAEc=
github.com/dymensionxyz/gerr-cosmos v1.0.0/go.mod h1:n+0olxPogzWqFKba45mCpvrHLGmeS8W9UZjggHnWk6c=
github.com/dymensionxyz/rpc v1.3.1 h1:7EXWIobaBes5zldRvTIg7TmNsEKjicrWA/OjCc0NaGs=
Expand All @@ -609,8 +611,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evmos/go-ethereum v1.10.26 h1:7wlczxUWTwhzJJUyh3Kkqt3/5fdSJzh8c42boc9GuII=
github.com/evmos/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
Expand Down
24 changes: 24 additions & 0 deletions x/cpc/abi/precompiled_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package abi

import (
_ "embed"
"encoding/json"
"github.com/evmos/evmos/v12/x/evm/abi"
)

var (
//go:embed price_feed.json
priceFeedJson []byte

PriceFeedCpcInfo abi.CustomPrecompiledContractInfo
)

func init() {
var err error

err = json.Unmarshal(priceFeedJson, &PriceFeedCpcInfo)
if err != nil {
panic(err)
}
PriceFeedCpcInfo.Name = "Price Feed"
}
48 changes: 48 additions & 0 deletions x/cpc/abi/precompiled_info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package abi

import (
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
"math"
"math/big"
"testing"
)

//goland:noinspection GoUnusedGlobalVariable
var (
bigIntMaxInt64 = new(big.Int).SetUint64(math.MaxInt64)
bigIntMaxInt64Bz = common.BytesToHash(bigIntMaxInt64.Bytes()).Bytes()
bigIntMaxUint64 = new(big.Int).SetUint64(math.MaxUint64)
bigIntMaxUint64Bz = common.BytesToHash(bigIntMaxUint64.Bytes()).Bytes()
bigIntOneBz = common.BytesToHash(big.NewInt(1).Bytes()).Bytes()
text = "hello"
textAbiEncodedBz = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
maxUint8Value = uint8(math.MaxUint8)
maxUint8ValueBz = common.BytesToHash([]byte{math.MaxUint8}).Bytes()
_32Bytes = [32]byte{0x1, 0x2, 0x3, 0x32, 0xFF}
)

func Test_PriceFeed(t *testing.T) {
cpcInfo := PriceFeedCpcInfo

t.Run("getPrice(string)", func(t *testing.T) {
bz, err := cpcInfo.ABI.Methods["getPrice"].Inputs.Pack(text)
require.NoError(t, err)

ret, err := cpcInfo.UnpackMethodInput(
"getPrice",
append([]byte{0x52, 0x4f, 0x38, 0x89}, bz...),
)
require.NoError(t, err)
require.Len(t, ret, 1)
require.Equal(t, text, ret[0].(string))

bz, err = cpcInfo.PackMethodOutput("getPrice", bigIntMaxUint64, true)
require.NoError(t, err)
ops, err := cpcInfo.ABI.Methods["getPrice"].Outputs.Unpack(bz)
require.NoError(t, err)
require.Len(t, ops, 2)
require.Equal(t, bigIntMaxUint64, ops[0].(*big.Int))
require.Equal(t, true, ops[1].(bool))
})
}
26 changes: 26 additions & 0 deletions x/cpc/abi/price_feed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"inputs": [
{
"internalType": "string",
"name": "tokenName",
"type": "string"
}
],
"name": "getPrice",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
]
11 changes: 11 additions & 0 deletions x/cpc/abi/price_feed.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

interface IDymPriceFeedCPC {
/**
* @dev Returns price for the given token by name.
* The second return value indicating whether the operation succeeded.
*/
function getPrice(string memory tokenName) external view returns (uint256, bool);
}
39 changes: 39 additions & 0 deletions x/cpc/precompiles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package cpc

import (
"fmt"
"github.com/ethereum/go-ethereum/common"
)

const (
cpcAddrNoncePriceFeed byte = iota + 1
)

// CpcPriceFeedFixedAddress is the address of the price-feed custom precompiled contract.
var CpcPriceFeedFixedAddress common.Address

func init() {
generatedCpcAddresses := make(map[common.Address]struct{})

// generateCpcAddress generates a custom precompiled contract address based on the contract address nonce.
generateCpcAddress := func(contractAddrNonce byte) common.Address {
if contractAddrNonce == 0 {
panic("contract address nonce cannot be zero")
}
bz := make([]byte, 20)
bz[0] = 0xCC
bz[1] = contractAddrNonce
bz[2] = 0x01 // avoid collision with custom precompiled contracts
bz[19] = contractAddrNonce

addr := common.BytesToAddress(bz)
if _, ok := generatedCpcAddresses[addr]; ok {
panic(fmt.Sprintf("generated address %s already exists", addr.Hex()))
}
generatedCpcAddresses[addr] = struct{}{}

return addr
}

CpcPriceFeedFixedAddress = generateCpcAddress(cpcAddrNoncePriceFeed)
}
82 changes: 82 additions & 0 deletions x/cpc/precompiles_pricefeed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cpc

import (
"github.com/dymensionxyz/rollapp-evm/x/cpc/abi"
"github.com/ethereum/go-ethereum/common"
corevm "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
evmkeeper "github.com/evmos/evmos/v12/x/evm/keeper"
"math/big"
)

// contract

var _ evmkeeper.CustomPrecompiledContractI = &priceFeedCustomPrecompiledContract{}

// priceFeedCustomPrecompiledContract
type priceFeedCustomPrecompiledContract struct {
keeper *evmkeeper.Keeper // FIXME: replace with something that can be used to get the price
executors []evmkeeper.ExtendedCustomPrecompiledContractMethodExecutorI
}

// NewPreFeedCustomPrecompiledContract creates a new price-feed custom precompiled contract.
func NewPreFeedCustomPrecompiledContract(keeper *evmkeeper.Keeper) evmkeeper.CustomPrecompiledContractI {
contract := &priceFeedCustomPrecompiledContract{
keeper: keeper,
}

contract.executors = []evmkeeper.ExtendedCustomPrecompiledContractMethodExecutorI{
&priceFeedCustomPrecompiledContractRoGetPrice{contract},
}

return contract
}

func (m priceFeedCustomPrecompiledContract) GetName() string {
return abi.PriceFeedCpcInfo.Name
}

func (m priceFeedCustomPrecompiledContract) GetAddress() common.Address {
return CpcPriceFeedFixedAddress
}

func (m priceFeedCustomPrecompiledContract) GetMethodExecutors() []evmkeeper.ExtendedCustomPrecompiledContractMethodExecutorI {
return m.executors
}

// getPrice(string)

var _ evmkeeper.ExtendedCustomPrecompiledContractMethodExecutorI = &priceFeedCustomPrecompiledContractRoGetPrice{}

type priceFeedCustomPrecompiledContractRoGetPrice struct {
contract *priceFeedCustomPrecompiledContract
}

func (e priceFeedCustomPrecompiledContractRoGetPrice) Execute(_ corevm.ContractRef, _ common.Address, input []byte, _ evmkeeper.CpcExecutorEnv) ([]byte, error) {
ips, err := abi.PriceFeedCpcInfo.UnpackMethodInput("getPrice", input)
if err != nil {
return nil, err
}

denom := ips[0].(string)
var price *big.Int

{ // FIXME: implement it correctly
// generate random price
price = new(big.Int).SetBytes(crypto.Keccak256([]byte(denom)))
}

return abi.PriceFeedCpcInfo.PackMethodOutput("getPrice", price, true)
}

func (e priceFeedCustomPrecompiledContractRoGetPrice) Method4BytesSignatures() []byte {
return []byte{0x52, 0x4f, 0x38, 0x89}
}

func (e priceFeedCustomPrecompiledContractRoGetPrice) RequireGas() uint64 {
return 500_000
}

func (e priceFeedCustomPrecompiledContractRoGetPrice) ReadOnly() bool {
return true
}
Loading