upgrade to ethermint v0.21.0 #99
@ -4,7 +4,9 @@ order: 6
|
||||
|
||||
# Hooks
|
||||
|
||||
The evm module implements an `EvmHooks` interface that extend the `Tx` processing logic externally. This supports EVM contracts to call native cosmos modules by
|
||||
The `x/evm` module implements an `EvmHooks` interface that extend and customize the `Tx` processing logic externally.
|
||||
|
||||
This supports EVM contracts to call native cosmos modules by
|
||||
|
||||
1. defining a log signature and emitting the specific log from the smart contract,
|
||||
2. recognizing those logs in the native tx processing code, and
|
||||
@ -14,7 +16,8 @@ To do this, the interface includes a `PostTxProcessing` hook that registers cus
|
||||
|
||||
```go
|
||||
type EvmHooks interface {
|
||||
PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*ethtypes.Log) error
|
||||
// Must be called after tx is processed successfully, if return an error, the whole transaction is reverted.
|
||||
PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error
|
||||
}
|
||||
```
|
||||
|
||||
@ -23,11 +26,11 @@ type EvmHooks interface {
|
||||
`PostTxProcessing` is only called after a EVM transaction finished successfully and delegates the call to underlying hooks. If no hook has been registered, this function returns with a `nil` error.
|
||||
|
||||
```go
|
||||
func (k *Keeper) PostTxProcessing(txHash common.Hash, logs []*ethtypes.Log) error {
|
||||
func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
|
||||
if k.hooks == nil {
|
||||
return nil
|
||||
}
|
||||
return k.hooks.PostTxProcessing(k.Ctx(), txHash, logs)
|
||||
return k.hooks.PostTxProcessing(k.Ctx(), msg, receipt)
|
||||
}
|
||||
```
|
||||
|
||||
@ -35,9 +38,9 @@ It's executed in the same cache context as the EVM transaction, if it returns an
|
||||
|
||||
The error returned by the hooks is translated to a VM error `failed to process native logs`, the detailed error message is stored in the return value. The message is sent to native modules asynchronously, there's no way for the caller to catch and recover the error.
|
||||
|
||||
## Use Case: Call Native erc20 Module on Evmos
|
||||
## Use Case: Call Native ERC20 Module on Evmos
|
||||
|
||||
Here is an example taken from the [Evmos erc20 module](https://evmos.dev/modules/erc20/) that shows how the `EVMHooks` supports a contract calling a native module to convert ERC-20 Tokens intor Cosmos native Coins. Following the steps from above.
|
||||
Here is an example taken from the Evmos [erc20 module](https://evmos.dev/modules/erc20/) that shows how the `EVMHooks` supports a contract calling a native module to convert ERC-20 Tokens into Cosmos native Coins. Following the steps from above.
|
||||
|
||||
You can define and emit a `Transfer` log signature in the smart contract like this:
|
||||
|
||||
@ -63,15 +66,21 @@ The application will register a `BankSendHook` to the `EvmKeeper`. It recognizes
|
||||
const ERC20EventTransfer = "Transfer"
|
||||
|
||||
// PostTxProcessing implements EvmHooks.PostTxProcessing
|
||||
func (k Keeper) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*ethtypes.Log) error {
|
||||
params := k.GetParams(ctx)
|
||||
if !params.EnableEVMHook {
|
||||
return sdkerrors.Wrap(types.ErrInternalTokenPair, "EVM Hook is currently disabled")
|
||||
func (k Keeper) PostTxProcessing(
|
||||
ctx sdk.Context,
|
||||
msg core.Message,
|
||||
receipt *ethtypes.Receipt,
|
||||
) error {
|
||||
params := h.k.GetParams(ctx)
|
||||
if !params.EnableErc20 || !params.EnableEVMHook {
|
||||
// no error is returned to allow for other post processing txs
|
||||
// to pass
|
||||
return nil
|
||||
}
|
||||
|
||||
erc20 := contracts.ERC20BurnableContract.ABI
|
||||
|
||||
for i, log := range logs {
|
||||
for i, log := range receipt.Logs {
|
||||
if len(log.Topics) < 3 {
|
||||
continue
|
||||
}
|
||||
@ -85,13 +94,13 @@ func (k Keeper) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*et
|
||||
}
|
||||
|
||||
if event.Name != types.ERC20EventTransfer {
|
||||
k.Logger(ctx).Info("emitted event", "name", event.Name, "signature", event.Sig)
|
||||
h.k.Logger(ctx).Info("emitted event", "name", event.Name, "signature", event.Sig)
|
||||
continue
|
||||
}
|
||||
|
||||
transferEvent, err := erc20.Unpack(event.Name, log.Data)
|
||||
if err != nil {
|
||||
k.Logger(ctx).Error("failed to unpack transfer event", "error", err.Error())
|
||||
h.k.Logger(ctx).Error("failed to unpack transfer event", "error", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
@ -108,21 +117,26 @@ func (k Keeper) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*et
|
||||
// check that the contract is a registered token pair
|
||||
contractAddr := log.Address
|
||||
|
||||
id := k.GetERC20Map(ctx, contractAddr)
|
||||
id := h.k.GetERC20Map(ctx, contractAddr)
|
||||
|
||||
if len(id) == 0 {
|
||||
// no token is registered for the caller contract
|
||||
continue
|
||||
}
|
||||
|
||||
pair, found := k.GetTokenPair(ctx, id)
|
||||
pair, found := h.k.GetTokenPair(ctx, id)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
// check that relaying for the pair is enabled
|
||||
// check that conversion for the pair is enabled
|
||||
if !pair.Enabled {
|
||||
return fmt.Errorf("internal relaying is disabled for pair %s, please create a governance proposal", contractAddr) // convert to SDK error
|
||||
// continue to allow transfers for the ERC20 in case the token pair is disabled
|
||||
h.k.Logger(ctx).Debug(
|
||||
"ERC20 token -> Cosmos coin conversion is disabled for pair",
|
||||
"coin", pair.Denom, "contract", pair.Erc20Address,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// ignore as the burning always transfers to the zero address
|
||||
@ -140,15 +154,15 @@ func (k Keeper) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*et
|
||||
// Mint the coin only if ERC20 is external
|
||||
switch pair.ContractOwner {
|
||||
case types.OWNER_MODULE:
|
||||
_, err = k.CallEVM(ctx, erc20, types.ModuleAddress, contractAddr, "burn", tokens)
|
||||
_, err = h.k.CallEVM(ctx, erc20, types.ModuleAddress, contractAddr, true, "burn", tokens)
|
||||
case types.OWNER_EXTERNAL:
|
||||
err = k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
|
||||
err = h.k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
|
||||
default:
|
||||
err = types.ErrUndefinedOwner
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
k.Logger(ctx).Debug(
|
||||
h.k.Logger(ctx).Debug(
|
||||
"failed to process EVM hook for ER20 -> coin conversion",
|
||||
"coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
|
||||
)
|
||||
@ -160,10 +174,10 @@ func (k Keeper) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*et
|
||||
recipient := sdk.AccAddress(from.Bytes())
|
||||
|
||||
// transfer the tokens from ModuleAccount to sender address
|
||||
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, coins); err != nil {
|
||||
k.Logger(ctx).Debug(
|
||||
if err := h.k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, coins); err != nil {
|
||||
h.k.Logger(ctx).Debug(
|
||||
"failed to process EVM hook for ER20 -> coin conversion",
|
||||
"tx-hash", txHash.Hex(), "log-idx", i,
|
||||
"tx-hash", receipt.TxHash.Hex(), "log-idx", i,
|
||||
"coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
|
||||
)
|
||||
continue
|
||||
@ -171,7 +185,6 @@ func (k Keeper) PostTxProcessing(ctx sdk.Context, txHash common.Hash, logs []*et
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
Lastly, register the hook in `app.go`:
|
||||
|
Loading…
Reference in New Issue
Block a user