1use abstract_interface::{Abstract, ExecuteMsgFns};
6use abstract_std::objects::{
7 pool_id::UncheckedPoolAddress, PoolMetadata, UncheckedChannelEntry, UncheckedContractEntry,
8};
9use cw_asset::AssetInfoUnchecked;
10use cw_orch::prelude::*;
11
12use crate::client::{AbstractClient, AbstractClientResult};
13
14impl<Chain: CwEnv> AbstractClient<Chain> {
15 pub fn builder(chain: Chain) -> AbstractClientBuilder<Chain> {
17 AbstractClientBuilder::new(chain)
18 }
19
20 #[cfg(feature = "test-utils")]
21 pub fn cw20_builder(
23 &self,
24 name: impl Into<String>,
25 symbol: impl Into<String>,
26 decimals: u8,
27 ) -> self::cw20_builder::Cw20Builder<Chain> {
28 use crate::Environment;
29 self::cw20_builder::Cw20Builder::new(
30 self.environment(),
31 name.into(),
32 symbol.into(),
33 decimals,
34 )
35 }
36}
37
38pub struct AbstractClientBuilder<Chain: CwEnv> {
41 chain: Chain,
42 dexes: Vec<String>,
43 contracts: Vec<(UncheckedContractEntry, String)>,
44 assets: Vec<(String, AssetInfoUnchecked)>,
45 channels: Vec<(UncheckedChannelEntry, String)>,
46 pools: Vec<(UncheckedPoolAddress, PoolMetadata)>,
47}
48
49impl<Chain: CwEnv> AbstractClientBuilder<Chain> {
50 pub(crate) fn new(chain: Chain) -> Self {
51 Self {
52 chain,
53 dexes: vec![],
54 contracts: vec![],
55 assets: vec![],
56 channels: vec![],
57 pools: vec![],
58 }
59 }
60
61 pub fn contract(
63 &mut self,
64 contract_entry: UncheckedContractEntry,
65 address: impl Into<String>,
66 ) -> &mut Self {
67 self.contracts.push((contract_entry, address.into()));
68 self
69 }
70
71 pub fn contracts(&mut self, contracts: Vec<(UncheckedContractEntry, String)>) -> &mut Self {
73 self.contracts = contracts;
74 self
75 }
76
77 pub fn asset(&mut self, name: impl Into<String>, asset_info: AssetInfoUnchecked) -> &mut Self {
79 self.assets.push((name.into(), asset_info));
80 self
81 }
82
83 pub fn assets(&mut self, assets: Vec<(String, AssetInfoUnchecked)>) -> &mut Self {
85 self.assets = assets;
86 self
87 }
88
89 pub fn channel(
91 &mut self,
92 channel_entry: UncheckedChannelEntry,
93 channel_value: impl Into<String>,
94 ) -> &mut Self {
95 self.channels.push((channel_entry, channel_value.into()));
96 self
97 }
98
99 pub fn channels(&mut self, channels: Vec<(UncheckedChannelEntry, String)>) -> &mut Self {
101 self.channels = channels;
102 self
103 }
104
105 pub fn pool(
107 &mut self,
108 pool_address: UncheckedPoolAddress,
109 pool_metadata: PoolMetadata,
110 ) -> &mut Self {
111 self.pools.push((pool_address, pool_metadata));
112 self
113 }
114
115 pub fn pools(&mut self, pools: Vec<(UncheckedPoolAddress, PoolMetadata)>) -> &mut Self {
117 self.pools = pools;
118 self
119 }
120
121 pub fn dex(&mut self, dex: &str) -> &mut Self {
123 self.dexes.push(dex.to_string());
124 self
125 }
126
127 pub fn dexes(&mut self, dexes: Vec<String>) -> &mut Self {
129 self.dexes = dexes;
130 self
131 }
132
133 pub fn build(&self) -> AbstractClientResult<AbstractClient<Chain>> {
135 let abstr = Abstract::deploy_on(self.chain.clone(), ())?;
136 self.update_ans(&abstr)?;
137
138 AbstractClient::new(self.chain.clone())
139 }
140
141 fn update_ans(&self, abstr: &Abstract<Chain>) -> AbstractClientResult<()> {
142 let ans_host = &abstr.ans_host;
143 if !self.dexes.is_empty() {
144 ans_host.update_dexes(self.dexes.clone(), vec![])?;
145 }
146 if !self.contracts.is_empty() {
147 ans_host.update_contract_addresses(self.contracts.clone(), vec![])?;
148 }
149 if !self.assets.is_empty() {
150 ans_host.update_asset_addresses(self.assets.clone(), vec![])?;
151 }
152 if !self.channels.is_empty() {
153 ans_host.update_channels(self.channels.clone(), vec![])?;
154 }
155 if !self.pools.is_empty() {
156 ans_host.update_pools(self.pools.clone(), vec![])?;
157 }
158
159 Ok(())
160 }
161}
162
163#[cfg(feature = "test-utils")]
164pub mod cw20_builder {
165 use cosmwasm_std::Addr;
169 pub use cw20::*;
170 pub use cw20_base::msg::InstantiateMarketingInfo;
171 use cw_orch::{environment::CwEnv, prelude::*};
172 pub use cw_plus_interface::cw20_base::{
173 Cw20Base, ExecuteMsgInterfaceFns, InstantiateMsg, QueryMsgInterfaceFns,
174 };
175
176 use crate::client::AbstractClientResult;
177
178 pub struct Cw20Builder<Chain: CwEnv> {
184 chain: Chain,
185 name: String,
186 symbol: String,
187 decimals: u8,
188 initial_balances: Vec<Cw20Coin>,
189 mint: Option<MinterResponse>,
190 marketing: Option<InstantiateMarketingInfo>,
191 admin: Option<Addr>,
192 }
193
194 impl<Chain: CwEnv> Cw20Builder<Chain> {
195 pub(crate) fn new(chain: Chain, name: String, symbol: String, decimals: u8) -> Self {
197 Self {
198 chain,
199 name,
200 symbol,
201 decimals,
202 initial_balances: vec![],
203 mint: None,
204 marketing: None,
205 admin: None,
206 }
207 }
208
209 pub fn initial_balance(&mut self, initial_balance: Cw20Coin) -> &mut Self {
211 self.initial_balances.push(initial_balance);
212 self
213 }
214
215 pub fn initial_balances(&mut self, initial_balances: Vec<Cw20Coin>) -> &mut Self {
217 self.initial_balances = initial_balances;
218 self
219 }
220
221 pub fn mint(&mut self, mint: MinterResponse) -> &mut Self {
223 self.mint = Some(mint);
224 self
225 }
226
227 pub fn marketing(&mut self, marketing: InstantiateMarketingInfo) -> &mut Self {
229 self.marketing = Some(marketing);
230 self
231 }
232
233 pub fn admin(&mut self, admin: impl Into<String>) -> &mut Self {
235 self.admin = Some(Addr::unchecked(admin.into()));
236 self
237 }
238
239 pub fn instantiate_with_id(&self, id: &str) -> AbstractClientResult<Cw20Base<Chain>> {
241 let cw20 = Cw20Base::new(id, self.chain.clone());
242 cw20.upload()?;
243 cw20.instantiate(
244 &InstantiateMsg {
245 decimals: self.decimals,
246 mint: self.mint.clone(),
247 symbol: self.symbol.clone(),
248 name: self.name.clone(),
249 initial_balances: self.initial_balances.clone(),
250 marketing: self.marketing.clone(),
251 },
252 self.admin.as_ref(),
253 &[],
254 )?;
255 Ok(cw20)
256 }
257 }
258}