From d3c3424ce34d4744d5e388e337199e5eccfb3b27 Mon Sep 17 00:00:00 2001 From: mj Date: Wed, 24 Jan 2024 22:41:22 +0800 Subject: [PATCH] Add more CW20 methods to ERC20 Wrapper (#1255) * Initial commit for producer/consumer loadtest client (#1190) * Initial commit for producer/consumer loadtest client * update sei-cosmos * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * rm rounds * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * finalize * debug * debug * debug * debug * debug * debug * debug * debug * bump cosmos * gofmt * debug * debug * Bump version to v3.6.1 (#1222) * Bump version to v3.6.0 * Fix empty branch * Bump cosmos to fix upgrade migration * Update version * Fix changelogs readme * Bump tendermint version * Fix version * Add 3.6.1 upgrade handler * Supress lint * Add migration process readme for SeiDB (#1221) * Add seidb migration steps * Add migration process for SeiDB * Fix * Update seidb_migration.md * Loadtest producer consumer various fixes (#1227) * debug * debug * debug * set default broadcast to sync * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * grpc * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * lint * int * Add TransferFrom method to contract and bindings * Lt client fix (#1260) * Fix account loading in loadtest client * Fix account loading in loadtest client * debug * debug * debug * debug * debug * debug * debug * debug * update * base * base with error * comments * fix bug * old additions * revert merge * pre merge * issues * add tests for transfer and transferFrom * add approve functions * add send_from --------- Co-authored-by: Philip Su Co-authored-by: Yiming Zang <50607998+yzang2019@users.noreply.github.com> Co-authored-by: Mingjun --- example/cosmwasm/cw20/src/contract.rs | 205 +++++++++++++++++++++++++- example/cosmwasm/cw20/src/error.rs | 6 + example/cosmwasm/cw20/src/msg.rs | 39 ++++- example/cosmwasm/cw20/src/querier.rs | 43 +++++- wasmbinding/queries.go | 9 ++ x/evm/client/wasm/bindings/queries.go | 24 +++ x/evm/client/wasm/query.go | 107 ++++++++++++++ x/evm/client/wasm/query_test.go | 32 ++++ 8 files changed, 458 insertions(+), 7 deletions(-) diff --git a/example/cosmwasm/cw20/src/contract.rs b/example/cosmwasm/cw20/src/contract.rs index 1d1a58bfc0..93b8d67327 100644 --- a/example/cosmwasm/cw20/src/contract.rs +++ b/example/cosmwasm/cw20/src/contract.rs @@ -1,9 +1,10 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - DepsMut, Env, MessageInfo, Response, Uint128, + DepsMut, Env, MessageInfo, Response, Uint128, Binary, }; -use crate::msg::{EvmQueryWrapper, EvmMsg, InstantiateMsg, ExecuteMsg}; +use cw20::Cw20ReceiveMsg; +use crate::msg::{cw20receive_into_cosmos_msg, EvmMsg, EvmQueryWrapper, ExecuteMsg, InstantiateMsg}; use crate::querier::EvmQuerier; use crate::error::ContractError; use crate::state::ERC20_ADDRESS; @@ -30,6 +31,27 @@ pub fn execute( ExecuteMsg::Transfer { recipient, amount } => { execute_transfer(deps, env, info, recipient, amount) }, + ExecuteMsg::Burn { amount: _ } => { + execute_burn() + }, + ExecuteMsg::Mint { recipient: _ , amount: _ } => { + execute_mint() + }, + ExecuteMsg::Send { contract, amount, msg } => { + execute_send(deps, env, info, contract, amount, msg) + }, + ExecuteMsg::TransferFrom { owner, recipient, amount } => { + execute_transfer_from(deps, env, info, owner, recipient, amount) + }, + ExecuteMsg::SendFrom { owner, contract, amount, msg} => { + execute_send_from(deps, env, info, owner, contract, amount, msg) + } + ExecuteMsg::IncreaseAllowance { spender, amount, expires: _ } => { + execute_increase_allowance(deps, env, info, spender, amount) + }, + ExecuteMsg::DecreaseAllowance { spender, amount, expires: _ } => { + execute_decrease_allowance(deps, env, info, spender, amount) + } _ => Result::Ok(Response::new()) } } @@ -40,6 +62,158 @@ pub fn execute_transfer( info: MessageInfo, recipient: String, amount: Uint128, +) -> Result, ContractError> { + let mut res = transfer(deps, _env, info, recipient, amount)?; + res = res.add_attribute("action", "transfer"); + Ok(res) +} + +pub fn execute_send( + deps: DepsMut, + _env: Env, + info: MessageInfo, + contract: String, + amount: Uint128, + msg: Binary, +) -> Result, ContractError> { + let mut res = transfer(deps, _env, info.clone(), contract.clone(), amount)?; + let send = Cw20ReceiveMsg { + sender: info.sender.to_string(), + amount: amount.clone(), + msg, + }; + + res = res + .add_message(cw20receive_into_cosmos_msg(contract.clone(), send)?) + .add_attribute("action", "send"); + Ok(res) +} + +// Increase the allowance of spender by amount. +// Expiration does not work here since it is not supported by ERC20. +pub fn execute_increase_allowance( + deps: DepsMut, + _env: Env, + info: MessageInfo, + spender: String, + amount: Uint128, +) -> Result, ContractError> { + deps.api.addr_validate(&spender)?; + + let erc_addr = ERC20_ADDRESS.load(deps.storage)?; + + let querier = EvmQuerier::new(&deps.querier); + + // Query the current allowance for this user + let current_allowance = querier.erc20_allowance(erc_addr.clone(), info.sender.clone().into_string(), spender.clone())?.allowance; + + // Set the new allowance as the sum of the current allowance and amount specified + let new_allowance = current_allowance + amount; + + // Send the message to approve the new amount + let payload = querier.erc20_approve_payload(spender.clone(), new_allowance)?; + let msg = EvmMsg::DelegateCallEvm { to: erc_addr, data: payload.encoded_payload }; + + let res = Response::new() + .add_attribute("action", "increase_allowance") + .add_attribute("spender", spender) + .add_attribute("amount", amount) + .add_attribute("new_allowance", new_allowance) + .add_attribute("by", info.sender) + .add_message(msg); + + Ok(res) +} + +// Decrease the allowance of spender by amount. +// Expiration does not work here since it is not supported by ERC20. +pub fn execute_decrease_allowance( + deps: DepsMut, + _env: Env, + info: MessageInfo, + spender: String, + amount: Uint128, +) -> Result, ContractError> { + deps.api.addr_validate(&spender)?; + + let erc_addr = ERC20_ADDRESS.load(deps.storage)?; + + // Query the current allowance for this spender + let querier = EvmQuerier::new(&deps.querier); + let current_allowance = querier.erc20_allowance(erc_addr.clone(), info.sender.clone().into_string(), spender.clone())?.allowance; + + // If the new allowance after deduction is negative, set allowance to 0. + let new_allowance = match current_allowance.checked_sub(amount) + { + Ok(new_amount) => new_amount, + Err(_) => Uint128::MIN, + }; + + // Send the message to approve the new amount. + let payload = querier.erc20_approve_payload(spender.clone(), new_allowance)?; + let msg = EvmMsg::DelegateCallEvm { to: erc_addr, data: payload.encoded_payload }; + + let res = Response::new() + .add_attribute("action", "decrease_allowance") + .add_attribute("spender", spender) + .add_attribute("amount", amount) + .add_attribute("new_allowance", new_allowance) + .add_attribute("by", info.sender) + .add_message(msg); + + Ok(res) +} + +pub fn execute_transfer_from( + deps: DepsMut, + env: Env, + info: MessageInfo, + owner: String, + recipient: String, + amount: Uint128, +) -> Result, ContractError> { + let mut res = transfer_from(deps, env, info, owner, recipient, amount)?; + res = res.add_attribute("action", "transfer_from"); + + Ok(res) +} + +pub fn execute_send_from( + deps: DepsMut, + env: Env, + info: MessageInfo, + owner: String, + contract: String, + amount: Uint128, + msg: Binary, +) -> Result, ContractError> { + let mut res = transfer_from(deps, env, info.clone(), owner, contract.clone(), amount)?; + let send = Cw20ReceiveMsg { + sender: info.sender.to_string(), + amount: amount.clone(), + msg, + }; + + res = res + .add_message(cw20receive_into_cosmos_msg(contract.clone(), send)?) + .add_attribute("action", "send_from"); + Ok(res) +} + +pub fn execute_burn() -> Result, ContractError> { + Err(ContractError::BurnNotSupported {}) +} + +pub fn execute_mint() -> Result, ContractError> { + Err(ContractError::MintNotSupported {}) +} + +fn transfer( + deps: DepsMut, + _env: Env, + info: MessageInfo, + recipient: String, + amount: Uint128, ) -> Result, ContractError> { deps.api.addr_validate(&recipient)?; @@ -49,11 +223,36 @@ pub fn execute_transfer( let payload = querier.erc20_transfer_payload(recipient.clone(), amount)?; let msg = EvmMsg::DelegateCallEvm { to: erc_addr, data: payload.encoded_payload }; let res = Response::new() - .add_attribute("action", "transfer") .add_attribute("from", info.sender) .add_attribute("to", recipient) .add_attribute("amount", amount) .add_message(msg); + Ok(res) +} + +pub fn transfer_from( + deps: DepsMut, + _env: Env, + info: MessageInfo, + owner: String, + recipient: String, + amount: Uint128, +) -> Result, ContractError> { + deps.api.addr_validate(&owner)?; + deps.api.addr_validate(&recipient)?; + + let erc_addr = ERC20_ADDRESS.load(deps.storage)?; + + let querier = EvmQuerier::new(&deps.querier); + let payload = querier.erc20_transfer_from_payload(owner.clone(), recipient.clone(), amount)?; + let msg = EvmMsg::DelegateCallEvm { to: erc_addr, data: payload.encoded_payload }; + let res = Response::new() + .add_attribute("from", owner) + .add_attribute("to", recipient) + .add_attribute("by", info.sender) + .add_attribute("amount", amount) + .add_message(msg); + Ok(res) } \ No newline at end of file diff --git a/example/cosmwasm/cw20/src/error.rs b/example/cosmwasm/cw20/src/error.rs index 6858cb12b7..3a567de993 100644 --- a/example/cosmwasm/cw20/src/error.rs +++ b/example/cosmwasm/cw20/src/error.rs @@ -5,4 +5,10 @@ use thiserror::Error; pub enum ContractError { #[error("{0}")] Std(#[from] StdError), + + #[error("ERC20 does not support Burn in it's base spec")] + BurnNotSupported {}, + + #[error("ERC20 does not support Mint in it's base spec")] + MintNotSupported {}, } \ No newline at end of file diff --git a/example/cosmwasm/cw20/src/msg.rs b/example/cosmwasm/cw20/src/msg.rs index 97d2bff713..8571e74dc7 100644 --- a/example/cosmwasm/cw20/src/msg.rs +++ b/example/cosmwasm/cw20/src/msg.rs @@ -1,4 +1,5 @@ -use cosmwasm_std::{Uint128, CosmosMsg, CustomMsg, CustomQuery}; +use cosmwasm_std::{CosmosMsg, CustomMsg, CustomQuery, StdResult, Uint128, WasmMsg}; +use cw20::Cw20ReceiveMsg; use schemars::JsonSchema; use cosmwasm_schema::cw_serde; use serde::{Deserialize, Serialize}; @@ -36,13 +37,32 @@ pub enum EvmQuery { recipient: String, amount: Uint128, }, + Erc20TransferFromPayload { + owner: String, + recipient: String, + amount: Uint128, + }, + Erc20ApprovePayload { + spender: String, + amount: Uint128, + }, + Erc20Allowance { + contract_address: String, + owner: String, + spender: String, + }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct Erc20TransferPayloadResponse { +pub struct ErcPayloadResponse { pub encoded_payload: String, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct Erc20AllowanceResponse { + pub allowance: Uint128, +} + // implement custom query impl CustomMsg for EvmMsg {} @@ -60,4 +80,19 @@ pub enum EvmMsg { to: String, data: String, // base64 encoded }, +} + +/// Helper to convert a Cw20ReceiveMsg into an EvmMsg +pub fn cw20receive_into_cosmos_msg, C>(contract_addr: T, message: Cw20ReceiveMsg) -> StdResult> +where + C: Clone + std::fmt::Debug + PartialEq + JsonSchema, +{ + let msg = message.into_binary()?; + let execute = WasmMsg::Execute { + contract_addr: contract_addr.into(), + msg, + funds: vec![], + }; + + Ok(execute.into()) } \ No newline at end of file diff --git a/example/cosmwasm/cw20/src/querier.rs b/example/cosmwasm/cw20/src/querier.rs index f0a3484991..ef2c9dfd73 100644 --- a/example/cosmwasm/cw20/src/querier.rs +++ b/example/cosmwasm/cw20/src/querier.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{QuerierWrapper, StdResult, Uint128}; -use crate::msg::{Route, EvmQuery, EvmQueryWrapper, Erc20TransferPayloadResponse}; +use crate::msg::{Erc20AllowanceResponse, ErcPayloadResponse, EvmQuery, EvmQueryWrapper, Route}; pub struct EvmQuerier<'a> { querier: &'a QuerierWrapper<'a, EvmQueryWrapper>, @@ -12,7 +12,7 @@ impl<'a> EvmQuerier<'a> { } // returns base64-encoded bytes - pub fn erc20_transfer_payload(&self, recipient: String, amount: Uint128) -> StdResult { + pub fn erc20_transfer_payload(&self, recipient: String, amount: Uint128) -> StdResult { let request = EvmQueryWrapper { route: Route::Evm, query_data: EvmQuery::Erc20TransferPayload { @@ -23,4 +23,43 @@ impl<'a> EvmQuerier<'a> { self.querier.query(&request) } + + // returns base64-encoded bytes + pub fn erc20_transfer_from_payload(&self, owner: String, recipient: String, amount: Uint128) -> StdResult { + let request = EvmQueryWrapper { + route: Route::Evm, + query_data: EvmQuery::Erc20TransferFromPayload { + owner, recipient, amount, + }, + } + .into(); + + self.querier.query(&request) + } + + // returns base64-encoded bytes + pub fn erc20_approve_payload(&self, spender: String, amount: Uint128) -> StdResult { + let request = EvmQueryWrapper { + route: Route::Evm, + query_data: EvmQuery::Erc20ApprovePayload { + spender, amount, + }, + } + .into(); + + self.querier.query(&request) + } + + // returns base64-encoded bytes + pub fn erc20_allowance(&self, contract_address: String, owner: String, spender: String) -> StdResult { + let request = EvmQueryWrapper { + route: Route::Evm, + query_data: EvmQuery::Erc20Allowance { + contract_address, owner, spender, + }, + } + .into(); + + self.querier.query(&request) + } } \ No newline at end of file diff --git a/wasmbinding/queries.go b/wasmbinding/queries.go index 04ee18e4ba..24bfd85754 100644 --- a/wasmbinding/queries.go +++ b/wasmbinding/queries.go @@ -206,6 +206,15 @@ func (qp QueryPlugin) HandleEVMQuery(ctx sdk.Context, queryData json.RawMessage) case parsedQuery.ERC20TransferPayload != nil: c := parsedQuery.ERC20TransferPayload return qp.evmHandler.HandleERC20TransferPayload(ctx, c.Recipient, c.Amount) + case parsedQuery.ERC20TransferFromPayload != nil: + c := parsedQuery.ERC20TransferFromPayload + return qp.evmHandler.HandleERC20TransferFromPayload(ctx, c.Owner, c.Recipient, c.Amount) + case parsedQuery.ERC20ApprovePayload != nil: + c := parsedQuery.ERC20ApprovePayload + return qp.evmHandler.HandleERC20ApprovePayload(ctx, c.Spender, c.Amount) + case parsedQuery.ERC20Allowance != nil: + c := parsedQuery.ERC20Allowance + return qp.evmHandler.HandleERC20Allowance(ctx, c.ContractAddress, c.Owner, c.Spender) case parsedQuery.ERC721Owner != nil: c := parsedQuery.ERC721Owner return qp.evmHandler.HandleERC721Owner(ctx, c.Caller, c.ContractAddress, c.TokenID) diff --git a/x/evm/client/wasm/bindings/queries.go b/x/evm/client/wasm/bindings/queries.go index 8ce54982a2..f1aab19b95 100644 --- a/x/evm/client/wasm/bindings/queries.go +++ b/x/evm/client/wasm/bindings/queries.go @@ -7,6 +7,9 @@ import ( type SeiEVMQuery struct { StaticCall *StaticCallRequest `json:"static_call,omitempty"` ERC20TransferPayload *ERC20TransferPayloadRequest `json:"erc20_transfer_payload,omitempty"` + ERC20TransferFromPayload *ERC20TransferFromPayloadRequest `json:"erc20_transfer_from_payload,omitempty"` + ERC20ApprovePayload *ERC20ApprovePayloadRequest `json:"erc20_approve_payload,omitempty"` + ERC20Allowance *ERC20AllowanceRequest `json:"erc20_allowance,omitempty"` ERC721Owner *ERC721OwnerRequest `json:"erc721_owner,omitempty"` ERC721TransferPayload *ERC721TransferPayloadRequest `json:"erc721_transfer_payload,omitempty"` ERC721ApprovePayload *ERC721ApprovePayloadRequest `json:"erc721_approve_payload,omitempty"` @@ -24,6 +27,23 @@ type ERC20TransferPayloadRequest struct { Amount *sdk.Int `json:"amount"` } +type ERC20TransferFromPayloadRequest struct { + Owner string `json:"owner"` + Recipient string `json:"recipient"` + Amount *sdk.Int `json:"amount"` +} + +type ERC20ApprovePayloadRequest struct { + Spender string `json:"spender"` + Amount *sdk.Int `json:"token_id"` +} + +type ERC20AllowanceRequest struct { + ContractAddress string `json:"contract_address"` + Owner string `json:"owner"` + Spender string `json:"spender"` +} + type ERC721OwnerRequest struct { Caller string `json:"caller"` ContractAddress string `json:"contract_address"` @@ -50,6 +70,10 @@ type ERCPayloadResponse struct { EncodedPayload string `json:"encoded_payload"` } +type ERC20AllowanceResponse struct { + Allowance *sdk.Int `json:"allowance"` +} + type ERC721OwnerResponse struct { Owner string `json:"owner"` } diff --git a/x/evm/client/wasm/query.go b/x/evm/client/wasm/query.go index d1bbc5d3bb..ead924074c 100644 --- a/x/evm/client/wasm/query.go +++ b/x/evm/client/wasm/query.go @@ -177,3 +177,110 @@ func (h *EVMQueryHandler) HandleERC721SetApprovalAllPayload(ctx sdk.Context, to res := bindings.ERCPayloadResponse{EncodedPayload: base64.StdEncoding.EncodeToString(bz)} return json.Marshal(res) } + +func (h *EVMQueryHandler) HandleERC20TransferFromPayload(ctx sdk.Context, owner string, recipient string, amount *sdk.Int) ([]byte, error) { + ownerAddr, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return nil, err + } + + recipientAddr, err := sdk.AccAddressFromBech32(recipient) + if err != nil { + return nil, err + } + + abi, err := native.NativeMetaData.GetAbi() + if err != nil { + return nil, err + } + ownerEvmAddr, found := h.k.GetEVMAddress(ctx, ownerAddr) + if !found { + ownerEvmAddr = common.Address{} + ownerEvmAddr.SetBytes(ownerAddr) + } + recipientEvmAddr, found := h.k.GetEVMAddress(ctx, recipientAddr) + if !found { + recipientEvmAddr = common.Address{} + recipientEvmAddr.SetBytes(recipientAddr) + } + bz, err := abi.Pack("transferFrom", ownerEvmAddr, recipientEvmAddr, amount.BigInt()) + if err != nil { + return nil, err + } + res := bindings.ERCPayloadResponse{EncodedPayload: base64.StdEncoding.EncodeToString(bz)} + return json.Marshal(res) +} + +func (h *EVMQueryHandler) HandleERC20ApprovePayload(ctx sdk.Context, spender string, amount *sdk.Int) ([]byte, error) { + spenderAddr, err := sdk.AccAddressFromBech32(spender) + if err != nil { + return nil, err + } + + abi, err := native.NativeMetaData.GetAbi() + if err != nil { + return nil, err + } + spenderEvmAddr, found := h.k.GetEVMAddress(ctx, spenderAddr) + if !found { + spenderEvmAddr = common.Address{} + spenderEvmAddr.SetBytes(spenderAddr) + } + + bz, err := abi.Pack("approve", spenderEvmAddr, amount.BigInt()) + if err != nil { + return nil, err + } + res := bindings.ERCPayloadResponse{EncodedPayload: base64.StdEncoding.EncodeToString(bz)} + return json.Marshal(res) +} + +func (h *EVMQueryHandler) HandleERC20Allowance(ctx sdk.Context, contractAddress string, owner string, spender string) ([]byte, error) { + // Get the evm address of the owner + ownerAddr, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return nil, err + } + ownerEvmAddr, found := h.k.GetEVMAddress(ctx, ownerAddr) + if !found { + ownerEvmAddr = common.Address{} + ownerEvmAddr.SetBytes(ownerAddr) + } + + // Get the evm address of spender + spenderAddr, err := sdk.AccAddressFromBech32(spender) + if err != nil { + return nil, err + } + spenderEvmAddr, found := h.k.GetEVMAddress(ctx, spenderAddr) + if !found { + spenderEvmAddr = common.Address{} + spenderEvmAddr.SetBytes(spenderAddr) + } + + // Fetch the contract ABI + contract := common.HexToAddress(contractAddress) + abi, err := native.NativeMetaData.GetAbi() + if err != nil { + return nil, err + } + + // Make the query to allowance(owner, spender) + bz, err := abi.Pack("allowance", ownerEvmAddr, spenderEvmAddr) + if err != nil { + return nil, err + } + res, err := h.k.StaticCallEVM(ctx, ownerAddr, &contract, bz) + if err != nil { + return nil, err + } + + // Parse the response (Should be of type uint256 if successful) + typed, err := abi.Unpack("allowance", res) + if err != nil { + return nil, err + } + allowance := typed[0].(sdk.Int) + response := bindings.ERC20AllowanceResponse{Allowance: &allowance} + return json.Marshal(response) +} diff --git a/x/evm/client/wasm/query_test.go b/x/evm/client/wasm/query_test.go index 57a5a58d1f..13cfef6e91 100644 --- a/x/evm/client/wasm/query_test.go +++ b/x/evm/client/wasm/query_test.go @@ -3,6 +3,7 @@ package wasm_test import ( "testing" + "github.com/cosmos/cosmos-sdk/types" testkeeper "github.com/sei-protocol/sei-chain/testutil/keeper" "github.com/sei-protocol/sei-chain/x/evm/client/wasm" "github.com/stretchr/testify/require" @@ -35,3 +36,34 @@ func TestERC721ApproveAllPayload(t *testing.T) { require.Nil(t, err) require.NotEmpty(t, res) } + +func TestERC20TransferPayload(t *testing.T) { + k, ctx := testkeeper.MockEVMKeeper() + addr1, _ := testkeeper.MockAddressPair() + h := wasm.NewEVMQueryHandler(k) + value := types.NewInt(500) + res, err := h.HandleERC20TransferPayload(ctx, addr1.String(), &value) + require.Nil(t, err) + require.NotEmpty(t, res) +} + +func TestERC20TransferFromPayload(t *testing.T) { + k, ctx := testkeeper.MockEVMKeeper() + addr1, _ := testkeeper.MockAddressPair() + addr2, _ := testkeeper.MockAddressPair() + h := wasm.NewEVMQueryHandler(k) + value := types.NewInt(500) + res, err := h.HandleERC20TransferFromPayload(ctx, addr1.String(), addr2.String(), &value) + require.Nil(t, err) + require.NotEmpty(t, res) +} + +func TestERC20ApprovePayload(t *testing.T) { + k, ctx := testkeeper.MockEVMKeeper() + addr1, _ := testkeeper.MockAddressPair() + h := wasm.NewEVMQueryHandler(k) + value := types.NewInt(500) + res, err := h.HandleERC20ApprovePayload(ctx, addr1.String(), &value) + require.Nil(t, err) + require.NotEmpty(t, res) +}