Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Recipient in rate limiting withdrawals #804

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- feat: Improved macros and execution flow for AMP [(#741)](https://github.com/andromedaprotocol/andromeda-core/pull/741)
- chore: remove unused contracts & code [(#790)](https://github.com/andromedaprotocol/andromeda-core/pull/790)
- feat: Recipient in rate limiting withdrawals [(#804)](https://github.com/andromedaprotocol/andromeda-core/pull/804)

### Fixed

Expand Down
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "andromeda-rate-limiting-withdrawals"
version = "2.1.0-beta"
version = "2.1.1-b.1"
edition = "2021"
rust-version = "1.75.0"

Expand Down Expand Up @@ -33,3 +33,4 @@ cw-orch = { workspace = true }

[dev-dependencies]
andromeda-app = { workspace = true }
rstest = { workspace = true }
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ use andromeda_finance::rate_limiting_withdrawals::{
use andromeda_std::{
ado_base::{InstantiateMsg as BaseInstantiateMsg, MigrateMsg},
ado_contract::ADOContract,
amp::{
messages::{AMPCtx, AMPPkt},
Recipient,
},
andr_execute_fn,
common::{context::ExecuteContext, encode_binary, Milliseconds},
error::ContractError,
};
use cosmwasm_std::{
ensure, entry_point, BankMsg, Binary, Coin, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Reply,
Response, StdError, Uint128,
Response, StdError, SubMsg, Uint128,
};
use cw_utils::one_coin;

Expand Down Expand Up @@ -72,7 +76,7 @@ pub fn instantiate(
pub fn execute(ctx: ExecuteContext, msg: ExecuteMsg) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::Deposit { recipient } => execute_deposit(ctx, recipient),
ExecuteMsg::Withdraw { amount } => execute_withdraw(ctx, amount),
ExecuteMsg::Withdraw { amount, recipient } => execute_withdraw(ctx, amount, recipient),
_ => ADOContract::default().execute(ctx, msg),
}
}
Expand Down Expand Up @@ -134,9 +138,18 @@ fn execute_deposit(
Ok(res)
}

fn execute_withdraw(ctx: ExecuteContext, amount: Uint128) -> Result<Response, ContractError> {
fn execute_withdraw(
ctx: ExecuteContext,
amount: Uint128,
recipient: Option<Recipient>,
) -> Result<Response, ContractError> {
let ExecuteContext {
deps, info, env, ..
deps,
info,
env,
amp_ctx,
contract,
..
} = ctx;

// check if sender has an account
Expand Down Expand Up @@ -186,12 +199,34 @@ fn execute_withdraw(ctx: ExecuteContext, amount: Uint128) -> Result<Response, Co
amount,
};

let message: SubMsg = if let Some(recipient) = recipient {
let amp_msg = recipient.generate_amp_msg(&deps.as_ref(), Some(vec![coin.clone()]))?;
let ctx = if let Some(pkt) = amp_ctx {
pkt.ctx
} else {
AMPCtx::new(
info.sender.to_string(),
env.contract.address.to_string(),
0,
None,
)
};
let amp_pkt = AMPPkt::new_with_ctx(ctx, vec![amp_msg]);
let kernel_address = contract.get_kernel_address(deps.storage)?;
amp_pkt.to_sub_msg(kernel_address, Some(vec![coin.clone()]), 0)?
} else {
SubMsg::reply_always(
CosmosMsg::Bank(BankMsg::Send {
to_address: info.sender.to_string(),
amount: vec![coin.clone()],
}),
0,
)
};

// Transfer funds
let res = Response::new()
.add_message(CosmosMsg::Bank(BankMsg::Send {
to_address: info.sender.to_string(),
amount: vec![coin.clone()],
}))
.add_submessage(message)
.add_attribute("action", "withdrew funds")
.add_attribute("coin", coin.to_string());
Ok(res)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use andromeda_std::{common::Milliseconds, error::ContractError};
use andromeda_std::{
amp::{AndrAddr, Recipient},
common::Milliseconds,
error::ContractError,
};
use cosmwasm_std::{
coin,
coin, from_json,
testing::{mock_env, mock_info},
DepsMut, Response, Uint128,
BankMsg, Binary, CosmosMsg, DepsMut, Response, Uint128, WasmMsg,
};
pub const OWNER: &str = "creator";

Expand All @@ -17,6 +21,8 @@ use andromeda_finance::rate_limiting_withdrawals::{
AccountDetails, CoinAndLimit, ExecuteMsg, InstantiateMsg, MinimumFrequency,
};

use rstest::*;

fn init(deps: DepsMut) -> Response {
let msg = InstantiateMsg {
owner: Some(OWNER.to_owned()),
Expand Down Expand Up @@ -136,6 +142,7 @@ fn test_withdraw_account_not_found() {
let info = mock_info("random", &[]);
let exec = ExecuteMsg::Withdraw {
amount: Uint128::from(19_u16),
recipient: None,
};
let err = execute(deps.as_mut(), mock_env(), info, exec).unwrap_err();
assert_eq!(err, ContractError::AccountNotFound {});
Expand All @@ -157,6 +164,7 @@ fn test_withdraw_over_account_limit() {
let info = mock_info("andromedauser", &[]);
let exec = ExecuteMsg::Withdraw {
amount: Uint128::from(31_u16),
recipient: None,
};
let err = execute(deps.as_mut(), mock_env(), info, exec).unwrap_err();
assert_eq!(err, ContractError::InsufficientFunds {});
Expand All @@ -178,12 +186,14 @@ fn test_withdraw_funds_locked() {
let info = mock_info("andromedauser", &[]);
let exec = ExecuteMsg::Withdraw {
amount: Uint128::from(10_u16),
recipient: None,
};
let _res = execute(deps.as_mut(), mock_env(), info, exec).unwrap();

let info = mock_info("andromedauser", &[]);
let exec = ExecuteMsg::Withdraw {
amount: Uint128::from(10_u16),
recipient: None,
};

let err = execute(deps.as_mut(), mock_env(), info, exec).unwrap_err();
Expand Down Expand Up @@ -219,13 +229,16 @@ fn test_withdraw_over_allowed_limit() {
let info = mock_info("andromedauser", &[]);
let exec = ExecuteMsg::Withdraw {
amount: Uint128::from(21_u16),
recipient: None,
};
let err = execute(deps.as_mut(), mock_env(), info, exec).unwrap_err();
assert_eq!(err, ContractError::WithdrawalLimitExceeded {});
}

#[test]
fn test_withdraw_works() {
#[rstest]
#[case::direct(None, "andromedauser")] // Withdraw to self
#[case::with_recipient(Some(Recipient::new("recipient".to_string(), Some(Binary::default()))), "recipient")] // Withdraw to different recipient
fn test_withdraw_works(#[case] recipient: Option<Recipient>, #[case] expected_recipient: &str) {
let mut deps = mock_dependencies_custom(&[]);
let env = mock_env();
let info = mock_info("creator", &[]);
Expand All @@ -246,14 +259,45 @@ fn test_withdraw_works() {
};

let info = mock_info("creator", &[coin(30, "junox")]);

let _res = execute(deps.as_mut(), mock_env(), info, exec).unwrap();

let info = mock_info("andromedauser", &[]);
let exec = ExecuteMsg::Withdraw {
amount: Uint128::from(10_u16),
recipient: recipient.clone(),
};
let _res = execute(deps.as_mut(), mock_env(), info, exec).unwrap();
let res = execute(deps.as_mut(), mock_env(), info, exec).unwrap();
let sub_msg = res.messages[0].msg.clone();

if recipient.is_some() {
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
funds,
msg,
}) = sub_msg
{
assert_eq!(contract_addr, MOCK_KERNEL_CONTRACT);
assert_eq!(funds, vec![coin(10, "junox")]);
let msg: ExecuteMsg = from_json(&msg).unwrap_or_else(|e| {
panic!("Failed to deserialize pkt: {}", e);
});

if let ExecuteMsg::AMPReceive(pkt) = msg {
let amp_msg = pkt.messages[0].clone();
assert_eq!(amp_msg.recipient, AndrAddr::from_string(expected_recipient));
assert_eq!(amp_msg.message, Binary::default());
} else {
panic!("Message is not a AMPReceive");
}
} else {
panic!("SubMsg is not a WasmMsg::Execute");
}
} else if let CosmosMsg::Bank(BankMsg::Send { to_address, amount }) = sub_msg {
assert_eq!(to_address, expected_recipient.to_string());
assert_eq!(amount, vec![coin(10, "junox")]);
} else {
panic!("SubMsg is not a BankMsg::Send");
}

let expected_balance = AccountDetails {
balance: Uint128::from(20_u16),
Expand Down
5 changes: 4 additions & 1 deletion packages/andromeda-finance/src/rate_limiting_withdrawals.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use andromeda_std::{andr_exec, andr_instantiate, andr_query, common::MillisecondsDuration};
use andromeda_std::{
amp::Recipient, andr_exec, andr_instantiate, andr_query, common::MillisecondsDuration,
};
use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::{Timestamp, Uint128};

Expand Down Expand Up @@ -55,6 +57,7 @@ pub enum ExecuteMsg {
#[attrs(nonpayable)]
Withdraw {
amount: Uint128,
recipient: Option<Recipient>,
},
}

Expand Down