abstract_std/objects/pool/
pool_id.rs1use std::{fmt, str::FromStr};
2
3use cosmwasm_std::{Addr, Api, StdError};
4
5use crate::{error::AbstractError, AbstractResult};
6
7#[cosmwasm_schema::cw_serde]
8#[non_exhaustive]
9#[cfg_attr(not(target_arch = "wasm32"), derive(Eq, Hash, PartialOrd, Ord))]
11pub enum PoolAddressBase<T> {
12 SeparateAddresses { swap: T, liquidity: T },
13 Contract(T),
14 Id(u64),
15}
16
17impl<T> PoolAddressBase<T> {
18 pub fn contract<C: Into<T>>(contract: C) -> Self {
19 Self::Contract(contract.into())
20 }
21 pub fn id<N: Into<u64>>(id: N) -> Self {
22 Self::Id(id.into())
23 }
24}
25
26pub type PoolAddress = PoolAddressBase<Addr>;
28
29impl PoolAddress {
30 pub fn expect_contract(&self) -> AbstractResult<Addr> {
31 match self {
32 PoolAddress::Contract(addr) => Ok(addr.clone()),
33 _ => Err(AbstractError::Assert(
34 "Pool address not a contract address.".into(),
35 )),
36 }
37 }
38
39 pub fn expect_id(&self) -> AbstractResult<u64> {
40 match self {
41 PoolAddress::Id(id) => Ok(*id),
42 _ => Err(AbstractError::Assert(
43 "Pool address not an numerical ID.".into(),
44 )),
45 }
46 }
47}
48pub type UncheckedPoolAddress = PoolAddressBase<String>;
50
51impl FromStr for UncheckedPoolAddress {
52 type Err = AbstractError;
53
54 fn from_str(s: &str) -> Result<Self, Self::Err> {
55 let words: Vec<&str> = s.split(':').collect();
56
57 match words[0] {
58 "contract" => {
59 if words.len() != 2 {
60 return Err(AbstractError::FormattingError {
61 object: "unchecked pool address".to_string(),
62 expected: "contract:{{contract_addr}}".to_string(),
63 actual: s.to_string(),
64 });
65 }
66
67 Ok(UncheckedPoolAddress::Contract(String::from(words[1])))
68 }
69 "id" => {
70 if words.len() != 2 {
71 return Err(AbstractError::FormattingError {
72 object: "unchecked pool address".to_string(),
73 expected: "id:{{pool_id}}".to_string(),
74 actual: s.to_string(),
75 });
76 }
77 let parsed_id_res = words[1].parse::<u64>();
78 match parsed_id_res {
79 Ok(id) => Ok(UncheckedPoolAddress::Id(id)),
80 Err(err) => Err(StdError::generic_err(err.to_string()).into()),
81 }
82 }
83 _unknown => Err(AbstractError::FormattingError {
84 object: "unchecked pool address".to_string(),
85 expected: "'contract' or 'id'".to_string(),
86 actual: s.to_string(),
87 }),
88 }
89 }
90}
91
92impl From<PoolAddress> for UncheckedPoolAddress {
93 fn from(pool_info: PoolAddress) -> Self {
94 match pool_info {
95 PoolAddress::Contract(contract_addr) => {
96 UncheckedPoolAddress::Contract(contract_addr.into())
97 }
98 PoolAddress::Id(denom) => UncheckedPoolAddress::Id(denom),
99 PoolAddress::SeparateAddresses { swap, liquidity } => {
100 UncheckedPoolAddress::SeparateAddresses {
101 swap: swap.into(),
102 liquidity: liquidity.into(),
103 }
104 }
105 }
106 }
107}
108
109impl From<&PoolAddress> for UncheckedPoolAddress {
110 fn from(pool_id: &PoolAddress) -> Self {
111 match pool_id {
112 PoolAddress::Contract(contract_addr) => {
113 UncheckedPoolAddress::Contract(contract_addr.into())
114 }
115 PoolAddress::Id(denom) => UncheckedPoolAddress::Id(*denom),
116 PoolAddress::SeparateAddresses { swap, liquidity } => {
117 UncheckedPoolAddress::SeparateAddresses {
118 swap: swap.into(),
119 liquidity: liquidity.into(),
120 }
121 }
122 }
123 }
124}
125
126impl From<Addr> for PoolAddress {
127 fn from(contract_addr: Addr) -> Self {
128 PoolAddress::Contract(contract_addr)
129 }
130}
131
132impl UncheckedPoolAddress {
133 pub fn check(&self, api: &dyn Api) -> AbstractResult<PoolAddress> {
150 Ok(match self {
151 UncheckedPoolAddress::Contract(contract_addr) => {
152 PoolAddress::Contract(api.addr_validate(contract_addr)?)
153 }
154 UncheckedPoolAddress::Id(pool_id) => PoolAddress::Id(*pool_id),
155 UncheckedPoolAddress::SeparateAddresses { swap, liquidity } => {
156 PoolAddress::SeparateAddresses {
157 swap: api.addr_validate(swap)?,
158 liquidity: api.addr_validate(liquidity)?,
159 }
160 }
161 })
162 }
163}
164
165impl fmt::Display for PoolAddress {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 match self {
168 PoolAddress::Contract(contract_addr) => write!(f, "contract:{contract_addr}"),
169 PoolAddress::Id(pool_id) => write!(f, "id:{pool_id}"),
170 PoolAddress::SeparateAddresses { swap, liquidity } => {
171 write!(f, "swap:{swap}, pair: {liquidity}")
172 }
173 }
174 }
175}
176
177#[cfg(test)]
178mod test {
179 #![allow(clippy::needless_borrows_for_generic_args)]
180 use cosmwasm_std::testing::MockApi;
181
182 use super::*;
183
184 #[coverage_helper::test]
185 fn test_pool_id_from_str() {
186 let api = MockApi::default();
187 let contract_addr = api.addr_make("foo");
188 let pool_id_str = format!("contract:{contract_addr}");
189 let pool_id = UncheckedPoolAddress::from_str(&pool_id_str).unwrap();
190 let pool_id = pool_id.check(&api).unwrap();
191 assert_eq!(pool_id.to_string(), pool_id_str.to_string());
192 }
193
194 #[coverage_helper::test]
195 fn test_expect_contract_happy() {
196 let api = MockApi::default();
197 let contract_addr = api.addr_make("foo");
198 let pool_id = PoolAddress::Contract(contract_addr.clone());
199 let res = pool_id.expect_contract();
200 assert!(res.is_ok());
201 assert_eq!(res.unwrap(), contract_addr);
202 }
203
204 #[coverage_helper::test]
205 fn test_expect_contract_sad() {
206 let pool_id = PoolAddress::Id(1);
207 let res = pool_id.expect_contract();
208 assert!(res.is_err());
209 }
210
211 #[coverage_helper::test]
212 fn test_expect_id_happy() {
213 let pool_id = PoolAddress::Id(1);
214 let res = pool_id.expect_id();
215 assert!(res.is_ok());
216 assert_eq!(res.unwrap(), 1);
217 }
218
219 #[coverage_helper::test]
220 fn test_expect_id_sad() {
221 let api = MockApi::default();
222 let contract_addr = api.addr_make("foo");
223 let pool_id = PoolAddress::Contract(contract_addr);
224 let res = pool_id.expect_id();
225 assert!(res.is_err());
226 }
227}