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 }