Skip to content

Commit

Permalink
implement swap native token from erc20 contract
Browse files Browse the repository at this point in the history
  • Loading branch information
dreamer-zq committed Apr 16, 2024
1 parent f728b7f commit 997a167
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 2 deletions.
105 changes: 105 additions & 0 deletions modules/token/keeper/evm_hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package keeper

import (
"math/big"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/ethereum/go-ethereum/core"
ethtypes "github.com/ethereum/go-ethereum/core/types"

"github.com/irisnet/irismod/contracts"
"github.com/irisnet/irismod/modules/token/types"
)

type erc20Hook struct {
k Keeper
}

// PostTxProcessing processes the transaction receipt for ERC20 token swap to native.
//
// Parameters:
//
// ctx: the context in which the function is executed.
// msg: the core message associated with the transaction.
// receipt: the Ethereum receipt containing the transaction logs.
//
// Return type: error
func (hook erc20Hook) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
erc20 := contracts.ERC20TokenContract.ABI
for _, log := range receipt.Logs {
// Note: the `SwapToNative` event contains 1 topics
if len(log.Topics) != 1 {
continue
}

// Check if event is included in ERC20
eventID := log.Topics[0]
event, err := erc20.EventByID(eventID)
if err != nil {
continue
}

// Check if event is a `SwapToNative` event.
if event.Name != contracts.EventSwapToNative {
hook.k.Logger(ctx).Info("emitted event", "name", event.Name, "signature", event.Sig)
continue
}

token, err := hook.k.getTokenByContract(ctx, log.Address)
if err != nil {
hook.k.Logger(ctx).Error("invalid SwapToNative event", "contract", log.Address.Hex())
continue
}

eventArgs, err := erc20.Unpack(event.Name, log.Data)
if err != nil {
return errorsmod.Wrap(types.ErrInvalidContract, "failed to unpack SwapToNative event")
}

if len(eventArgs) != 2 {
return errorsmod.Wrapf(
types.ErrInvalidContract,
"swapToNative event has wrong number of parameters, expected 2, actual: %d",
len(eventArgs),
)
}

to, ok := eventArgs[0].(string)
if !ok || len(to) == 0 {
return errorsmod.Wrap(
types.ErrInvalidContract,
"swapToNative event `to` parameters is invalid, expected string",
)
}

receiver, err := sdk.AccAddressFromBech32(to)
if err != nil {
return errorsmod.Wrapf(
types.ErrInvalidContract,
"swapToNative event `to` parameters is invalid, expected iaa address, actual: %s",
to,
)
}

amount, ok := eventArgs[1].(*big.Int)
if !ok || amount.Cmp(big.NewInt(0)) == 0 {
return errorsmod.Wrap(
types.ErrInvalidContract,
"swapToNative event `amount` parameters is invalid, expected `*big.Int`",
)
}

mintedCoins := sdk.NewCoins(sdk.NewCoin(token.MinUnit, sdkmath.NewIntFromBigInt(amount)))
if err := hook.k.bankKeeper.MintCoins(ctx, types.ModuleName, mintedCoins); err != nil {
return err
}

if err := hook.k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, receiver, mintedCoins); err != nil {
return err
}
}
return nil
}
5 changes: 5 additions & 0 deletions modules/token/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("irismod/%s", types.ModuleName))
}

// Hooks returns the keeper's Hooks struct.
func (k Keeper) Hooks() types.Hook {
return erc20Hook{k}
}

// IssueToken issues a new token
func (k Keeper) IssueToken(
ctx sdk.Context,
Expand Down
23 changes: 23 additions & 0 deletions modules/token/keeper/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

gogotypes "github.com/cosmos/gogoproto/types"
"github.com/ethereum/go-ethereum/common"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -255,6 +256,28 @@ func (k Keeper) getTokenByMinUnit(ctx sdk.Context, minUnit string) (token v1.Tok
return token, nil
}

func (k Keeper) getTokenByContract(ctx sdk.Context, contract common.Address) (token v1.Token, err error) {
store := ctx.KVStore(k.storeKey)

bz := store.Get(types.KeyContract(contract.Hex()))
if bz == nil {
return token, errorsmod.Wrap(
types.ErrTokenNotExists,
fmt.Sprintf("token contract %s does not exist", contract),
)
}

var symbol gogotypes.StringValue
k.cdc.MustUnmarshal(bz, &symbol)

token, err = k.getTokenBySymbol(ctx, symbol.Value)
if err != nil {
return token, err
}

return token, nil
}

func (k Keeper) getSymbolByMinUnit(ctx sdk.Context, minUnit string) (string, error) {
store := ctx.KVStore(k.storeKey)

Expand Down
1 change: 1 addition & 0 deletions modules/token/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ var (
ErrERC20AlreadyExists = errorsmod.Register(ModuleName, 22, "erc20 contract already exists")
ErrERC20NotDeployed = errorsmod.Register(ModuleName, 23, "erc20 contract not deployed")
ErrUnsupportedKey = errorsmod.Register(ModuleName, 24, "evm not supported public key")
ErrInvalidContract = errorsmod.Register(ModuleName, 25, "invalid contract")
)
8 changes: 6 additions & 2 deletions modules/token/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"

"github.com/ethereum/go-ethereum/core"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"

cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
Expand Down Expand Up @@ -57,9 +58,7 @@ type AccountKeeper interface {

// EVMKeeper defines the expected keeper of the evm module
type EVMKeeper interface {
FeeDenom() string
ChainID() *big.Int

SupportedKey(pubKey cryptotypes.PubKey) bool
EstimateGas(ctx context.Context, req *types.EthCallRequest) (uint64, error)
ApplyMessage(ctx sdk.Context, msg core.Message, tracer vm.EVMLogger, commit bool) (*types.Result, error)
Expand All @@ -70,3 +69,8 @@ type ICS20Keeper interface{
HasTrace(ctx sdk.Context, denom string) bool
}

// Hook defines the hook interface
type Hook interface {
PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error
}

0 comments on commit 997a167

Please sign in to comment.