Injective | Documentation
InjectiveGithub
Injective | Documentation
Injective | Documentation
  • About Injective
  • DeFi
    • Wallet
      • Create a wallet
      • Accounts
    • Trading
      • Order Types
      • Trading Fees and Rebates
      • Margin Trading
      • Liquidation
      • Funding Rates
      • Performing Liquidations
      • Derivatives
      • Perpetuals
      • Expiry Futures
      • Pre-Launch Futures
      • Index Perpetual Futures
      • iAssets
    • Token Standards
      • INJ coin
      • Token Factory
      • CW20 Standard
    • Bridge
      • From Ethereum
      • From Solana
      • Using Wormhole
      • Using IBC
    • Staking
    • Governance
    • Auction
    • Transactions
    • Transaction Gas and Fees
    • Open Liquidity Program
      • Introduction
      • Rewards
      • Epochs
      • Eligibility
      • Program Details
      • Scoring Formula/Methodology
      • Formula Parameters
      • Reward Allocations
      • Flexible Reward Allocations
      • Eligible Markets
      • Legacy Reward Allocations
      • Reward Disbursements
      • Performance Tracking
      • Volatility Response Modifications
      • Fee Tiers
    • Injective Hub
    • Injective Explorer
  • Infrastructure
    • Command Line Node Interaction
    • gRPC Node Interaction
    • Go Node Interaction
    • REST Node Interaction
    • Run Node
    • Set up Keyring
    • Join Network
    • Cosmovisor
    • Upgrade Node
    • Mainnet Validator
      • Peggo
      • Canonical Chain Upgrades
      • v1.16.2
      • v1.16.1
      • v1.16.0
      • v1.15.0
      • v1.14.1
      • v1.14.0
      • v1.13.3
      • v1.13.2
      • v1.13.0 - Altaris
      • v1.12.1
      • v1.12.0 - Volan
      • v1.11.0
      • v1.10.0
      • 10009
      • 10008 - Camelot
      • 10007-rc1
      • 10006-rc1
      • 10005-rc1
      • 10004-rc1-patch
      • 10004-rc1
      • 10003-rc1
      • 10002-rc2
      • 10002-rc1
    • Testnet Validator
      • Peggo
    • Public Endpoints
    • Premium Endpoints
    • Archival Setup
    • Indexer API Reference
  • Developers
    • Convert addresses
    • Network Information
    • injectived
      • Install injectived
      • Using injectived
      • injectived Commands
      • Testnet Proposals
    • Concepts
      • Sentry Node
      • Indexer API
      • Trading Account
      • gRPC and Protobuf
      • Token Factory
      • Market min price tick size calculation
      • Market min quantity tick size calculation
      • CosmJs Support
      • Networks
    • Assets
      • Denom
      • Token Metadata
      • Injective List
      • Creating Tokens
    • dApps
      • Nuxt configuration
      • React configuration
      • Running examples
      • Smart contract examples
      • DEX examples
      • Webpack examples
    • Testnet Faucet
    • Chain API Reference
    • Injective TypeScript SDK
    • Injective Go SDK
    • Injective Python SDK
    • Injective CosmWasm SDK
    • Injective Rust SDK
    • The Graph
  • EVM Developers
    • EVM Network Information
    • Your First EVM Smart Contract
      • Compile with Hardhat
      • Test with Hardhat
      • Deploy with Hardhat
      • Verify with Hardhat
      • Interact with Hardhat
      • Compile with Foundry
      • Test with Foundry
      • Deploy with Foundry
      • Verify with Foundry
      • Interact with Foundry
    • Your First EVM dApp
      • Connect with MetaMask
      • Connect with WalletConnect
    • EVM Equivalence
    • MultiVM Token Standard
    • Precompiles
    • Bank Precompile
    • Exchange Precompile
    • ERC20 Module
    • Infrastructure & Tooling
    • EVM Testnet Faucet
    • EVM Testnet Explorer
  • Cosmwasm Developers
    • CosmWasm Smart Contracts
      • Your First CosmWasm Smart Contract
      • Injective Name Service
      • NeptuneService
      • CW20 to Bank & Market Order in One Transaction
    • Local Development Guide
    • Mainnet Deployment Guide
    • Whitelisting Deployment Address Guide
    • Create your Swap Contract Guide
    • Creating UIs Guide
    • Using Injective Queries and Messages
    • CW20 Adapter
    • Injective Test Tube
  • DeFi Developers
    • Min Price Tick Size Calculation
    • Min Quantity Tick Size Calculation
    • Testnet Faucet Integration
    • Launch a Token
    • Launch a Market
    • Denom Metadata
    • Provider Oracle
    • Build a DEX
    • Exchange API Reference
  • Native Developers
    • Wallets
      • Accounts
      • Connections
      • Strategy
      • Offchain Data
    • Bridges
      • Ethereum Bridge
    • Transactions
      • Cosmos Transaction
      • Ledger through Keplr Wallet Transaction
      • Ethereum Transaction
      • Ledger through Keplr Wallet Transaction
      • MsgBroadcaster Transaction
      • Private Key Transaction
      • Web3 Gateway Transaction
    • Transaction Examples
      • Auction
      • AuthZ
      • Bank
      • Distribution
      • Exchange
      • Fee Grant
      • Governance
      • IBC
      • Insurance
      • Peggy
      • Permissions
      • Staking
      • Token Factory
      • Wasm
    • Querying the Chain
      • Auction
      • Auth
      • Bank
      • Distribution
      • Exchange
      • Governance
      • IBC
      • Mint
      • Insurance Fund
      • Oracle
      • Peggy
      • Permissions
      • Staking
      • Tendermint
      • Wasm
      • WasmX
      • Token Factory
    • Querying the Indexer
      • Account
      • Auction
      • Derivatives
      • Explorer
      • Insurance Funds
      • Markets
      • Leaderboard
      • Mito
      • Oracle
      • Portfolio
      • Spot
      • Web3 Gateway Transactions
    • Streaming the Indexer
      • Account
      • Auction
      • Derivatives
      • Oracle
      • Portfolio
      • Spot
      • Explorer
    • Querying Ethereum with GraphQL
    • Injective Modules
      • Auction
        • State
        • Messages
        • EndBlock
        • Events
        • Params
        • Errors
      • Exchange
        • Derivative Markets Concepts
        • Spot Markets Concepts
        • Binary Option Markets Concepts
        • Other Concepts
        • State
        • State Transitions
        • Messages
        • Proposals
        • BeginBlock
        • EndBlock
        • Events
        • Params
        • MsgPrivilegedExecuteContract
        • Errors
      • ERC20
        • Concepts
        • State
        • Messages
        • Events
      • EVM
        • Concepts
        • State
        • State Transitions
        • Transactions
        • ABCI
        • Hooks
        • Events
        • Params
        • Client
      • Insurance
        • State
        • State Transitions
        • Messages
        • EndBlock
        • Events
        • Params
        • Improvements
        • Errors
      • OCR
        • Concepts
        • State
        • Messages
        • Proposals
        • BeginBlock
        • Hooks
        • Events
        • Params
        • Errors
      • Oracle
        • State
        • Keeper
        • Messages
        • Proposals
        • Events
        • Improvements
        • Errors
      • Peggy
        • Definitions
        • Workflow
        • State
        • Messages
        • Slashing
        • EndBlock
        • Events
        • Params
        • Relay Semantics
        • Improvements
        • Errors
      • Permissions
        • Concepts
        • State
        • State Transition
        • Errors
      • TokenFactory
        • Concepts
        • State
        • Messages
        • Events
        • Params
        • Errors
      • WasmX
        • Concepts
        • Data
        • Proposals
        • Messages
        • Params
        • Errors
      • Lanes
      • TxFees
    • Core Modules
      • Auth
      • AuthZ
      • Bank
      • Consensus
      • Crisis
      • Distribution
      • Evidence
      • Feegrant
      • Gov
      • Group
      • Mint
      • NFT
      • Params
      • Slashing
      • Staking
      • Upgrade
      • Circuit
      • Genutil
  • FAQ
  • Glossary
  • References
Powered by GitBook
On this page
Edit on GitHub
Export as PDF
  1. Native Developers
  2. Injective Modules
  3. EVM

Hooks

PreviousABCINextEvents

Last updated 1 minute ago

CtrlK
  • PostTxProcessing
  • Use Case: Call Native ERC20 Module on Injective

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

  3. converting them to native module calls.

To do this, the interface includes a PostTxProcessing hook that registers custom Tx hooks in the EvmKeeper. These Tx hooks are processed after the EVM state transition is finalized and doesn't fail. Note that there are no default hooks implemented in the EVM module.

type EvmHooks interface {
 // 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
}

PostTxProcessing

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.

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(), msg, receipt)
}

It's executed in the same cache context as the EVM transaction, if it returns an error, the whole EVM transaction is reverted, if the hook implementor doesn't want to revert the tx, they can always return nil instead.

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 Injective

Here is an example taken from the Injective erc20 module 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:

event Transfer(address indexed from, address indexed to, uint256 value);

function _transfer(address sender, address recipient, uint256 amount) internal virtual {
  require(sender != address(0), "ERC20: transfer from the zero address");
  require(recipient != address(0), "ERC20: transfer to the zero address");

  _beforeTokenTransfer(sender, recipient, amount);

  _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
  _balances[recipient] = _balances[recipient].add(amount);
  emit Transfer(sender, recipient, amount);
}

The application will register a BankSendHook to the EvmKeeper. It recognizes the ethereum tx Log and converts it to a call to the bank module's SendCoinsFromAccountToAccount method:


const ERC20EventTransfer = "Transfer"

// PostTxProcessing implements EvmHooks.PostTxProcessing
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 receipt.Logs {
  if len(log.Topics) < 3 {
   continue
  }

  eventID := log.Topics[0] // event ID

  event, err := erc20.EventByID(eventID)
  if err != nil {
   // invalid event for ERC20
   continue
  }

  if event.Name != types.ERC20EventTransfer {
   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 {
   h.k.Logger(ctx).Error("failed to unpack transfer event", "error", err.Error())
   continue
  }

  if len(transferEvent) == 0 {
   continue
  }

  tokens, ok := transferEvent[0].(*big.Int)
  // safety check and ignore if amount not positive
  if !ok || tokens == nil || tokens.Sign() != 1 {
   continue
  }

  // check that the contract is a registered token pair
  contractAddr := log.Address

  id := h.k.GetERC20Map(ctx, contractAddr)

  if len(id) == 0 {
   // no token is registered for the caller contract
   continue
  }

  pair, found := h.k.GetTokenPair(ctx, id)
  if !found {
   continue
  }

  // check that conversion for the pair is enabled
  if !pair.Enabled {
   // 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
  to := common.BytesToAddress(log.Topics[2].Bytes())
  if !bytes.Equal(to.Bytes(), types.ModuleAddress.Bytes()) {
   continue
  }

  // check that the event is Burn from the ERC20Burnable interface
  // NOTE: assume that if they are burning the token that has been registered as a pair, they want to mint a Cosmos coin

  // create the corresponding sdk.Coin that is paired with ERC20
  coins := sdk.Coins{{Denom: pair.Denom, Amount: sdk.NewIntFromBigInt(tokens)}}

  // Mint the coin only if ERC20 is external
  switch pair.ContractOwner {
  case types.OWNER_MODULE:
   _, err = h.k.CallEVM(ctx, erc20, types.ModuleAddress, contractAddr, true, "burn", tokens)
  case types.OWNER_EXTERNAL:
   err = h.k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
  default:
   err = types.ErrUndefinedOwner
  }

  if err != nil {
   h.k.Logger(ctx).Debug(
    "failed to process EVM hook for ER20 -> coin conversion",
    "coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
   )
   continue
  }

  // Only need last 20 bytes from log.topics
  from := common.BytesToAddress(log.Topics[1].Bytes())
  recipient := sdk.AccAddress(from.Bytes())

  // transfer the tokens from ModuleAccount to sender address
  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", receipt.TxHash.Hex(), "log-idx", i,
    "coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
   )
   continue
  }
 }

 return nil

Lastly, register the hook in app.go:

app.EvmKeeper = app.EvmKeeper.SetHooks(app.Erc20Keeper)