From 1a03ed7e30ef5023ff6c6ebe76a62d7ae5725f39 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 12 Aug 2024 11:43:36 +0700 Subject: [PATCH 1/2] feat: withdraw stuck asset --- Cargo.lock | 1 + .../adapters/ibc/orai-ibc-wasm/Cargo.toml | 1 + .../ibc/orai-ibc-wasm/src/contract.rs | 46 +++++++++++++++++-- .../adapters/ibc/orai-ibc-wasm/src/error.rs | 4 ++ .../adapters/ibc/orai-ibc-wasm/src/state.rs | 2 + packages/skip/src/ibc_wasm.rs | 10 +++- 6 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b619248e..edcfa9bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1629,6 +1629,7 @@ dependencies = [ "cosmos-sdk-proto 0.19.0", "cosmwasm-schema", "cosmwasm-std", + "cw-controllers", "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw2 1.1.2", diff --git a/contracts/adapters/ibc/orai-ibc-wasm/Cargo.toml b/contracts/adapters/ibc/orai-ibc-wasm/Cargo.toml index 7c60d13f..8fc75089 100644 --- a/contracts/adapters/ibc/orai-ibc-wasm/Cargo.toml +++ b/contracts/adapters/ibc/orai-ibc-wasm/Cargo.toml @@ -29,6 +29,7 @@ skip = { workspace = true } thiserror = { workspace = true } cw20-ics20-msg = { workspace = true } cw-utils = { workspace = true } +cw-controllers = { workspace = true } [dev-dependencies] cosmos-sdk-proto = { workspace = true } diff --git a/contracts/adapters/ibc/orai-ibc-wasm/src/contract.rs b/contracts/adapters/ibc/orai-ibc-wasm/src/contract.rs index 7a808394..65a91fb3 100644 --- a/contracts/adapters/ibc/orai-ibc-wasm/src/contract.rs +++ b/contracts/adapters/ibc/orai-ibc-wasm/src/contract.rs @@ -1,9 +1,10 @@ use crate::{ error::{ContractError, ContractResult}, - state::{ENTRY_POINT_CONTRACT_ADDRESS, IBC_WASM_CONTRACT_ADDRESS}, + state::{ENTRY_POINT_CONTRACT_ADDRESS, IBC_WASM_CONTRACT_ADDRESS, OWNER}, }; use cosmwasm_std::{ - entry_point, from_json, to_json_binary, DepsMut, Env, MessageInfo, Response, WasmMsg, + entry_point, from_json, to_json_binary, Addr, DepsMut, Empty, Env, MessageInfo, Response, + WasmMsg, }; use cw2::set_contract_version; @@ -31,6 +32,9 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> ContractResult ContractResult { // Set contract version @@ -72,6 +76,7 @@ pub fn instantiate( // Store the entry point contract address ENTRY_POINT_CONTRACT_ADDRESS.save(deps.storage, &checked_entry_point_contract_address)?; IBC_WASM_CONTRACT_ADDRESS.save(deps.storage, &checked_ibc_wasm_contract_address)?; + OWNER.set(deps, Some(info.sender))?; Ok(Response::new() .add_attribute("action", "instantiate") @@ -142,9 +147,44 @@ pub fn execute( } execute_ibc_wasm_transfer(deps, env, info, ibc_wasm_info, coin) } + ExecuteMsg::UpdateOwner { new_owner } => execute_update_owner(deps, info, new_owner), + ExecuteMsg::WithdrawAsset { coin, receiver } => { + execute_withdraw_asset(deps, info, coin, receiver) + } } } +// update new owner +fn execute_update_owner( + deps: DepsMut, + info: MessageInfo, + new_owner: Addr, +) -> ContractResult { + OWNER.execute_update_admin::(deps, info, Some(new_owner.clone()))?; + + Ok(Response::new().add_attributes(vec![ + ("action", "update_owner"), + ("new_owner", new_owner.as_str()), + ])) +} + +// withdraw stuck coin and transfer back to user +fn execute_withdraw_asset( + deps: DepsMut, + info: MessageInfo, + coin: Asset, + receiver: Option, +) -> ContractResult { + OWNER.assert_admin(deps.as_ref(), &info.sender)?; + let receiver = receiver.unwrap_or(info.sender); + + let msg = coin.transfer(receiver.as_str()); + + Ok(Response::new() + .add_attributes(vec![("action", "withdraw_asset")]) + .add_message(msg)) +} + // Converts the given info and coin into a ibc wasm transfer message, fn execute_ibc_wasm_transfer( deps: DepsMut, diff --git a/contracts/adapters/ibc/orai-ibc-wasm/src/error.rs b/contracts/adapters/ibc/orai-ibc-wasm/src/error.rs index b0b32358..1a18de49 100644 --- a/contracts/adapters/ibc/orai-ibc-wasm/src/error.rs +++ b/contracts/adapters/ibc/orai-ibc-wasm/src/error.rs @@ -1,4 +1,5 @@ use cosmwasm_std::{OverflowError, StdError}; +use cw_controllers::AdminError; use cw_utils::PaymentError; use skip::error::SkipError; use thiserror::Error; @@ -19,6 +20,9 @@ pub enum ContractError { #[error("{0}")] Payment(#[from] PaymentError), + #[error("{0}")] + AdminError(#[from] AdminError), + #[error("Unauthorized")] Unauthorized, diff --git a/contracts/adapters/ibc/orai-ibc-wasm/src/state.rs b/contracts/adapters/ibc/orai-ibc-wasm/src/state.rs index 5d6cefce..181032af 100644 --- a/contracts/adapters/ibc/orai-ibc-wasm/src/state.rs +++ b/contracts/adapters/ibc/orai-ibc-wasm/src/state.rs @@ -1,5 +1,7 @@ use cosmwasm_std::Addr; +use cw_controllers::Admin; use cw_storage_plus::Item; pub const ENTRY_POINT_CONTRACT_ADDRESS: Item = Item::new("entry_point_contract_address"); pub const IBC_WASM_CONTRACT_ADDRESS: Item = Item::new("checked_ibc_wasm_contract_address"); +pub const OWNER: Admin = Admin::new("owner"); diff --git a/packages/skip/src/ibc_wasm.rs b/packages/skip/src/ibc_wasm.rs index 6002d684..b009be92 100644 --- a/packages/skip/src/ibc_wasm.rs +++ b/packages/skip/src/ibc_wasm.rs @@ -3,7 +3,7 @@ use crate::{asset::Asset, error::SkipError}; use std::convert::From; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Coin, Coins, StdError}; +use cosmwasm_std::{Addr, Coin, Coins, StdError}; use cw20::Cw20ReceiveMsg; use cw20_ics20_msg::msg::TransferBackMsg; @@ -16,6 +16,7 @@ use cw20_ics20_msg::msg::TransferBackMsg; pub struct MigrateMsg { pub entry_point_contract_address: String, pub ibc_wasm_contract_address: String, + pub new_owner: Option, } /////////////////// /// INSTANTIATE /// @@ -40,6 +41,13 @@ pub enum ExecuteMsg { coin: Asset, }, Receive(Cw20ReceiveMsg), + UpdateOwner { + new_owner: Addr, + }, + WithdrawAsset { + coin: Asset, + receiver: Option, + }, } #[cw_serde] pub enum Cw20HookMsg { From 6e0658d208287278da146541c112e20bb00a0cf3 Mon Sep 17 00:00:00 2001 From: trung2891 Date: Mon, 12 Aug 2024 11:52:36 +0700 Subject: [PATCH 2/2] chore: add test withdraw stuck asset --- .../tests/test_execute_ibc_transfer.rs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/contracts/adapters/ibc/orai-ibc-wasm/tests/test_execute_ibc_transfer.rs b/contracts/adapters/ibc/orai-ibc-wasm/tests/test_execute_ibc_transfer.rs index 8d73bbad..c4e48b8a 100644 --- a/contracts/adapters/ibc/orai-ibc-wasm/tests/test_execute_ibc_transfer.rs +++ b/contracts/adapters/ibc/orai-ibc-wasm/tests/test_execute_ibc_transfer.rs @@ -1,13 +1,14 @@ use cosmwasm_std::{ - testing::{mock_dependencies, mock_env, mock_info}, to_json_binary, Addr, Coin, SubMsg, Uint128, WasmMsg + testing::{mock_dependencies, mock_env, mock_info}, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, SubMsg, Uint128, WasmMsg }; use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg}; use cw20_ics20_msg::msg::TransferBackMsg; +use cw_controllers::AdminError; use skip::{asset::Asset, ibc_wasm::ExecuteMsg}; use skip_api_ibc_adapter_orai_ibc_wasm::{ - error::ContractResult, state::{ENTRY_POINT_CONTRACT_ADDRESS, IBC_WASM_CONTRACT_ADDRESS}, + contract::execute, error::{ContractError, ContractResult}, state::{ENTRY_POINT_CONTRACT_ADDRESS, IBC_WASM_CONTRACT_ADDRESS, OWNER} }; use test_case::test_case; @@ -145,3 +146,18 @@ fn test_execute_ibc_transfer(params: Params) -> ContractResult<()> { Ok(()) } + + +#[test] +fn test_withdraw_stuck_asset() { + let mut deps = mock_dependencies(); + OWNER.set(deps.as_mut(), Some(Addr::unchecked("admin"))).unwrap(); + + // case 1: unauthorized + let err = execute(deps.as_mut(), mock_env(), mock_info("addr000", &[]), ExecuteMsg::WithdrawAsset { coin: Asset::Native(Coin { denom: "orai".to_string(), amount: Uint128::new(1000000) }), receiver: Some(Addr::unchecked("receiver")) }).unwrap_err(); + assert_eq!(err, ContractError::AdminError(AdminError::NotAdmin { })); + + // case 2: success + let res = execute(deps.as_mut(), mock_env(), mock_info("admin", &[]), ExecuteMsg::WithdrawAsset { coin: Asset::Native(Coin { denom: "orai".to_string(), amount: Uint128::new(1000000) }), receiver: Some(Addr::unchecked("receiver")) }).unwrap(); + assert_eq!(res.messages, vec![SubMsg::new(CosmosMsg::Bank(BankMsg::Send { to_address: "receiver".to_string(), amount: vec![Coin{denom: "orai".to_string(), amount: Uint128::new(1000000)}] }))]); +} \ No newline at end of file