github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/erc20/keeper/evm_log_handler.go (about)

     1  package keeper
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math/big"
     7  
     8  	"github.com/ethereum/go-ethereum/accounts/abi"
     9  	"github.com/ethereum/go-ethereum/common"
    10  
    11  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    12  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    13  	"github.com/fibonacci-chain/fbc/x/erc20/types"
    14  	evmtypes "github.com/fibonacci-chain/fbc/x/evm/types"
    15  )
    16  
    17  var (
    18  	_ evmtypes.EvmLogHandler = SendToIbcEventHandler{}
    19  )
    20  
    21  const (
    22  	SendToIbcEventName         = "__OKCSendToIbc"
    23  	SendNative20ToIbcEventName = "__OKCSendNative20ToIbc"
    24  	SendToWasmEventName        = "__OKCSendToWasm"
    25  )
    26  
    27  // SendToIbcEvent represent the signature of
    28  // `event __OKCSendToIbc(string recipient, uint256 amount)`
    29  var SendToIbcEvent abi.Event
    30  
    31  // SendNative20ToIbcEvent represent the signature of
    32  // `event __OKCSendNative20ToIbc(string recipient, uint256 amount, string portID, string channelID)`
    33  var SendNative20ToIbcEvent abi.Event
    34  
    35  func init() {
    36  	addressType, _ := abi.NewType("address", "", nil)
    37  	uint256Type, _ := abi.NewType("uint256", "", nil)
    38  	stringType, _ := abi.NewType("string", "", nil)
    39  
    40  	SendToIbcEvent = abi.NewEvent(
    41  		SendToIbcEventName,
    42  		SendToIbcEventName,
    43  		false,
    44  		abi.Arguments{abi.Argument{
    45  			Name:    "sender",
    46  			Type:    addressType,
    47  			Indexed: false,
    48  		}, abi.Argument{
    49  			Name:    "recipient",
    50  			Type:    stringType,
    51  			Indexed: false,
    52  		}, abi.Argument{
    53  			Name:    "amount",
    54  			Type:    uint256Type,
    55  			Indexed: false,
    56  		}},
    57  	)
    58  
    59  	SendNative20ToIbcEvent = abi.NewEvent(
    60  		SendNative20ToIbcEventName,
    61  		SendNative20ToIbcEventName,
    62  		false,
    63  		abi.Arguments{abi.Argument{
    64  			Name:    "sender",
    65  			Type:    addressType,
    66  			Indexed: false,
    67  		}, abi.Argument{
    68  			Name:    "recipient",
    69  			Type:    stringType,
    70  			Indexed: false,
    71  		}, abi.Argument{
    72  			Name:    "amount",
    73  			Type:    uint256Type,
    74  			Indexed: false,
    75  		}, abi.Argument{
    76  			Name:    "portID",
    77  			Type:    stringType,
    78  			Indexed: false,
    79  		}, abi.Argument{
    80  			Name:    "channelID",
    81  			Type:    stringType,
    82  			Indexed: false,
    83  		}},
    84  	)
    85  }
    86  
    87  type SendToIbcEventHandler struct {
    88  	Keeper
    89  }
    90  
    91  func NewSendToIbcEventHandler(k Keeper) *SendToIbcEventHandler {
    92  	return &SendToIbcEventHandler{k}
    93  }
    94  
    95  // EventID Return the id of the log signature it handles
    96  func (h SendToIbcEventHandler) EventID() common.Hash {
    97  	return SendToIbcEvent.ID
    98  }
    99  
   100  // Handle Process the log
   101  func (h SendToIbcEventHandler) Handle(ctx sdk.Context, contract common.Address, data []byte) error {
   102  	h.Logger(ctx).Info("trigger evm event", "event", SendToIbcEvent.Name, "contract", contract)
   103  	// first confirm that the contract address and denom are registered,
   104  	// to avoid unpacking any contract '__OKCSendToIbc' event, which consumes performance
   105  	denom, found := h.Keeper.GetDenomByContract(ctx, contract)
   106  	if !found {
   107  		return fmt.Errorf("contract %s is not connected to native token", contract)
   108  	}
   109  	if !types.IsValidIBCDenom(denom) {
   110  		return fmt.Errorf("the native token associated with the contract %s is not an ibc voucher", contract)
   111  	}
   112  
   113  	unpacked, err := SendToIbcEvent.Inputs.Unpack(data)
   114  	if err != nil {
   115  		// log and ignore
   116  		h.Keeper.Logger(ctx).Error("log signature matches but failed to decode", "error", err)
   117  		return nil
   118  	}
   119  
   120  	contractAddr := sdk.AccAddress(contract.Bytes())
   121  	sender := sdk.AccAddress(unpacked[0].(common.Address).Bytes())
   122  	recipient := unpacked[1].(string)
   123  	amount := sdk.NewIntFromBigInt(unpacked[2].(*big.Int))
   124  	amountDec := sdk.NewDecFromIntWithPrec(amount, sdk.Precision)
   125  	vouchers := sdk.NewCoins(sdk.NewCoin(denom, amountDec))
   126  
   127  	// 1. transfer IBC coin to user so that he will be the refunded address if transfer fails
   128  	if err = h.bankKeeper.SendCoins(ctx, contractAddr, sender, vouchers); err != nil {
   129  		return err
   130  	}
   131  
   132  	// 2. Initiate IBC transfer from sender account
   133  	if err = h.Keeper.IbcTransferVouchers(ctx, sender.String(), recipient, vouchers); err != nil {
   134  		return err
   135  	}
   136  
   137  	if !ctx.IsCheckTx() && !ctx.IsTraceTx() {
   138  		txHash := tmtypes.Tx(ctx.TxBytes()).Hash(ctx.BlockHeight())
   139  		ethTxHash := common.BytesToHash(txHash)
   140  		ibcEvents := eventStr(ctx.EventManager().Events())
   141  
   142  		h.Keeper.addSendToIbcInnerTx(ethTxHash.Hex(), contract.String(), sender.String(), recipient, vouchers.String(), ibcEvents)
   143  	}
   144  	return nil
   145  }
   146  
   147  type SendNative20ToIbcEventHandler struct {
   148  	Keeper
   149  }
   150  
   151  func NewSendNative20ToIbcEventHandler(k Keeper) *SendNative20ToIbcEventHandler {
   152  	return &SendNative20ToIbcEventHandler{k}
   153  }
   154  
   155  // EventID Return the id of the log signature it handles
   156  func (h SendNative20ToIbcEventHandler) EventID() common.Hash {
   157  	return SendNative20ToIbcEvent.ID
   158  }
   159  
   160  // Handle Process the log
   161  func (h SendNative20ToIbcEventHandler) Handle(ctx sdk.Context, contract common.Address, data []byte) error {
   162  	h.Logger(ctx).Info("trigger evm event", "event", SendNative20ToIbcEvent.Name, "contract", contract)
   163  	// first confirm that the contract address and denom are registered,
   164  	// to avoid unpacking any contract '__OKCSendNative20ToIbc' event, which consumes performance
   165  	denom, found := h.Keeper.GetDenomByContract(ctx, contract)
   166  	if !found {
   167  		return fmt.Errorf("contract %s is not connected to native token", contract)
   168  	}
   169  	if err := sdk.ValidateDenom(denom); err != nil {
   170  		return fmt.Errorf("the native token associated with the contract %s is not an valid token", contract)
   171  	}
   172  
   173  	unpacked, err := SendNative20ToIbcEvent.Inputs.Unpack(data)
   174  	if err != nil {
   175  		// log and ignore
   176  		h.Keeper.Logger(ctx).Error("log signature matches but failed to decode", "error", err)
   177  		return nil
   178  	}
   179  
   180  	sender := sdk.AccAddress(unpacked[0].(common.Address).Bytes())
   181  	recipient := unpacked[1].(string)
   182  	amount := sdk.NewIntFromBigInt(unpacked[2].(*big.Int))
   183  	portID := unpacked[3].(string)
   184  	channelID := unpacked[4].(string)
   185  
   186  	amountDec := sdk.NewDecFromIntWithPrec(amount, sdk.Precision)
   187  	native20s := sdk.NewCoins(sdk.NewCoin(denom, amountDec))
   188  
   189  	// 1. mint new tokens to user so that he will be the refunded address if transfer fails
   190  	if err = h.supplyKeeper.MintCoins(ctx, types.ModuleName, native20s); err != nil {
   191  		return err
   192  	}
   193  	if err = h.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sender, native20s); err != nil {
   194  		return err
   195  	}
   196  
   197  	// 2. Initiate IBC transfer from sender account
   198  	if err = h.Keeper.IbcTransferNative20(ctx, sender.String(), recipient, native20s, portID, channelID); err != nil {
   199  		return err
   200  	}
   201  
   202  	if !ctx.IsCheckTx() && !ctx.IsTraceTx() {
   203  		txHash := tmtypes.Tx(ctx.TxBytes()).Hash(ctx.BlockHeight())
   204  		ethTxHash := common.BytesToHash(txHash)
   205  		ibcEvents := eventStr(ctx.EventManager().Events())
   206  
   207  		h.Keeper.addSendNative20ToIbcInnerTx(ethTxHash.Hex(), types.ModuleName, sender.String(), recipient, native20s.String(), ibcEvents)
   208  	}
   209  	return nil
   210  }
   211  
   212  func eventStr(events sdk.Events) string {
   213  	if len(events) == 0 {
   214  		return ""
   215  	}
   216  	var buf bytes.Buffer
   217  	buf.WriteString(`{"events":`)
   218  	sdk.StringifyEvents(events).MarshalJsonToBuffer(&buf)
   219  	buf.WriteString("}")
   220  
   221  	return buf.String()
   222  }