dao_testing/
helpers.rs

1use cosmwasm_std::{to_json_binary, Addr, Binary, Uint128};
2use cw20::Cw20Coin;
3use cw_multi_test::{App, Executor};
4use cw_utils::Duration;
5use dao_interface::state::{Admin, ModuleInstantiateInfo};
6use dao_voting::threshold::ActiveThreshold;
7use dao_voting_cw4::msg::GroupContract;
8
9use crate::contracts::{
10    cw20_balances_voting_contract, cw20_base_contract, cw20_stake_contract,
11    cw20_staked_balances_voting_contract, cw4_group_contract, dao_dao_contract,
12    dao_voting_cw4_contract,
13};
14
15const CREATOR_ADDR: &str = "creator";
16
17pub fn instantiate_with_cw20_balances_governance(
18    app: &mut App,
19    governance_code_id: u64,
20    governance_instantiate: Binary,
21    initial_balances: Option<Vec<Cw20Coin>>,
22) -> Addr {
23    let cw20_id = app.store_code(cw20_base_contract());
24    let core_id = app.store_code(dao_dao_contract());
25    let votemod_id = app.store_code(cw20_balances_voting_contract());
26
27    let initial_balances = initial_balances.unwrap_or_else(|| {
28        vec![Cw20Coin {
29            address: CREATOR_ADDR.to_string(),
30            amount: Uint128::new(100_000_000),
31        }]
32    });
33
34    // Collapse balances so that we can test double votes.
35    let initial_balances: Vec<Cw20Coin> = {
36        let mut already_seen = vec![];
37        initial_balances
38            .into_iter()
39            .filter(|Cw20Coin { address, amount: _ }| {
40                if already_seen.contains(address) {
41                    false
42                } else {
43                    already_seen.push(address.clone());
44                    true
45                }
46            })
47            .collect()
48    };
49
50    let governance_instantiate = dao_interface::msg::InstantiateMsg {
51        dao_uri: None,
52        admin: None,
53        name: "DAO DAO".to_string(),
54        description: "A DAO that builds DAOs".to_string(),
55        image_url: None,
56        automatically_add_cw20s: true,
57        automatically_add_cw721s: true,
58        voting_module_instantiate_info: ModuleInstantiateInfo {
59            code_id: votemod_id,
60            msg: to_json_binary(&dao_voting_cw20_balance::msg::InstantiateMsg {
61                token_info: dao_voting_cw20_balance::msg::TokenInfo::New {
62                    code_id: cw20_id,
63                    label: "DAO DAO governance token".to_string(),
64                    name: "DAO".to_string(),
65                    symbol: "DAO".to_string(),
66                    decimals: 6,
67                    initial_balances,
68                    marketing: None,
69                },
70            })
71            .unwrap(),
72            admin: Some(Admin::CoreModule {}),
73            funds: vec![],
74            label: "DAO DAO voting module".to_string(),
75        },
76        proposal_modules_instantiate_info: vec![ModuleInstantiateInfo {
77            code_id: governance_code_id,
78            msg: governance_instantiate,
79            admin: Some(Admin::CoreModule {}),
80            funds: vec![],
81            label: "DAO DAO governance module".to_string(),
82        }],
83        initial_items: None,
84    };
85
86    app.instantiate_contract(
87        core_id,
88        Addr::unchecked(CREATOR_ADDR),
89        &governance_instantiate,
90        &[],
91        "DAO DAO",
92        None,
93    )
94    .unwrap()
95}
96
97pub fn instantiate_with_staked_balances_governance(
98    app: &mut App,
99    governance_code_id: u64,
100    governance_instantiate: Binary,
101    initial_balances: Option<Vec<Cw20Coin>>,
102) -> Addr {
103    let initial_balances = initial_balances.unwrap_or_else(|| {
104        vec![Cw20Coin {
105            address: CREATOR_ADDR.to_string(),
106            amount: Uint128::new(100_000_000),
107        }]
108    });
109
110    // Collapse balances so that we can test double votes.
111    let initial_balances: Vec<Cw20Coin> = {
112        let mut already_seen = vec![];
113        initial_balances
114            .into_iter()
115            .filter(|Cw20Coin { address, amount: _ }| {
116                if already_seen.contains(address) {
117                    false
118                } else {
119                    already_seen.push(address.clone());
120                    true
121                }
122            })
123            .collect()
124    };
125
126    let cw20_id = app.store_code(cw20_base_contract());
127    let cw20_stake_id = app.store_code(cw20_stake_contract());
128    let staked_balances_voting_id = app.store_code(cw20_staked_balances_voting_contract());
129    let core_contract_id = app.store_code(dao_dao_contract());
130
131    let instantiate_core = dao_interface::msg::InstantiateMsg {
132        dao_uri: None,
133        admin: None,
134        name: "DAO DAO".to_string(),
135        description: "A DAO that builds DAOs".to_string(),
136        image_url: None,
137        automatically_add_cw20s: true,
138        automatically_add_cw721s: false,
139        voting_module_instantiate_info: ModuleInstantiateInfo {
140            code_id: staked_balances_voting_id,
141            msg: to_json_binary(&dao_voting_cw20_staked::msg::InstantiateMsg {
142                active_threshold: None,
143                token_info: dao_voting_cw20_staked::msg::TokenInfo::New {
144                    code_id: cw20_id,
145                    label: "DAO DAO governance token.".to_string(),
146                    name: "DAO DAO".to_string(),
147                    symbol: "DAO".to_string(),
148                    decimals: 6,
149                    initial_balances: initial_balances.clone(),
150                    marketing: None,
151                    staking_code_id: cw20_stake_id,
152                    unstaking_duration: Some(Duration::Height(6)),
153                    initial_dao_balance: None,
154                },
155            })
156            .unwrap(),
157            admin: None,
158            funds: vec![],
159            label: "DAO DAO voting module".to_string(),
160        },
161        proposal_modules_instantiate_info: vec![ModuleInstantiateInfo {
162            code_id: governance_code_id,
163            label: "DAO DAO governance module.".to_string(),
164            admin: Some(Admin::CoreModule {}),
165            msg: governance_instantiate,
166            funds: vec![],
167        }],
168        initial_items: None,
169    };
170
171    let core_addr = app
172        .instantiate_contract(
173            core_contract_id,
174            Addr::unchecked(CREATOR_ADDR),
175            &instantiate_core,
176            &[],
177            "DAO DAO",
178            None,
179        )
180        .unwrap();
181
182    let gov_state: dao_interface::query::DumpStateResponse = app
183        .wrap()
184        .query_wasm_smart(
185            core_addr.clone(),
186            &dao_interface::msg::QueryMsg::DumpState {},
187        )
188        .unwrap();
189    let voting_module = gov_state.voting_module;
190
191    let staking_contract: Addr = app
192        .wrap()
193        .query_wasm_smart(
194            voting_module.clone(),
195            &dao_voting_cw20_staked::msg::QueryMsg::StakingContract {},
196        )
197        .unwrap();
198    let token_contract: Addr = app
199        .wrap()
200        .query_wasm_smart(
201            voting_module,
202            &dao_interface::voting::Query::TokenContract {},
203        )
204        .unwrap();
205
206    // Stake all the initial balances.
207    for Cw20Coin { address, amount } in initial_balances {
208        app.execute_contract(
209            Addr::unchecked(address),
210            token_contract.clone(),
211            &cw20::Cw20ExecuteMsg::Send {
212                contract: staking_contract.to_string(),
213                amount,
214                msg: to_json_binary(&cw20_stake::msg::ReceiveMsg::Stake {}).unwrap(),
215            },
216            &[],
217        )
218        .unwrap();
219    }
220
221    // Update the block so that those staked balances appear.
222    app.update_block(|block| block.height += 1);
223
224    core_addr
225}
226
227pub fn instantiate_with_staking_active_threshold(
228    app: &mut App,
229    code_id: u64,
230    governance_instantiate: Binary,
231    initial_balances: Option<Vec<Cw20Coin>>,
232    active_threshold: Option<ActiveThreshold>,
233) -> Addr {
234    let cw20_id = app.store_code(cw20_base_contract());
235    let cw20_staking_id = app.store_code(cw20_stake_contract());
236    let governance_id = app.store_code(dao_dao_contract());
237    let votemod_id = app.store_code(cw20_staked_balances_voting_contract());
238
239    let initial_balances = initial_balances.unwrap_or_else(|| {
240        vec![
241            Cw20Coin {
242                address: "blob".to_string(),
243                amount: Uint128::new(100_000_000),
244            },
245            Cw20Coin {
246                address: "blue".to_string(),
247                amount: Uint128::new(100_000_000),
248            },
249        ]
250    });
251
252    let governance_instantiate = dao_interface::msg::InstantiateMsg {
253        dao_uri: None,
254        admin: None,
255        name: "DAO DAO".to_string(),
256        description: "A DAO that builds DAOs".to_string(),
257        image_url: None,
258        automatically_add_cw20s: true,
259        automatically_add_cw721s: true,
260        voting_module_instantiate_info: ModuleInstantiateInfo {
261            code_id: votemod_id,
262            msg: to_json_binary(&dao_voting_cw20_staked::msg::InstantiateMsg {
263                token_info: dao_voting_cw20_staked::msg::TokenInfo::New {
264                    code_id: cw20_id,
265                    label: "DAO DAO governance token".to_string(),
266                    name: "DAO".to_string(),
267                    symbol: "DAO".to_string(),
268                    decimals: 6,
269                    initial_balances,
270                    marketing: None,
271                    staking_code_id: cw20_staking_id,
272                    unstaking_duration: None,
273                    initial_dao_balance: None,
274                },
275                active_threshold,
276            })
277            .unwrap(),
278            admin: Some(Admin::CoreModule {}),
279            funds: vec![],
280            label: "DAO DAO voting module".to_string(),
281        },
282        proposal_modules_instantiate_info: vec![ModuleInstantiateInfo {
283            code_id,
284            msg: governance_instantiate,
285            admin: Some(Admin::CoreModule {}),
286            funds: vec![],
287            label: "DAO DAO governance module".to_string(),
288        }],
289        initial_items: None,
290    };
291
292    app.instantiate_contract(
293        governance_id,
294        Addr::unchecked(CREATOR_ADDR),
295        &governance_instantiate,
296        &[],
297        "DAO DAO",
298        None,
299    )
300    .unwrap()
301}
302
303pub fn instantiate_with_cw4_groups_governance(
304    app: &mut App,
305    core_code_id: u64,
306    proposal_module_instantiate: Binary,
307    initial_weights: Option<Vec<Cw20Coin>>,
308) -> Addr {
309    let cw4_id = app.store_code(cw4_group_contract());
310    let core_id = app.store_code(dao_dao_contract());
311    let votemod_id = app.store_code(dao_voting_cw4_contract());
312
313    let initial_weights = initial_weights.unwrap_or_default();
314
315    // Remove duplicates so that we can test duplicate voting.
316    let initial_weights: Vec<cw4::Member> = {
317        let mut already_seen = vec![];
318        initial_weights
319            .into_iter()
320            .filter(|Cw20Coin { address, .. }| {
321                if already_seen.contains(address) {
322                    false
323                } else {
324                    already_seen.push(address.clone());
325                    true
326                }
327            })
328            .map(|Cw20Coin { address, amount }| cw4::Member {
329                addr: address,
330                weight: amount.u128() as u64,
331            })
332            .collect()
333    };
334
335    let governance_instantiate = dao_interface::msg::InstantiateMsg {
336        dao_uri: None,
337        admin: None,
338        name: "DAO DAO".to_string(),
339        description: "A DAO that builds DAOs".to_string(),
340        image_url: None,
341        automatically_add_cw20s: true,
342        automatically_add_cw721s: true,
343        voting_module_instantiate_info: ModuleInstantiateInfo {
344            code_id: votemod_id,
345            msg: to_json_binary(&dao_voting_cw4::msg::InstantiateMsg {
346                group_contract: GroupContract::New {
347                    cw4_group_code_id: cw4_id,
348                    initial_members: initial_weights,
349                },
350            })
351            .unwrap(),
352            admin: Some(Admin::CoreModule {}),
353            funds: vec![],
354            label: "DAO DAO voting module".to_string(),
355        },
356        proposal_modules_instantiate_info: vec![ModuleInstantiateInfo {
357            code_id: core_code_id,
358            msg: proposal_module_instantiate,
359            admin: Some(Admin::CoreModule {}),
360            funds: vec![],
361            label: "DAO DAO governance module".to_string(),
362        }],
363        initial_items: None,
364    };
365
366    let addr = app
367        .instantiate_contract(
368            core_id,
369            Addr::unchecked(CREATOR_ADDR),
370            &governance_instantiate,
371            &[],
372            "DAO DAO",
373            None,
374        )
375        .unwrap();
376
377    // Update the block so that weights appear.
378    app.update_block(|block| block.height += 1);
379
380    addr
381}