Skip to content

Commit

Permalink
Add more CW20 methods to ERC20 Wrapper (#1255)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Co-authored-by: Yiming Zang <[email protected]>
Co-authored-by: Mingjun <[email protected]>
  • Loading branch information
4 people authored and udpatil committed Apr 17, 2024
1 parent eb261fb commit d3c3424
Show file tree
Hide file tree
Showing 8 changed files with 458 additions and 7 deletions.
205 changes: 202 additions & 3 deletions example/cosmwasm/cw20/src/contract.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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())
}
}
Expand All @@ -40,6 +62,158 @@ pub fn execute_transfer(
info: MessageInfo,
recipient: String,
amount: Uint128,
) -> Result<Response<EvmMsg>, ContractError> {
let mut res = transfer(deps, _env, info, recipient, amount)?;
res = res.add_attribute("action", "transfer");
Ok(res)
}

pub fn execute_send(
deps: DepsMut<EvmQueryWrapper>,
_env: Env,
info: MessageInfo,
contract: String,
amount: Uint128,
msg: Binary,
) -> Result<Response<EvmMsg>, 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<EvmQueryWrapper>,
_env: Env,
info: MessageInfo,
spender: String,
amount: Uint128,
) -> Result<Response<EvmMsg>, 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<EvmQueryWrapper>,
_env: Env,
info: MessageInfo,
spender: String,
amount: Uint128,
) -> Result<Response<EvmMsg>, 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<EvmQueryWrapper>,
env: Env,
info: MessageInfo,
owner: String,
recipient: String,
amount: Uint128,
) -> Result<Response<EvmMsg>, 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<EvmQueryWrapper>,
env: Env,
info: MessageInfo,
owner: String,
contract: String,
amount: Uint128,
msg: Binary,
) -> Result<Response<EvmMsg>, 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<Response<EvmMsg>, ContractError> {
Err(ContractError::BurnNotSupported {})
}

pub fn execute_mint() -> Result<Response<EvmMsg>, ContractError> {
Err(ContractError::MintNotSupported {})
}

fn transfer(
deps: DepsMut<EvmQueryWrapper>,
_env: Env,
info: MessageInfo,
recipient: String,
amount: Uint128,
) -> Result<Response<EvmMsg>, ContractError> {
deps.api.addr_validate(&recipient)?;

Expand All @@ -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<EvmQueryWrapper>,
_env: Env,
info: MessageInfo,
owner: String,
recipient: String,
amount: Uint128,
) -> Result<Response<EvmMsg>, 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)
}
6 changes: 6 additions & 0 deletions example/cosmwasm/cw20/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {},
}
39 changes: 37 additions & 2 deletions example/cosmwasm/cw20/src/msg.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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 {}

Expand All @@ -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<T: Into<String>, C>(contract_addr: T, message: Cw20ReceiveMsg) -> StdResult<CosmosMsg<C>>
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())
}
43 changes: 41 additions & 2 deletions example/cosmwasm/cw20/src/querier.rs
Original file line number Diff line number Diff line change
@@ -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>,
Expand All @@ -12,7 +12,7 @@ impl<'a> EvmQuerier<'a> {
}

// returns base64-encoded bytes
pub fn erc20_transfer_payload(&self, recipient: String, amount: Uint128) -> StdResult<Erc20TransferPayloadResponse> {
pub fn erc20_transfer_payload(&self, recipient: String, amount: Uint128) -> StdResult<ErcPayloadResponse> {
let request = EvmQueryWrapper {
route: Route::Evm,
query_data: EvmQuery::Erc20TransferPayload {
Expand All @@ -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<ErcPayloadResponse> {
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<ErcPayloadResponse> {
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<Erc20AllowanceResponse> {
let request = EvmQueryWrapper {
route: Route::Evm,
query_data: EvmQuery::Erc20Allowance {
contract_address, owner, spender,
},
}
.into();

self.querier.query(&request)
}
}
9 changes: 9 additions & 0 deletions wasmbinding/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit d3c3424

Please sign in to comment.