-
Notifications
You must be signed in to change notification settings - Fork 807
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
738 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"}] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"}] |
Oops, something went wrong.