abstract_std/objects/pool/
pool_id.rsuse std::{fmt, str::FromStr};
use cosmwasm_std::{Addr, Api, StdError};
use crate::{error::AbstractError, AbstractResult};
#[cosmwasm_schema::cw_serde]
#[non_exhaustive]
#[cfg_attr(not(target_arch = "wasm32"), derive(Eq, Hash, PartialOrd, Ord))]
pub enum PoolAddressBase<T> {
SeparateAddresses { swap: T, liquidity: T },
Contract(T),
Id(u64),
}
impl<T> PoolAddressBase<T> {
pub fn contract<C: Into<T>>(contract: C) -> Self {
Self::Contract(contract.into())
}
pub fn id<N: Into<u64>>(id: N) -> Self {
Self::Id(id.into())
}
}
pub type PoolAddress = PoolAddressBase<Addr>;
impl PoolAddress {
pub fn expect_contract(&self) -> AbstractResult<Addr> {
match self {
PoolAddress::Contract(addr) => Ok(addr.clone()),
_ => Err(AbstractError::Assert(
"Pool address not a contract address.".into(),
)),
}
}
pub fn expect_id(&self) -> AbstractResult<u64> {
match self {
PoolAddress::Id(id) => Ok(*id),
_ => Err(AbstractError::Assert(
"Pool address not an numerical ID.".into(),
)),
}
}
}
pub type UncheckedPoolAddress = PoolAddressBase<String>;
impl FromStr for UncheckedPoolAddress {
type Err = AbstractError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let words: Vec<&str> = s.split(':').collect();
match words[0] {
"contract" => {
if words.len() != 2 {
return Err(AbstractError::FormattingError {
object: "unchecked pool address".to_string(),
expected: "contract:{{contract_addr}}".to_string(),
actual: s.to_string(),
});
}
Ok(UncheckedPoolAddress::Contract(String::from(words[1])))
}
"id" => {
if words.len() != 2 {
return Err(AbstractError::FormattingError {
object: "unchecked pool address".to_string(),
expected: "id:{{pool_id}}".to_string(),
actual: s.to_string(),
});
}
let parsed_id_res = words[1].parse::<u64>();
match parsed_id_res {
Ok(id) => Ok(UncheckedPoolAddress::Id(id)),
Err(err) => Err(StdError::generic_err(err.to_string()).into()),
}
}
_unknown => Err(AbstractError::FormattingError {
object: "unchecked pool address".to_string(),
expected: "'contract' or 'id'".to_string(),
actual: s.to_string(),
}),
}
}
}
impl From<PoolAddress> for UncheckedPoolAddress {
fn from(pool_info: PoolAddress) -> Self {
match pool_info {
PoolAddress::Contract(contract_addr) => {
UncheckedPoolAddress::Contract(contract_addr.into())
}
PoolAddress::Id(denom) => UncheckedPoolAddress::Id(denom),
PoolAddress::SeparateAddresses { swap, liquidity } => {
UncheckedPoolAddress::SeparateAddresses {
swap: swap.into(),
liquidity: liquidity.into(),
}
}
}
}
}
impl From<&PoolAddress> for UncheckedPoolAddress {
fn from(pool_id: &PoolAddress) -> Self {
match pool_id {
PoolAddress::Contract(contract_addr) => {
UncheckedPoolAddress::Contract(contract_addr.into())
}
PoolAddress::Id(denom) => UncheckedPoolAddress::Id(*denom),
PoolAddress::SeparateAddresses { swap, liquidity } => {
UncheckedPoolAddress::SeparateAddresses {
swap: swap.into(),
liquidity: liquidity.into(),
}
}
}
}
}
impl From<Addr> for PoolAddress {
fn from(contract_addr: Addr) -> Self {
PoolAddress::Contract(contract_addr)
}
}
impl UncheckedPoolAddress {
pub fn check(&self, api: &dyn Api) -> AbstractResult<PoolAddress> {
Ok(match self {
UncheckedPoolAddress::Contract(contract_addr) => {
PoolAddress::Contract(api.addr_validate(contract_addr)?)
}
UncheckedPoolAddress::Id(pool_id) => PoolAddress::Id(*pool_id),
UncheckedPoolAddress::SeparateAddresses { swap, liquidity } => {
PoolAddress::SeparateAddresses {
swap: api.addr_validate(swap)?,
liquidity: api.addr_validate(liquidity)?,
}
}
})
}
}
impl fmt::Display for PoolAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PoolAddress::Contract(contract_addr) => write!(f, "contract:{contract_addr}"),
PoolAddress::Id(pool_id) => write!(f, "id:{pool_id}"),
PoolAddress::SeparateAddresses { swap, liquidity } => {
write!(f, "swap:{swap}, pair: {liquidity}")
}
}
}
}
#[cfg(test)]
mod test {
#![allow(clippy::needless_borrows_for_generic_args)]
use cosmwasm_std::testing::MockApi;
use super::*;
#[coverage_helper::test]
fn test_pool_id_from_str() {
let api = MockApi::default();
let contract_addr = api.addr_make("foo");
let pool_id_str = format!("contract:{contract_addr}");
let pool_id = UncheckedPoolAddress::from_str(&pool_id_str).unwrap();
let pool_id = pool_id.check(&api).unwrap();
assert_eq!(pool_id.to_string(), pool_id_str.to_string());
}
#[coverage_helper::test]
fn test_expect_contract_happy() {
let api = MockApi::default();
let contract_addr = api.addr_make("foo");
let pool_id = PoolAddress::Contract(contract_addr.clone());
let res = pool_id.expect_contract();
assert!(res.is_ok());
assert_eq!(res.unwrap(), contract_addr);
}
#[coverage_helper::test]
fn test_expect_contract_sad() {
let pool_id = PoolAddress::Id(1);
let res = pool_id.expect_contract();
assert!(res.is_err());
}
#[coverage_helper::test]
fn test_expect_id_happy() {
let pool_id = PoolAddress::Id(1);
let res = pool_id.expect_id();
assert!(res.is_ok());
assert_eq!(res.unwrap(), 1);
}
#[coverage_helper::test]
fn test_expect_id_sad() {
let api = MockApi::default();
let contract_addr = api.addr_make("foo");
let pool_id = PoolAddress::Contract(contract_addr);
let res = pool_id.expect_id();
assert!(res.is_err());
}
}