Skip to content

Commit 1c46e1d

Browse files
codchenudpatil
authored andcommitted
Add ERC pointer registry (#1490)
1 parent 7e7ddad commit 1c46e1d

File tree

20 files changed

+738
-30
lines changed

20 files changed

+738
-30
lines changed

precompiles/common/expected_keepers.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ type EVMKeeper interface {
3131
IsCodeHashWhitelistedForBankSend(ctx sdk.Context, h common.Hash) bool
3232
GetPriorityNormalizer(ctx sdk.Context) sdk.Dec
3333
GetBaseDenom(ctx sdk.Context) string
34+
SetERC20NativePointer(ctx sdk.Context, token string, addr common.Address) error
35+
GetERC20NativePointer(ctx sdk.Context, token string) (addr common.Address, version uint16, exists bool)
36+
SetERC20CW20Pointer(ctx sdk.Context, cw20Address string, addr common.Address) error
37+
GetERC20CW20Pointer(ctx sdk.Context, cw20Address string) (addr common.Address, version uint16, exists bool)
38+
SetERC721CW721Pointer(ctx sdk.Context, cw721Address string, addr common.Address) error
39+
GetERC721CW721Pointer(ctx sdk.Context, cw721Address string) (addr common.Address, version uint16, exists bool)
3440
}
3541

3642
type OracleKeeper interface {

precompiles/ibc/ibc.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99

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

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

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

101101
gasMultiplier := p.evmKeeper.GetPriorityNormalizer(ctx)
102102
gasLimitBigInt := new(big.Int).Mul(new(big.Int).SetUint64(suppliedGas), gasMultiplier.RoundInt().BigInt())
103-
if gasLimitBigInt.Cmp(wasmd.MaxUint64BigInt) > 0 {
104-
gasLimitBigInt = wasmd.MaxUint64BigInt
103+
if gasLimitBigInt.Cmp(utils.BigMaxU64) > 0 {
104+
gasLimitBigInt = utils.BigMaxU64
105105
}
106106
ctx = ctx.WithGasMeter(sdk.NewGasMeter(gasLimitBigInt.Uint64()))
107107

precompiles/pointer/Pointer.sol

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
address constant POINTER_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000100b;
5+
6+
IPointer constant POINTER_CONTRACT = IPointer(POINTER_PRECOMPILE_ADDRESS);
7+
8+
interface IPointer {
9+
function addNativePointer(
10+
string memory token
11+
) payable external returns (address ret);
12+
13+
function addCW20Pointer(
14+
string memory cwAddr
15+
) payable external returns (address ret);
16+
17+
function addCW721Pointer(
18+
string memory cwAddr
19+
) payable external returns (address ret);
20+
}

precompiles/pointer/abi.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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"}]

precompiles/pointer/pointer.go

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
package pointer
2+
3+
import (
4+
"bytes"
5+
"embed"
6+
"encoding/json"
7+
"fmt"
8+
"math"
9+
"math/big"
10+
11+
sdk "github.com/cosmos/cosmos-sdk/types"
12+
ethabi "github.com/ethereum/go-ethereum/accounts/abi"
13+
"github.com/ethereum/go-ethereum/common"
14+
"github.com/ethereum/go-ethereum/core/vm"
15+
pcommon "github.com/sei-protocol/sei-chain/precompiles/common"
16+
"github.com/sei-protocol/sei-chain/utils"
17+
"github.com/sei-protocol/sei-chain/x/evm/artifacts/cw20"
18+
"github.com/sei-protocol/sei-chain/x/evm/artifacts/cw721"
19+
"github.com/sei-protocol/sei-chain/x/evm/artifacts/native"
20+
)
21+
22+
const (
23+
AddNativePointer = "addNativePointer"
24+
AddCW20Pointer = "addCW20Pointer"
25+
AddCW721Pointer = "addCW721Pointer"
26+
)
27+
28+
const PointerAddress = "0x000000000000000000000000000000000000100b"
29+
30+
var _ vm.PrecompiledContract = &Precompile{}
31+
32+
// Embed abi json file to the executable binary. Needed when importing as dependency.
33+
//
34+
//go:embed abi.json
35+
var f embed.FS
36+
37+
type Precompile struct {
38+
pcommon.Precompile
39+
evmKeeper pcommon.EVMKeeper
40+
bankKeeper pcommon.BankKeeper
41+
wasmdKeeper pcommon.WasmdViewKeeper
42+
address common.Address
43+
44+
AddNativePointerID []byte
45+
AddCW20PointerID []byte
46+
AddCW721PointerID []byte
47+
}
48+
49+
func NewPrecompile(evmKeeper pcommon.EVMKeeper, bankKeeper pcommon.BankKeeper, wasmdKeeper pcommon.WasmdViewKeeper) (*Precompile, error) {
50+
abiBz, err := f.ReadFile("abi.json")
51+
if err != nil {
52+
return nil, fmt.Errorf("error loading the pointer ABI %s", err)
53+
}
54+
55+
newAbi, err := ethabi.JSON(bytes.NewReader(abiBz))
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
p := &Precompile{
61+
Precompile: pcommon.Precompile{ABI: newAbi},
62+
evmKeeper: evmKeeper,
63+
bankKeeper: bankKeeper,
64+
wasmdKeeper: wasmdKeeper,
65+
address: common.HexToAddress(PointerAddress),
66+
}
67+
68+
for name, m := range newAbi.Methods {
69+
switch name {
70+
case AddNativePointer:
71+
p.AddNativePointerID = m.ID
72+
case AddCW20Pointer:
73+
p.AddCW20PointerID = m.ID
74+
case AddCW721Pointer:
75+
p.AddCW721PointerID = m.ID
76+
}
77+
}
78+
79+
return p, nil
80+
}
81+
82+
// RequiredGas returns the required bare minimum gas to execute the precompile.
83+
func (p Precompile) RequiredGas(input []byte) uint64 {
84+
// gas is calculated dynamically
85+
return 0
86+
}
87+
88+
func (p Precompile) Address() common.Address {
89+
return p.address
90+
}
91+
92+
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) {
93+
ctx, method, args, err := p.Prepare(evm, input)
94+
if err != nil {
95+
return nil, 0, err
96+
}
97+
98+
switch method.Name {
99+
case AddNativePointer:
100+
return p.AddNative(ctx, method, caller, args, value, evm, suppliedGas)
101+
case AddCW20Pointer:
102+
return p.AddCW20(ctx, method, caller, args, value, evm, suppliedGas)
103+
case AddCW721Pointer:
104+
return p.AddCW721(ctx, method, caller, args, value, evm, suppliedGas)
105+
default:
106+
err = fmt.Errorf("unknown method %s", method.Name)
107+
}
108+
return
109+
}
110+
111+
func (p Precompile) Run(*vm.EVM, common.Address, []byte, *big.Int) ([]byte, error) {
112+
panic("static gas Run is not implemented for dynamic gas precompile")
113+
}
114+
115+
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) {
116+
pcommon.AssertArgsLength(args, 1)
117+
token := args[0].(string)
118+
existingAddr, existingVersion, exists := p.evmKeeper.GetERC20NativePointer(ctx, token)
119+
if exists && existingVersion >= native.CurrentVersion {
120+
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)
121+
}
122+
metadata, metadataExists := p.bankKeeper.GetDenomMetaData(ctx, token)
123+
if !metadataExists {
124+
return nil, 0, fmt.Errorf("denom %s does not have metadata stored and thus can only have its pointer set through gov proposal", token)
125+
}
126+
name := metadata.Name
127+
symbol := metadata.Symbol
128+
var decimals uint8
129+
for _, denomUnit := range metadata.DenomUnits {
130+
if denomUnit.Exponent > uint32(decimals) && denomUnit.Exponent <= math.MaxUint8 {
131+
decimals = uint8(denomUnit.Exponent)
132+
name = denomUnit.Denom
133+
symbol = denomUnit.Denom
134+
if len(denomUnit.Aliases) > 0 {
135+
name = denomUnit.Aliases[0]
136+
}
137+
}
138+
}
139+
constructorArguments := []interface{}{
140+
token, name, symbol, decimals,
141+
}
142+
143+
packedArgs, err := native.GetParsedABI().Pack("", constructorArguments...)
144+
if err != nil {
145+
panic(err)
146+
}
147+
bin := append(native.GetBin(), packedArgs...)
148+
if value == nil {
149+
value = utils.Big0
150+
}
151+
ret, contractAddr, remainingGas, err := evm.Create(vm.AccountRef(caller), bin, suppliedGas, value)
152+
if err != nil {
153+
return
154+
}
155+
err = p.evmKeeper.SetERC20NativePointer(ctx, token, contractAddr)
156+
if err != nil {
157+
return
158+
}
159+
ret, err = method.Outputs.Pack(contractAddr)
160+
return
161+
}
162+
163+
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) {
164+
pcommon.AssertArgsLength(args, 1)
165+
cwAddr := args[0].(string)
166+
existingAddr, existingVersion, exists := p.evmKeeper.GetERC20CW20Pointer(ctx, cwAddr)
167+
if exists && existingVersion >= cw20.CurrentVersion {
168+
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)
169+
}
170+
res, err := p.wasmdKeeper.QuerySmart(ctx, sdk.MustAccAddressFromBech32(cwAddr), []byte("{\"token_info\":{}}"))
171+
if err != nil {
172+
return nil, 0, err
173+
}
174+
formattedRes := map[string]interface{}{}
175+
if err := json.Unmarshal(res, &formattedRes); err != nil {
176+
return nil, 0, err
177+
}
178+
name := formattedRes["name"].(string)
179+
symbol := formattedRes["symbol"].(string)
180+
constructorArguments := []interface{}{
181+
cwAddr, name, symbol,
182+
}
183+
184+
packedArgs, err := cw20.GetParsedABI().Pack("", constructorArguments...)
185+
if err != nil {
186+
panic(err)
187+
}
188+
bin := append(cw20.GetBin(), packedArgs...)
189+
if value == nil {
190+
value = utils.Big0
191+
}
192+
ret, contractAddr, remainingGas, err := evm.Create(vm.AccountRef(caller), bin, suppliedGas, value)
193+
if err != nil {
194+
return
195+
}
196+
err = p.evmKeeper.SetERC20CW20Pointer(ctx, cwAddr, contractAddr)
197+
if err != nil {
198+
return
199+
}
200+
ret, err = method.Outputs.Pack(contractAddr)
201+
return
202+
}
203+
204+
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) {
205+
pcommon.AssertArgsLength(args, 1)
206+
cwAddr := args[0].(string)
207+
existingAddr, existingVersion, exists := p.evmKeeper.GetERC721CW721Pointer(ctx, cwAddr)
208+
if exists && existingVersion >= cw721.CurrentVersion {
209+
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)
210+
}
211+
res, err := p.wasmdKeeper.QuerySmart(ctx, sdk.MustAccAddressFromBech32(cwAddr), []byte("{\"contract_info\":{}}"))
212+
if err != nil {
213+
return nil, 0, err
214+
}
215+
formattedRes := map[string]interface{}{}
216+
if err := json.Unmarshal(res, &formattedRes); err != nil {
217+
return nil, 0, err
218+
}
219+
name := formattedRes["name"].(string)
220+
symbol := formattedRes["symbol"].(string)
221+
constructorArguments := []interface{}{
222+
cwAddr, name, symbol,
223+
}
224+
225+
packedArgs, err := cw721.GetParsedABI().Pack("", constructorArguments...)
226+
if err != nil {
227+
panic(err)
228+
}
229+
bin := append(cw721.GetBin(), packedArgs...)
230+
if value == nil {
231+
value = utils.Big0
232+
}
233+
ret, contractAddr, remainingGas, err := evm.Create(vm.AccountRef(caller), bin, suppliedGas, value)
234+
if err != nil {
235+
return
236+
}
237+
err = p.evmKeeper.SetERC721CW721Pointer(ctx, cwAddr, contractAddr)
238+
if err != nil {
239+
return
240+
}
241+
ret, err = method.Outputs.Pack(contractAddr)
242+
return
243+
}

precompiles/pointer/pointer_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package pointer_test
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
8+
"github.com/ethereum/go-ethereum/common"
9+
"github.com/ethereum/go-ethereum/core"
10+
"github.com/ethereum/go-ethereum/core/vm"
11+
"github.com/sei-protocol/sei-chain/app"
12+
"github.com/sei-protocol/sei-chain/precompiles/pointer"
13+
testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper"
14+
"github.com/sei-protocol/sei-chain/x/evm/artifacts/native"
15+
"github.com/sei-protocol/sei-chain/x/evm/state"
16+
"github.com/sei-protocol/sei-chain/x/evm/types"
17+
"github.com/stretchr/testify/require"
18+
)
19+
20+
func TestAddNative(t *testing.T) {
21+
testApp := app.Setup(false, false)
22+
p, err := pointer.NewPrecompile(&testApp.EvmKeeper, testApp.BankKeeper, testApp.WasmKeeper)
23+
require.Nil(t, err)
24+
ctx := testApp.GetContextForDeliverTx([]byte{}).WithBlockTime(time.Now())
25+
_, caller := testkeeper.MockAddressPair()
26+
suppliedGas := uint64(10000000)
27+
cfg := types.DefaultChainConfig().EthereumConfig(testApp.EvmKeeper.ChainID(ctx))
28+
29+
// token has no metadata
30+
m, err := p.ABI.MethodById(p.AddNativePointerID)
31+
require.Nil(t, err)
32+
args, err := m.Inputs.Pack("test")
33+
require.Nil(t, err)
34+
statedb := state.NewDBImpl(ctx, &testApp.EvmKeeper, true)
35+
blockCtx, _ := testApp.EvmKeeper.GetVMBlockContext(ctx, core.GasPool(suppliedGas))
36+
evm := vm.NewEVM(*blockCtx, vm.TxContext{}, statedb, cfg, vm.Config{})
37+
_, g, err := p.RunAndCalculateGas(evm, caller, caller, append(p.AddNativePointerID, args...), suppliedGas, nil)
38+
require.NotNil(t, err)
39+
require.Equal(t, uint64(0), g)
40+
_, _, exists := testApp.EvmKeeper.GetERC20NativePointer(statedb.Ctx(), "test")
41+
require.False(t, exists)
42+
43+
// token has metadata
44+
testApp.BankKeeper.SetDenomMetaData(ctx, banktypes.Metadata{
45+
Base: "test",
46+
Name: "base_name",
47+
Symbol: "base_symbol",
48+
DenomUnits: []*banktypes.DenomUnit{{
49+
Exponent: 6,
50+
Denom: "denom",
51+
Aliases: []string{"DENOM"},
52+
}},
53+
})
54+
statedb = state.NewDBImpl(ctx, &testApp.EvmKeeper, true)
55+
evm = vm.NewEVM(*blockCtx, vm.TxContext{}, statedb, cfg, vm.Config{})
56+
ret, g, err := p.RunAndCalculateGas(evm, caller, caller, append(p.AddNativePointerID, args...), suppliedGas, nil)
57+
require.Nil(t, err)
58+
require.Equal(t, uint64(8907806), g)
59+
outputs, err := m.Outputs.Unpack(ret)
60+
require.Nil(t, err)
61+
addr := outputs[0].(common.Address)
62+
pointerAddr, version, exists := testApp.EvmKeeper.GetERC20NativePointer(statedb.Ctx(), "test")
63+
require.Equal(t, addr, pointerAddr)
64+
require.Equal(t, native.CurrentVersion, version)
65+
require.True(t, exists)
66+
67+
// pointer already exists
68+
statedb = state.NewDBImpl(statedb.Ctx(), &testApp.EvmKeeper, true)
69+
evm = vm.NewEVM(*blockCtx, vm.TxContext{}, statedb, cfg, vm.Config{})
70+
_, g, err = p.RunAndCalculateGas(evm, caller, caller, append(p.AddNativePointerID, args...), suppliedGas, nil)
71+
require.NotNil(t, err)
72+
require.Equal(t, uint64(0), g)
73+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
address constant POINTERVIEW_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000100A;
5+
6+
IPointerview constant POINTERVIEW_CONTRACT = IPointerview(POINTERVIEW_PRECOMPILE_ADDRESS);
7+
8+
interface IPointerview {
9+
function getNativePointer(
10+
string memory token
11+
) view external returns (address addr, uint16 version, bool exists);
12+
13+
function getCW20Pointer(
14+
string memory cwAddr
15+
) view external returns (address addr, uint16 version, bool exists);
16+
17+
function getCW721Pointer(
18+
string memory cwAddr
19+
) view external returns (address addr, uint16 version, bool exists);
20+
}

precompiles/pointerview/abi.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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"}]

0 commit comments

Comments
 (0)