abstract_core/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 use cosmwasm_std::testing::MockApi;
180 use speculoos::prelude::*;
181
182 use super::*;
183
184 #[test]
185 fn test_pool_id_from_str() {
186 let api = MockApi::default();
187 let pool_id_str = "contract:cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02";
188 let pool_id = UncheckedPoolAddress::from_str(pool_id_str).unwrap();
189 let pool_id = pool_id.check(&api).unwrap();
190 assert_that!(pool_id.to_string()).is_equal_to(pool_id_str.to_string());
191 }
192
193 #[test]
194 fn test_expect_contract_happy() {
195 let api = MockApi::default();
196 let pool_id = PoolAddress::Contract(
197 api.addr_validate("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")
198 .unwrap(),
199 );
200 let res = pool_id.expect_contract();
201 assert_that!(res).is_ok();
202 assert_that!(res.unwrap()).is_equal_to(Addr::unchecked(
203 "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02",
204 ));
205 }
206
207 #[test]
208 fn test_expect_contract_sad() {
209 let pool_id = PoolAddress::Id(1);
210 let res = pool_id.expect_contract();
211 assert_that!(res).is_err();
212 }
213
214 #[test]
215 fn test_expect_id_happy() {
216 let pool_id = PoolAddress::Id(1);
217 let res = pool_id.expect_id();
218 assert_that!(res).is_ok();
219 assert_that!(res.unwrap()).is_equal_to(1);
220 }
221
222 #[test]
223 fn test_expect_id_sad() {
224 let api = MockApi::default();
225 let pool_id = PoolAddress::Contract(
226 api.addr_validate("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")
227 .unwrap(),
228 );
229 let res = pool_id.expect_id();
230 assert_that!(res).is_err();
231 }
232}