Skip to content

Commit

Permalink
fixed unsafe force withdraw to support native and cw20 tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso committed Nov 1, 2024
1 parent 78226e5 commit 321de22
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,25 @@
"unsafe_force_withdraw": {
"type": "object",
"required": [
"amount"
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Coin"
"description": "amount to withdraw",
"allOf": [
{
"$ref": "#/definitions/Uint128"
}
]
},
"denom": {
"description": "denom to withdraw",
"allOf": [
{
"$ref": "#/definitions/UncheckedDenom"
}
]
}
},
"additionalProperties": false
Expand Down Expand Up @@ -321,21 +335,6 @@
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>. See also <https://github.com/CosmWasm/cosmwasm/blob/main/docs/MESSAGE_TYPES.md>.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"CreateMsg": {
"type": "object",
"required": [
Expand Down
28 changes: 17 additions & 11 deletions contracts/distribution/dao-rewards-distributor/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{
ensure, from_json, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Deps, DepsMut, Env,
MessageInfo, Order, Response, StdError, StdResult, Uint128, Uint256,
ensure, from_json, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Order,
Response, StdError, StdResult, Uint128, Uint256,
};
use cw2::{get_contract_version, set_contract_version};
use cw20::{Cw20ReceiveMsg, Denom};
use cw20::{Cw20ReceiveMsg, Denom, UncheckedDenom};
use cw_storage_plus::Bound;
use cw_utils::{must_pay, nonpayable, Duration, Expiration};
use dao_interface::voting::InfoResponse;
Expand Down Expand Up @@ -91,8 +91,8 @@ pub fn execute(
ExecuteMsg::FundLatest {} => execute_fund_latest_native(deps, env, info),
ExecuteMsg::Claim { id } => execute_claim(deps, env, info, id),
ExecuteMsg::Withdraw { id } => execute_withdraw(deps, info, env, id),
ExecuteMsg::UnsafeForceWithdraw { amount } => {
execute_unsafe_force_withdraw(deps, info, amount)
ExecuteMsg::UnsafeForceWithdraw { amount, denom } => {
execute_unsafe_force_withdraw(deps, info, amount, denom)
}
}
}
Expand Down Expand Up @@ -600,22 +600,28 @@ fn execute_update_owner(
fn execute_unsafe_force_withdraw(
deps: DepsMut,
info: MessageInfo,
amount: Coin,
amount: Uint128,
denom: UncheckedDenom,
) -> Result<Response, ContractError> {
nonpayable(&info)?;

// only the owner can initiate a force withdraw
cw_ownable::assert_owner(deps.storage, &info.sender)?;

let send = CosmosMsg::Bank(BankMsg::Send {
to_address: info.sender.to_string(),
amount: vec![amount.clone()],
});
let checked_denom = denom.into_checked(deps.as_ref())?;

let denom_str = match &checked_denom {
Denom::Native(denom) => denom.to_string(),
Denom::Cw20(address) => address.to_string(),
};

let send = get_transfer_msg(info.sender, amount, checked_denom)?;

Ok(Response::new()
.add_message(send)
.add_attribute("action", "unsafe_force_withdraw")
.add_attribute("amount", amount.to_string()))
.add_attribute("amount", amount.to_string())
.add_attribute("denom", denom_str))
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand Down
9 changes: 7 additions & 2 deletions contracts/distribution/dao-rewards-distributor/src/msg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::{Coin, Uint128};
use cosmwasm_std::Uint128;
use cw20::{Cw20ReceiveMsg, Denom, UncheckedDenom};
use cw4::MemberChangedHookMsg;
use cw_ownable::cw_ownable_execute;
Expand Down Expand Up @@ -62,7 +62,12 @@ pub enum ExecuteMsg {
Withdraw { id: u64 },
/// forcibly withdraw funds from the contract. this is unsafe and should
/// only be used to recover funds that are stuck in the contract.
UnsafeForceWithdraw { amount: Coin },
UnsafeForceWithdraw {
/// amount to withdraw
amount: Uint128,
/// denom to withdraw
denom: UncheckedDenom,
},
}

#[cw_serde]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -975,8 +975,11 @@ impl Suite {
.unwrap();
}

pub fn unsafe_force_withdraw(&mut self, amount: Coin) {
let msg = ExecuteMsg::UnsafeForceWithdraw { amount };
pub fn unsafe_force_withdraw(&mut self, amount: impl Into<Uint128>, denom: UncheckedDenom) {
let msg = ExecuteMsg::UnsafeForceWithdraw {
amount: amount.into(),
denom,
};
self.base
.app
.execute_contract(
Expand All @@ -988,8 +991,15 @@ impl Suite {
.unwrap();
}

pub fn unsafe_force_withdraw_unauthorized(&mut self, amount: Coin) -> ContractError {
let msg = ExecuteMsg::UnsafeForceWithdraw { amount };
pub fn unsafe_force_withdraw_unauthorized(
&mut self,
amount: impl Into<Uint128>,
denom: UncheckedDenom,
) -> ContractError {
let msg = ExecuteMsg::UnsafeForceWithdraw {
amount: amount.into(),
denom,
};
self.base
.app
.execute_contract(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2997,7 +2997,10 @@ fn test_unsafe_force_withdraw() {
suite.get_balance_native(suite.distribution_contract.clone(), &suite.reward_denom);

// non-owner cannot force withdraw
let err = suite.unsafe_force_withdraw_unauthorized(coin(100, &suite.reward_denom));
let err = suite.unsafe_force_withdraw_unauthorized(
100u128,
UncheckedDenom::Native(suite.reward_denom.clone()),
);
assert_eq!(err, ContractError::Ownable(OwnershipError::NotOwner));

let after_balance =
Expand All @@ -3009,7 +3012,7 @@ fn test_unsafe_force_withdraw() {
assert_eq!(owner_balance, 0);

// owner can force withdraw
suite.unsafe_force_withdraw(coin(100, &suite.reward_denom));
suite.unsafe_force_withdraw(100u128, UncheckedDenom::Native(suite.reward_denom.clone()));

// owner has balance
let owner_balance = suite.get_balance_native(OWNER, &suite.reward_denom);
Expand Down

0 comments on commit 321de22

Please sign in to comment.