Skip to content

Commit

Permalink
feat: pre-compilation get ERC20 token
Browse files Browse the repository at this point in the history
  • Loading branch information
nulnut committed Jan 24, 2025
1 parent e72fe47 commit 39a2445
Show file tree
Hide file tree
Showing 13 changed files with 286 additions and 8 deletions.
2 changes: 1 addition & 1 deletion app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ func NewAppKeeper(
},
func(_ sdk.Context, _ ethparams.Rules) vm.PrecompiledContract {
return crosschain.NewPrecompiledContract(
appKeepers.BankKeeper, appKeepers.GovKeeper, crosschainPrecompileRouter)
appKeepers.BankKeeper, appKeepers.GovKeeper, appKeepers.Erc20Keeper, crosschainPrecompileRouter)
},
func(_ sdk.Context, _ ethparams.Rules) vm.PrecompiledContract {
return stakingprecompile.NewPrecompiledContract(
Expand Down
11 changes: 11 additions & 0 deletions contract/crosschain.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,14 @@ func (args *IsOracleOnlineArgs) Validate() error {
}
return nil
}

type GetERC20TokenArgs struct {
Denom common.Hash `abi:"_denom"`
}

func (args *GetERC20TokenArgs) Validate() error {
if args.Denom == (common.Hash{}) {
return errors.New("invalid denom")
}
return nil
}
12 changes: 12 additions & 0 deletions contract/crosschain_precompile.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ func (k CrosschainPrecompileKeeper) IsOracleOnline(ctx context.Context, args IsO
return res.Online, nil
}

func (k CrosschainPrecompileKeeper) GetERC20Token(ctx context.Context, args GetERC20TokenArgs) (common.Address, bool, error) {
res := struct {
Token common.Address
Enable bool
}{}
err := k.QueryContract(ctx, common.Address{}, k.contractAddr, k.abi, "getERC20Token", &res, args.Denom)
if err != nil {
return common.Address{}, false, err
}
return res.Token, res.Enable, nil
}

func (k CrosschainPrecompileKeeper) BridgeCall(ctx context.Context, value *big.Int, from common.Address, args BridgeCallArgs) (*evmtypes.MsgEthereumTxResponse, *big.Int, error) {
res, err := k.ApplyContract(ctx, from, k.contractAddr, value, k.abi, "bridgeCall",
args.DstChain, args.Refund, args.Tokens, args.Amounts, args.To, args.Data, args.QuoteId, args.GasLimit, args.Memo)
Expand Down
47 changes: 46 additions & 1 deletion contract/icrosschain.sol.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions precompiles/crosschain/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@ type Contract struct {
func NewPrecompiledContract(
bankKeeper types.BankKeeper,
govKeeper types.GovKeeper,
erc20Keeper types.Erc20Keeper,
router *Router,
) *Contract {
keeper := &Keeper{
bankKeeper: bankKeeper,
router: router,
bankKeeper: bankKeeper,
erc20Keeper: erc20Keeper,
router: router,
}
return &Contract{
govKeeper: govKeeper,
methods: []contract.PrecompileMethod{
NewBridgeCoinAmountMethod(keeper),
NewHasOracleMethod(keeper),
NewIsOracleOnlineMethod(keeper),
NewGetERC20TokenMethod(keeper),

NewCrosschainMethod(keeper),
NewBridgeCallMethod(keeper),
Expand Down
76 changes: 76 additions & 0 deletions precompiles/crosschain/get_erc20_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package crosschain

import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"

"github.com/pundiai/fx-core/v8/contract"
"github.com/pundiai/fx-core/v8/x/evm/types"
)

type GetERC20TokenMethod struct {
*Keeper
GetERC20TokenABI
}

func NewGetERC20TokenMethod(keeper *Keeper) *GetERC20TokenMethod {
return &GetERC20TokenMethod{
Keeper: keeper,
GetERC20TokenABI: NewGetERC20TokenABI(),
}
}

func (m *GetERC20TokenMethod) GetMethodId() []byte {
return m.Method.ID
}

func (m *GetERC20TokenMethod) RequiredGas() uint64 {
return 1_000
}

func (m *GetERC20TokenMethod) IsReadonly() bool {
return true
}

func (m *GetERC20TokenMethod) Run(evm *vm.EVM, vmContract *vm.Contract) ([]byte, error) {
args, err := m.UnpackInput(vmContract.Input)
if err != nil {
return nil, err
}
stateDB := evm.StateDB.(types.ExtStateDB)
denom := contract.Byte32ToString(args.Denom)
erc20Token, err := m.Keeper.erc20Keeper.GetERC20Token(stateDB.Context(), denom)
if err != nil {
return nil, err
}
return m.PackOutput(common.HexToAddress(erc20Token.GetErc20Address()), erc20Token.Enabled)
}

type GetERC20TokenABI struct {
abi.Method
}

func NewGetERC20TokenABI() GetERC20TokenABI {
return GetERC20TokenABI{
Method: crosschainABI.Methods["getERC20Token"],
}
}

func (m GetERC20TokenABI) PackInput(args contract.GetERC20TokenArgs) ([]byte, error) {
arguments, err := m.Method.Inputs.Pack(args.Denom)
if err != nil {
return nil, err
}
return append(m.Method.ID, arguments...), nil
}

func (m GetERC20TokenABI) UnpackInput(data []byte) (*contract.GetERC20TokenArgs, error) {
args := new(contract.GetERC20TokenArgs)
err := types.ParseMethodArgs(m.Method, args, data[4:])
return args, err
}

func (m GetERC20TokenABI) PackOutput(tokenAddr common.Address, enable bool) ([]byte, error) {
return m.Method.Outputs.Pack(tokenAddr, enable)
}
68 changes: 68 additions & 0 deletions precompiles/crosschain/get_erc20_token_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package crosschain_test

import (
"fmt"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"

"github.com/pundiai/fx-core/v8/contract"
"github.com/pundiai/fx-core/v8/precompiles/crosschain"
"github.com/pundiai/fx-core/v8/testutil/helpers"
erc20types "github.com/pundiai/fx-core/v8/x/erc20/types"
)

func TestCrosschainGetERC20Tokwn(t *testing.T) {
getERC20TokenABI := crosschain.NewGetERC20TokenMethod(nil)
require.Len(t, getERC20TokenABI.Method.Inputs, 1)
require.Len(t, getERC20TokenABI.Method.Outputs, 2)
}

func (suite *CrosschainPrecompileTestSuite) TestGetERC20Token() {
testCases := []struct {
name string
malleate func() (contract.GetERC20TokenArgs, error)
result bool
}{
{
name: "erc20 token",
malleate: func() (contract.GetERC20TokenArgs, error) {
denom := "usdt"
err := suite.App.Erc20Keeper.ERC20Token.Set(suite.Ctx, denom, erc20types.ERC20Token{
Erc20Address: helpers.GenHexAddress().Hex(),
Denom: denom,
Enabled: false,
ContractOwner: erc20types.OWNER_MODULE,
})
suite.Require().NoError(err)
return contract.GetERC20TokenArgs{
Denom: contract.MustStrToByte32(denom),
}, nil
},
result: true,
},
{
name: "denom not found",
malleate: func() (contract.GetERC20TokenArgs, error) {
return contract.GetERC20TokenArgs{
Denom: contract.MustStrToByte32("test"),
}, fmt.Errorf("collections: not found: key 'test' of type github.com/cosmos/gogoproto/fx.erc20.v1.ERC20Token")
},
result: false,
},
}

for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
args, expectErr := tc.malleate()
token, enable := suite.WithError(expectErr).GetERC20Token(suite.Ctx, args)
if tc.result {
erc20Token, err := suite.App.Erc20Keeper.GetERC20Token(suite.Ctx, contract.Byte32ToString(args.Denom))
suite.Require().NoError(err)
suite.Require().Equal(common.HexToAddress(erc20Token.Erc20Address), token)
suite.Require().Equal(erc20Token.Enabled, enable)
}
})
}
}
5 changes: 3 additions & 2 deletions precompiles/crosschain/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import (
)

type Keeper struct {
router *Router
bankKeeper types.BankKeeper
router *Router
bankKeeper types.BankKeeper
erc20Keeper types.Erc20Keeper
}

func (c *Keeper) EvmTokenToBaseCoin(ctx sdk.Context, caller contract.Caller, crosschainKeeper types.CrosschainKeeper, holder, tokenAddr common.Address, amount *big.Int) (sdk.Coin, error) {
Expand Down
1 change: 1 addition & 0 deletions precompiles/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,5 @@ type SlashingKeeper interface {

type Erc20Keeper interface {
GetBaseDenom(ctx context.Context, token string) (string, error)
GetERC20Token(ctx context.Context, baseDenom string) (erc20types.ERC20Token, error)
}
4 changes: 4 additions & 0 deletions solidity/contracts/interfaces/ICrosschain.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ interface ICrosschain is IBridgeCall, IBridgeOracle {
uint256 _eventNonce
) external returns (bool _result);

function getERC20Token(
bytes32 _denom
) external view returns (address _token, bool _enable);

// Deprecated
event CrossChain(
address indexed sender,
Expand Down
Loading

0 comments on commit 39a2445

Please sign in to comment.