use crate::{ans_resolve::Resolve, features::AbstractNameService, AbstractSdkResult, Execution};
use core::objects::{AnsAsset, AssetEntry};
use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, Deps};
use cw_asset::Asset;
pub trait TransferInterface: AbstractNameService + Execution {
fn bank<'a>(&'a self, deps: Deps<'a>) -> Bank<Self> {
Bank { base: self, deps }
}
}
impl<T> TransferInterface for T where T: AbstractNameService + Execution {}
#[derive(Clone)]
pub struct Bank<'a, T: TransferInterface> {
base: &'a T,
deps: Deps<'a>,
}
impl<'a, T: TransferInterface> Bank<'a, T> {
pub fn balances(&self, assets: &[AssetEntry]) -> AbstractSdkResult<Vec<Asset>> {
assets
.iter()
.map(|asset| self.balance(asset))
.collect::<AbstractSdkResult<Vec<Asset>>>()
}
pub fn balance(&self, asset: &AssetEntry) -> AbstractSdkResult<Asset> {
let resolved_info = asset.resolve(&self.deps.querier, &self.base.ans_host(self.deps)?)?;
let balance =
resolved_info.query_balance(&self.deps.querier, self.base.proxy_address(self.deps)?)?;
Ok(Asset::new(resolved_info, balance))
}
pub fn transfer<R: Transferable>(
&self,
funds: Vec<R>,
recipient: &Addr,
) -> AbstractSdkResult<CosmosMsg> {
let transferable_funds = funds
.into_iter()
.map(|asset| asset.transferable_asset(self.base, self.deps))
.collect::<AbstractSdkResult<Vec<Asset>>>()?;
let transfer_msgs = transferable_funds
.iter()
.map(|asset| asset.transfer_msg(recipient.clone()))
.collect::<Result<Vec<CosmosMsg>, _>>();
self.base.executor(self.deps).execute(transfer_msgs?)
}
pub fn deposit<R: Transferable>(&self, funds: Vec<R>) -> AbstractSdkResult<Vec<CosmosMsg>> {
let recipient = self.base.proxy_address(self.deps)?;
let transferable_funds = funds
.into_iter()
.map(|asset| asset.transferable_asset(self.base, self.deps))
.collect::<AbstractSdkResult<Vec<Asset>>>()?;
transferable_funds
.iter()
.map(|asset| asset.transfer_msg(recipient.clone()))
.collect::<Result<Vec<CosmosMsg>, _>>()
.map_err(Into::into)
}
pub fn deposit_coins(&self, coins: Vec<Coin>) -> AbstractSdkResult<CosmosMsg> {
let recipient = self.base.proxy_address(self.deps)?.into_string();
Ok(CosmosMsg::Bank(BankMsg::Send {
to_address: recipient,
amount: coins,
}))
}
}
pub trait Transferable {
fn transferable_asset<T: AbstractNameService>(
self,
base: &T,
deps: Deps,
) -> AbstractSdkResult<Asset>;
}
impl Transferable for &AnsAsset {
fn transferable_asset<T: AbstractNameService>(
self,
base: &T,
deps: Deps,
) -> AbstractSdkResult<Asset> {
self.resolve(&deps.querier, &base.ans_host(deps)?)
}
}
impl Transferable for AnsAsset {
fn transferable_asset<T: AbstractNameService>(
self,
base: &T,
deps: Deps,
) -> AbstractSdkResult<Asset> {
self.resolve(&deps.querier, &base.ans_host(deps)?)
}
}
impl Transferable for Asset {
fn transferable_asset<T: AbstractNameService>(
self,
_base: &T,
_deps: Deps,
) -> AbstractSdkResult<Asset> {
Ok(self)
}
}
impl Transferable for Coin {
fn transferable_asset<T: AbstractNameService>(
self,
_base: &T,
_deps: Deps,
) -> AbstractSdkResult<Asset> {
Ok(Asset::from(self))
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::mock_module::*;
use abstract_testing::prelude::*;
use cosmwasm_std::{testing::*, *};
use speculoos::prelude::*;
mod transfer_coins {
use super::*;
use core::proxy::ExecuteMsg::ModuleAction;
#[test]
fn transfer_asset_to_sender() {
let app = MockModule::new();
let deps = mock_dependencies();
let expected_amount = 100u128;
let expected_recipient = Addr::unchecked("recipient");
let bank = app.bank(deps.as_ref());
let coins = coins(expected_amount, "asset");
let actual_res = bank.transfer(coins.clone(), &expected_recipient);
assert_that!(actual_res).is_ok();
let expected_msg: CosmosMsg = wasm_execute(
TEST_PROXY,
&ModuleAction {
msgs: vec![CosmosMsg::Bank(BankMsg::Send {
to_address: expected_recipient.to_string(),
amount: coins,
})],
},
vec![],
)
.unwrap()
.into();
assert_that!(actual_res.unwrap()).is_equal_to(expected_msg);
}
}
mod deposit_coins {
use super::*;
#[test]
fn deposit_coins() {
let app = MockModule::new();
let deps = mock_dependencies();
let expected_amount = 100u128;
let bank = app.bank(deps.as_ref());
let coins = coins(expected_amount, "asset");
let actual_res = bank.deposit_coins(coins.clone());
let expected_msg: CosmosMsg = CosmosMsg::Bank(BankMsg::Send {
to_address: TEST_PROXY.to_string(),
amount: coins,
});
assert_that!(actual_res).is_ok().is_equal_to(expected_msg);
}
}
}