Skip to content

Commit

Permalink
feat: Splitter Send Config (#686)
Browse files Browse the repository at this point in the history
Co-authored-by: Connor Barr <[email protected]>
  • Loading branch information
joemonem and crnbarr93 authored Dec 6, 2024
1 parent a146675 commit 3b80721
Show file tree
Hide file tree
Showing 26 changed files with 740 additions and 134 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased

### Added
- Added optional config for Send in Splitter contracts [(#686)](https://github.com/andromedaprotocol/andromeda-core/pull/686)

- Added Distance ADO [(#570)](https://github.com/andromedaprotocol/andromeda-core/pull/570)

Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion contracts/finance/andromeda-set-amount-splitter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "andromeda-set-amount-splitter"
version = "1.0.3-beta"
version = "1.1.0-beta"
edition = "2021"
rust-version = "1.75.0"

Expand Down
75 changes: 69 additions & 6 deletions contracts/finance/andromeda-set-amount-splitter/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use andromeda_finance::{
};
use andromeda_std::{
ado_base::{InstantiateMsg as BaseInstantiateMsg, MigrateMsg},
amp::messages::AMPPkt,
amp::{messages::AMPPkt, Recipient},
common::{actions::call_action, encode_binary, expiration::Expiry, Milliseconds},
error::ContractError,
};
Expand Down Expand Up @@ -52,13 +52,15 @@ pub fn instantiate(
Splitter {
recipients: msg.recipients.clone(),
lock: lock_time.get_time(&env.block),
default_recipient: msg.default_recipient.clone(),
}
}
None => {
Splitter {
recipients: msg.recipients.clone(),
// If locking isn't desired upon instantiation, it's automatically set to 0
lock: Milliseconds::default(),
default_recipient: msg.default_recipient.clone(),
}
}
};
Expand Down Expand Up @@ -124,7 +126,10 @@ pub fn handle_execute(mut ctx: ExecuteContext, msg: ExecuteMsg) -> Result<Respon
let res = match msg {
ExecuteMsg::UpdateRecipients { recipients } => execute_update_recipients(ctx, recipients),
ExecuteMsg::UpdateLock { lock_time } => execute_update_lock(ctx, lock_time),
ExecuteMsg::Send {} => execute_send(ctx),
ExecuteMsg::UpdateDefaultRecipient { recipient } => {
execute_update_default_recipient(ctx, recipient)
}
ExecuteMsg::Send { config } => execute_send(ctx, config),
_ => ADOContract::default().execute(ctx, msg),
}?;
Ok(res
Expand All @@ -133,7 +138,53 @@ pub fn handle_execute(mut ctx: ExecuteContext, msg: ExecuteMsg) -> Result<Respon
.add_events(action_response.events))
}

fn execute_send(ctx: ExecuteContext) -> Result<Response, ContractError> {
fn execute_update_default_recipient(
ctx: ExecuteContext,
recipient: Option<Recipient>,
) -> Result<Response, ContractError> {
let ExecuteContext {
deps, info, env, ..
} = ctx;

nonpayable(&info)?;

ensure!(
ADOContract::default().is_owner_or_operator(deps.storage, info.sender.as_str())?,
ContractError::Unauthorized {}
);

let mut splitter = SPLITTER.load(deps.storage)?;

// Can't call this function while the lock isn't expired
ensure!(
splitter.lock.is_expired(&env.block),
ContractError::ContractLocked {}
);

if let Some(ref recipient) = recipient {
recipient.validate(&deps.as_ref())?;
}
splitter.default_recipient = recipient;

SPLITTER.save(deps.storage, &splitter)?;

Ok(Response::default().add_attributes(vec![
attr("action", "update_default_recipient"),
attr(
"recipient",
splitter
.default_recipient
.map_or("no default recipient".to_string(), |r| {
r.address.to_string()
}),
),
]))
}

fn execute_send(
ctx: ExecuteContext,
config: Option<Vec<AddressAmount>>,
) -> Result<Response, ContractError> {
let ExecuteContext { deps, info, .. } = ctx;

ensure!(
Expand All @@ -158,8 +209,13 @@ fn execute_send(ctx: ExecuteContext) -> Result<Response, ContractError> {
);
denom_set.insert(coin.denom);
}

let splitter = SPLITTER.load(deps.storage)?;
let splitter_recipients = if let Some(config) = config {
validate_recipient_list(deps.as_ref(), config.clone())?;
config
} else {
splitter.recipients
};

let mut msgs: Vec<SubMsg> = Vec::new();
let mut amp_funds: Vec<Coin> = Vec::new();
Expand All @@ -171,7 +227,7 @@ fn execute_send(ctx: ExecuteContext) -> Result<Response, ContractError> {
let mut remainder_funds = coin.amount;
let denom = coin.denom;

for recipient in &splitter.recipients {
for recipient in splitter_recipients.clone() {
// Find the recipient's corresponding denom for the current iteration of the sent funds
let recipient_coin = recipient
.coins
Expand Down Expand Up @@ -200,8 +256,15 @@ fn execute_send(ctx: ExecuteContext) -> Result<Response, ContractError> {

// Refund message for sender
if !remainder_funds.is_zero() {
let remainder_recipient = splitter
.default_recipient
.clone()
.unwrap_or(Recipient::new(info.sender.to_string(), None));
let msg = SubMsg::new(CosmosMsg::Bank(BankMsg::Send {
to_address: info.sender.clone().into_string(),
to_address: remainder_recipient
.address
.get_raw_address(&deps.as_ref())?
.into_string(),
amount: coins(remainder_funds.u128(), denom),
}));
msgs.push(msg);
Expand Down
28 changes: 21 additions & 7 deletions contracts/finance/andromeda-set-amount-splitter/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::contract::{execute, instantiate, query, reply};
use andromeda_finance::set_amount_splitter::{AddressAmount, ExecuteMsg, InstantiateMsg, QueryMsg};
use andromeda_std::common::expiration::Expiry;
use andromeda_std::{amp::Recipient, common::expiration::Expiry};
use andromeda_testing::{
mock::MockApp, mock_ado, mock_contract::ExecuteResult, MockADO, MockContract,
};
Expand All @@ -21,16 +21,28 @@ impl MockSetAmountSplitter {
kernel_address: impl Into<String>,
lock_time: Option<Expiry>,
owner: Option<String>,
default_recipient: Option<Recipient>,
) -> Self {
let msg =
mock_set_amount_splitter_instantiate_msg(recipients, kernel_address, lock_time, owner);
let msg = mock_set_amount_splitter_instantiate_msg(
recipients,
kernel_address,
lock_time,
owner,
default_recipient,
);
let res = app.instantiate_contract(code_id, sender, &msg, &[], "Andromeda Splitter", None);

Self(res.unwrap())
}

pub fn execute_send(&self, app: &mut MockApp, sender: Addr, funds: &[Coin]) -> ExecuteResult {
let msg = mock_set_amount_splitter_send_msg();
pub fn execute_send(
&self,
app: &mut MockApp,
sender: Addr,
funds: &[Coin],
config: Option<Vec<AddressAmount>>,
) -> ExecuteResult {
let msg = mock_set_amount_splitter_send_msg(config);

self.execute(app, &msg, sender, funds)
}
Expand All @@ -46,15 +58,17 @@ pub fn mock_set_amount_splitter_instantiate_msg(
kernel_address: impl Into<String>,
lock_time: Option<Expiry>,
owner: Option<String>,
default_recipient: Option<Recipient>,
) -> InstantiateMsg {
InstantiateMsg {
recipients,
lock_time,
kernel_address: kernel_address.into(),
owner,
default_recipient,
}
}

pub fn mock_set_amount_splitter_send_msg() -> ExecuteMsg {
ExecuteMsg::Send {}
pub fn mock_set_amount_splitter_send_msg(config: Option<Vec<AddressAmount>>) -> ExecuteMsg {
ExecuteMsg::Send { config }
}
Loading

0 comments on commit 3b80721

Please sign in to comment.