Skip to content

Commit

Permalink
Add ERC pointer registry (#1490)
Browse files Browse the repository at this point in the history
  • Loading branch information
codchen authored and udpatil committed Apr 17, 2024
1 parent 5a68e8b commit 1acafae
Show file tree
Hide file tree
Showing 20 changed files with 738 additions and 30 deletions.
6 changes: 6 additions & 0 deletions precompiles/common/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ type EVMKeeper interface {
IsCodeHashWhitelistedForBankSend(ctx sdk.Context, h common.Hash) bool
GetPriorityNormalizer(ctx sdk.Context) sdk.Dec
GetBaseDenom(ctx sdk.Context) string
SetERC20NativePointer(ctx sdk.Context, token string, addr common.Address) error
GetERC20NativePointer(ctx sdk.Context, token string) (addr common.Address, version uint16, exists bool)
SetERC20CW20Pointer(ctx sdk.Context, cw20Address string, addr common.Address) error
GetERC20CW20Pointer(ctx sdk.Context, cw20Address string) (addr common.Address, version uint16, exists bool)
SetERC721CW721Pointer(ctx sdk.Context, cw721Address string, addr common.Address) error
GetERC721CW721Pointer(ctx sdk.Context, cw721Address string) (addr common.Address, version uint16, exists bool)
}

type OracleKeeper interface {
Expand Down
6 changes: 3 additions & 3 deletions precompiles/ibc/ibc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"github.com/cosmos/cosmos-sdk/types/bech32"

"github.com/sei-protocol/sei-chain/precompiles/wasmd"
"github.com/sei-protocol/sei-chain/utils"

sdk "github.com/cosmos/cosmos-sdk/types"
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
Expand Down Expand Up @@ -100,8 +100,8 @@ func (p Precompile) RunAndCalculateGas(evm *vm.EVM, caller common.Address, calli

gasMultiplier := p.evmKeeper.GetPriorityNormalizer(ctx)
gasLimitBigInt := new(big.Int).Mul(new(big.Int).SetUint64(suppliedGas), gasMultiplier.RoundInt().BigInt())
if gasLimitBigInt.Cmp(wasmd.MaxUint64BigInt) > 0 {
gasLimitBigInt = wasmd.MaxUint64BigInt
if gasLimitBigInt.Cmp(utils.BigMaxU64) > 0 {
gasLimitBigInt = utils.BigMaxU64
}
ctx = ctx.WithGasMeter(sdk.NewGasMeter(gasLimitBigInt.Uint64()))

Expand Down
20 changes: 20 additions & 0 deletions precompiles/pointer/Pointer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

address constant POINTER_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000100b;

IPointer constant POINTER_CONTRACT = IPointer(POINTER_PRECOMPILE_ADDRESS);

interface IPointer {
function addNativePointer(
string memory token
) payable external returns (address ret);

function addCW20Pointer(
string memory cwAddr
) payable external returns (address ret);

function addCW721Pointer(
string memory cwAddr
) payable external returns (address ret);
}
1 change: 1 addition & 0 deletions precompiles/pointer/abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"internalType":"string","name":"cwAddr","type":"string"}],"name":"addCW20Pointer","outputs":[{"internalType":"address","name":"ret","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"cwAddr","type":"string"}],"name":"addCW721Pointer","outputs":[{"internalType":"address","name":"ret","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"token","type":"string"}],"name":"addNativePointer","outputs":[{"internalType":"address","name":"ret","type":"address"}],"stateMutability":"payable","type":"function"}]
243 changes: 243 additions & 0 deletions precompiles/pointer/pointer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package pointer

import (
"bytes"
"embed"
"encoding/json"
"fmt"
"math"
"math/big"

sdk "github.com/cosmos/cosmos-sdk/types"
ethabi "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
pcommon "github.com/sei-protocol/sei-chain/precompiles/common"
"github.com/sei-protocol/sei-chain/utils"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/cw20"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/cw721"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/native"
)

const (
AddNativePointer = "addNativePointer"
AddCW20Pointer = "addCW20Pointer"
AddCW721Pointer = "addCW721Pointer"
)

const PointerAddress = "0x000000000000000000000000000000000000100b"

var _ vm.PrecompiledContract = &Precompile{}

// Embed abi json file to the executable binary. Needed when importing as dependency.
//
//go:embed abi.json
var f embed.FS

type Precompile struct {
pcommon.Precompile
evmKeeper pcommon.EVMKeeper
bankKeeper pcommon.BankKeeper
wasmdKeeper pcommon.WasmdViewKeeper
address common.Address

AddNativePointerID []byte
AddCW20PointerID []byte
AddCW721PointerID []byte
}

func NewPrecompile(evmKeeper pcommon.EVMKeeper, bankKeeper pcommon.BankKeeper, wasmdKeeper pcommon.WasmdViewKeeper) (*Precompile, error) {
abiBz, err := f.ReadFile("abi.json")
if err != nil {
return nil, fmt.Errorf("error loading the pointer ABI %s", err)
}

newAbi, err := ethabi.JSON(bytes.NewReader(abiBz))
if err != nil {
return nil, err
}

p := &Precompile{
Precompile: pcommon.Precompile{ABI: newAbi},
evmKeeper: evmKeeper,
bankKeeper: bankKeeper,
wasmdKeeper: wasmdKeeper,
address: common.HexToAddress(PointerAddress),
}

for name, m := range newAbi.Methods {
switch name {
case AddNativePointer:
p.AddNativePointerID = m.ID
case AddCW20Pointer:
p.AddCW20PointerID = m.ID
case AddCW721Pointer:
p.AddCW721PointerID = m.ID
}
}

return p, nil
}

// RequiredGas returns the required bare minimum gas to execute the precompile.
func (p Precompile) RequiredGas(input []byte) uint64 {
// gas is calculated dynamically
return 0
}

func (p Precompile) Address() common.Address {
return p.address
}

func (p Precompile) RunAndCalculateGas(evm *vm.EVM, caller common.Address, _ common.Address, input []byte, suppliedGas uint64, value *big.Int) (ret []byte, remainingGas uint64, err error) {
ctx, method, args, err := p.Prepare(evm, input)
if err != nil {
return nil, 0, err
}

switch method.Name {
case AddNativePointer:
return p.AddNative(ctx, method, caller, args, value, evm, suppliedGas)
case AddCW20Pointer:
return p.AddCW20(ctx, method, caller, args, value, evm, suppliedGas)
case AddCW721Pointer:
return p.AddCW721(ctx, method, caller, args, value, evm, suppliedGas)
default:
err = fmt.Errorf("unknown method %s", method.Name)
}
return
}

func (p Precompile) Run(*vm.EVM, common.Address, []byte, *big.Int) ([]byte, error) {
panic("static gas Run is not implemented for dynamic gas precompile")
}

func (p Precompile) AddNative(ctx sdk.Context, method *ethabi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
pcommon.AssertArgsLength(args, 1)
token := args[0].(string)
existingAddr, existingVersion, exists := p.evmKeeper.GetERC20NativePointer(ctx, token)
if exists && existingVersion >= native.CurrentVersion {
return nil, 0, fmt.Errorf("pointer at %s with version %d exists when trying to set pointer for version %d", existingAddr.Hex(), existingVersion, native.CurrentVersion)
}
metadata, metadataExists := p.bankKeeper.GetDenomMetaData(ctx, token)
if !metadataExists {
return nil, 0, fmt.Errorf("denom %s does not have metadata stored and thus can only have its pointer set through gov proposal", token)
}
name := metadata.Name
symbol := metadata.Symbol
var decimals uint8
for _, denomUnit := range metadata.DenomUnits {
if denomUnit.Exponent > uint32(decimals) && denomUnit.Exponent <= math.MaxUint8 {
decimals = uint8(denomUnit.Exponent)
name = denomUnit.Denom
symbol = denomUnit.Denom
if len(denomUnit.Aliases) > 0 {
name = denomUnit.Aliases[0]
}
}
}
constructorArguments := []interface{}{
token, name, symbol, decimals,
}

packedArgs, err := native.GetParsedABI().Pack("", constructorArguments...)
if err != nil {
panic(err)
}
bin := append(native.GetBin(), packedArgs...)
if value == nil {
value = utils.Big0
}
ret, contractAddr, remainingGas, err := evm.Create(vm.AccountRef(caller), bin, suppliedGas, value)
if err != nil {
return
}
err = p.evmKeeper.SetERC20NativePointer(ctx, token, contractAddr)
if err != nil {
return
}
ret, err = method.Outputs.Pack(contractAddr)
return
}

func (p Precompile) AddCW20(ctx sdk.Context, method *ethabi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
pcommon.AssertArgsLength(args, 1)
cwAddr := args[0].(string)
existingAddr, existingVersion, exists := p.evmKeeper.GetERC20CW20Pointer(ctx, cwAddr)
if exists && existingVersion >= cw20.CurrentVersion {
return nil, 0, fmt.Errorf("pointer at %s with version %d exists when trying to set pointer for version %d", existingAddr.Hex(), existingVersion, cw20.CurrentVersion)
}
res, err := p.wasmdKeeper.QuerySmart(ctx, sdk.MustAccAddressFromBech32(cwAddr), []byte("{\"token_info\":{}}"))
if err != nil {
return nil, 0, err
}
formattedRes := map[string]interface{}{}
if err := json.Unmarshal(res, &formattedRes); err != nil {
return nil, 0, err
}
name := formattedRes["name"].(string)
symbol := formattedRes["symbol"].(string)
constructorArguments := []interface{}{
cwAddr, name, symbol,
}

packedArgs, err := cw20.GetParsedABI().Pack("", constructorArguments...)
if err != nil {
panic(err)
}
bin := append(cw20.GetBin(), packedArgs...)
if value == nil {
value = utils.Big0
}
ret, contractAddr, remainingGas, err := evm.Create(vm.AccountRef(caller), bin, suppliedGas, value)
if err != nil {
return
}
err = p.evmKeeper.SetERC20CW20Pointer(ctx, cwAddr, contractAddr)
if err != nil {
return
}
ret, err = method.Outputs.Pack(contractAddr)
return
}

func (p Precompile) AddCW721(ctx sdk.Context, method *ethabi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
pcommon.AssertArgsLength(args, 1)
cwAddr := args[0].(string)
existingAddr, existingVersion, exists := p.evmKeeper.GetERC721CW721Pointer(ctx, cwAddr)
if exists && existingVersion >= cw721.CurrentVersion {
return nil, 0, fmt.Errorf("pointer at %s with version %d exists when trying to set pointer for version %d", existingAddr.Hex(), existingVersion, cw721.CurrentVersion)
}
res, err := p.wasmdKeeper.QuerySmart(ctx, sdk.MustAccAddressFromBech32(cwAddr), []byte("{\"contract_info\":{}}"))
if err != nil {
return nil, 0, err
}
formattedRes := map[string]interface{}{}
if err := json.Unmarshal(res, &formattedRes); err != nil {
return nil, 0, err
}
name := formattedRes["name"].(string)
symbol := formattedRes["symbol"].(string)
constructorArguments := []interface{}{
cwAddr, name, symbol,
}

packedArgs, err := cw721.GetParsedABI().Pack("", constructorArguments...)
if err != nil {
panic(err)
}
bin := append(cw721.GetBin(), packedArgs...)
if value == nil {
value = utils.Big0
}
ret, contractAddr, remainingGas, err := evm.Create(vm.AccountRef(caller), bin, suppliedGas, value)
if err != nil {
return
}
err = p.evmKeeper.SetERC721CW721Pointer(ctx, cwAddr, contractAddr)
if err != nil {
return
}
ret, err = method.Outputs.Pack(contractAddr)
return
}
73 changes: 73 additions & 0 deletions precompiles/pointer/pointer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package pointer_test

import (
"testing"
"time"

banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/sei-protocol/sei-chain/app"
"github.com/sei-protocol/sei-chain/precompiles/pointer"
testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/native"
"github.com/sei-protocol/sei-chain/x/evm/state"
"github.com/sei-protocol/sei-chain/x/evm/types"
"github.com/stretchr/testify/require"
)

func TestAddNative(t *testing.T) {
testApp := app.Setup(false, false)
p, err := pointer.NewPrecompile(&testApp.EvmKeeper, testApp.BankKeeper, testApp.WasmKeeper)
require.Nil(t, err)
ctx := testApp.GetContextForDeliverTx([]byte{}).WithBlockTime(time.Now())
_, caller := testkeeper.MockAddressPair()
suppliedGas := uint64(10000000)
cfg := types.DefaultChainConfig().EthereumConfig(testApp.EvmKeeper.ChainID(ctx))

// token has no metadata
m, err := p.ABI.MethodById(p.AddNativePointerID)
require.Nil(t, err)
args, err := m.Inputs.Pack("test")
require.Nil(t, err)
statedb := state.NewDBImpl(ctx, &testApp.EvmKeeper, true)
blockCtx, _ := testApp.EvmKeeper.GetVMBlockContext(ctx, core.GasPool(suppliedGas))
evm := vm.NewEVM(*blockCtx, vm.TxContext{}, statedb, cfg, vm.Config{})
_, g, err := p.RunAndCalculateGas(evm, caller, caller, append(p.AddNativePointerID, args...), suppliedGas, nil)
require.NotNil(t, err)
require.Equal(t, uint64(0), g)
_, _, exists := testApp.EvmKeeper.GetERC20NativePointer(statedb.Ctx(), "test")
require.False(t, exists)

// token has metadata
testApp.BankKeeper.SetDenomMetaData(ctx, banktypes.Metadata{
Base: "test",
Name: "base_name",
Symbol: "base_symbol",
DenomUnits: []*banktypes.DenomUnit{{
Exponent: 6,
Denom: "denom",
Aliases: []string{"DENOM"},
}},
})
statedb = state.NewDBImpl(ctx, &testApp.EvmKeeper, true)
evm = vm.NewEVM(*blockCtx, vm.TxContext{}, statedb, cfg, vm.Config{})
ret, g, err := p.RunAndCalculateGas(evm, caller, caller, append(p.AddNativePointerID, args...), suppliedGas, nil)
require.Nil(t, err)
require.Equal(t, uint64(8907806), g)
outputs, err := m.Outputs.Unpack(ret)
require.Nil(t, err)
addr := outputs[0].(common.Address)
pointerAddr, version, exists := testApp.EvmKeeper.GetERC20NativePointer(statedb.Ctx(), "test")
require.Equal(t, addr, pointerAddr)
require.Equal(t, native.CurrentVersion, version)
require.True(t, exists)

// pointer already exists
statedb = state.NewDBImpl(statedb.Ctx(), &testApp.EvmKeeper, true)
evm = vm.NewEVM(*blockCtx, vm.TxContext{}, statedb, cfg, vm.Config{})
_, g, err = p.RunAndCalculateGas(evm, caller, caller, append(p.AddNativePointerID, args...), suppliedGas, nil)
require.NotNil(t, err)
require.Equal(t, uint64(0), g)
}
20 changes: 20 additions & 0 deletions precompiles/pointerview/Pointerview.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

address constant POINTERVIEW_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000100A;

IPointerview constant POINTERVIEW_CONTRACT = IPointerview(POINTERVIEW_PRECOMPILE_ADDRESS);

interface IPointerview {
function getNativePointer(
string memory token
) view external returns (address addr, uint16 version, bool exists);

function getCW20Pointer(
string memory cwAddr
) view external returns (address addr, uint16 version, bool exists);

function getCW721Pointer(
string memory cwAddr
) view external returns (address addr, uint16 version, bool exists);
}
1 change: 1 addition & 0 deletions precompiles/pointerview/abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"internalType":"string","name":"cwAddr","type":"string"}],"name":"getCW20Pointer","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint16","name":"version","type":"uint16"},{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"cwAddr","type":"string"}],"name":"getCW721Pointer","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint16","name":"version","type":"uint16"},{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"token","type":"string"}],"name":"getNativePointer","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint16","name":"version","type":"uint16"},{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"}]
Loading

0 comments on commit 1acafae

Please sign in to comment.