apollo_cw_multi_test/addresses.rs
1//! # Implementation of address conversions and generators
2
3use anyhow::Result as AnyResult;
4use cosmwasm_std::{instantiate2_address, Addr, Api, CanonicalAddr, HexBinary, Storage};
5use sha2::digest::Update;
6use sha2::{Digest, Sha256};
7
8/// Common address generator interface.
9///
10/// The default implementation of this trait generates fully predictable
11/// addresses, no matter if [contract_address](AddressGenerator::contract_address)
12/// or [predictable_contract_address](AddressGenerator::predictable_contract_address) is used,
13/// but users should not make any assumptions about the value of the generated address.
14pub trait AddressGenerator {
15 /// Generates a _non-predictable_ contract address, just like the real-life chain
16 /// returns contract address after its instantiation.
17 /// Address generated by this function is returned as a result of processing
18 /// `WasmMsg::Instantiate` message.
19 ///
20 /// The default implementation generates a contract address based
21 /// on contract's instance identifier only.
22 ///
23 /// # Example
24 ///
25 /// ```
26 /// # use cosmwasm_std::testing::{MockApi, MockStorage};
27 /// # use cw_multi_test::{AddressGenerator, SimpleAddressGenerator};
28 /// # let api = MockApi::default();
29 /// # let mut storage = MockStorage::default();
30 /// struct MyAddressGenerator;
31 ///
32 /// impl AddressGenerator for MyAddressGenerator {}
33 ///
34 /// let my_address_generator = MyAddressGenerator{};
35 ///
36 /// let addr = my_address_generator.contract_address(&api, &mut storage, 100, 0).unwrap();
37 /// assert_eq!(addr.as_str(), "contract0");
38 ///
39 /// let addr = my_address_generator.contract_address(&api, &mut storage, 100, 1).unwrap();
40 /// assert_eq!(addr.as_str(), "contract1");
41 ///
42 /// let addr = my_address_generator.contract_address(&api, &mut storage, 200, 5).unwrap();
43 /// assert_eq!(addr.as_str(), "contract5");
44 ///
45 /// let addr = my_address_generator.contract_address(&api, &mut storage, 200, 6).unwrap();
46 /// assert_eq!(addr.as_str(), "contract6");
47 /// ```
48 fn contract_address(
49 &self,
50 _api: &dyn Api,
51 _storage: &mut dyn Storage,
52 _code_id: u64,
53 instance_id: u64,
54 ) -> AnyResult<Addr> {
55 Ok(Addr::unchecked(format!("contract{instance_id}")))
56 }
57
58 /// Generates a _predictable_ contract address, just like the real-life chain
59 /// returns contract address after its instantiation using `MsgInstantiateContract2` message.
60 /// Address generated by this function is returned as a result of processing
61 /// `WasmMsg::Instantiate2` message.
62 ///
63 /// The default implementation generates a contract address based on provided
64 /// creator address and salt.
65 ///
66 /// # Example
67 ///
68 /// ```
69 /// # use cosmwasm_std::Api;
70 /// # use cosmwasm_std::testing::{MockApi, MockStorage};
71 /// # use cw_multi_test::{AddressGenerator, SimpleAddressGenerator};
72 /// # let api = MockApi::default();
73 /// # let mut storage = MockStorage::default();
74 /// struct MyAddressGenerator;
75 ///
76 /// impl AddressGenerator for MyAddressGenerator {}
77 ///
78 /// let my_address_generator = MyAddressGenerator{};
79 ///
80 /// let creator1 = api.addr_canonicalize("creator1").unwrap();
81 /// let creator2 = api.addr_canonicalize("creator2").unwrap();
82 /// let salt1 = [0xc0,0xff,0xee];
83 /// let salt2 = [0xbe,0xef];
84 ///
85 /// let addr11 = my_address_generator.predictable_contract_address(&api, &mut storage, 1, 0, &[1], &creator1, &salt1).unwrap();
86 /// let addr12 = my_address_generator.predictable_contract_address(&api, &mut storage, 1, 0, &[1], &creator1, &salt2).unwrap();
87 /// let addr21 = my_address_generator.predictable_contract_address(&api, &mut storage, 1, 0, &[1], &creator2, &salt1).unwrap();
88 /// let addr22 = my_address_generator.predictable_contract_address(&api, &mut storage, 1, 0, &[1], &creator2, &salt2).unwrap();
89 ///
90 /// assert_ne!(addr11, addr12);
91 /// assert_ne!(addr11, addr21);
92 /// assert_ne!(addr11, addr22);
93 /// assert_ne!(addr12, addr21);
94 /// assert_ne!(addr12, addr22);
95 /// assert_ne!(addr21, addr22);
96 /// ```
97 fn predictable_contract_address(
98 &self,
99 _api: &dyn Api,
100 _storage: &mut dyn Storage,
101 _code_id: u64,
102 _instance_id: u64,
103 _checksum: &[u8],
104 creator: &CanonicalAddr,
105 salt: &[u8],
106 ) -> AnyResult<Addr> {
107 Ok(Addr::unchecked(format!(
108 "contract{creator}{}",
109 HexBinary::from(salt).to_hex()
110 )))
111 }
112}
113
114/// Default contract address generator used in [WasmKeeper](crate::WasmKeeper).
115pub struct SimpleAddressGenerator;
116
117impl AddressGenerator for SimpleAddressGenerator {}
118
119/// Address generator that mimics the [wasmd](https://github.com/CosmWasm/wasmd) behavior.
120///
121/// [MockAddressGenerator] implements [AddressGenerator] trait in terms of
122/// [`contract_address`](AddressGenerator::contract_address) and
123/// [`predictable_contract_address`](AddressGenerator::predictable_contract_address) functions:
124/// - `contract_address` generates non-predictable addresses for contracts,
125/// using the same algorithm as `wasmd`, see: [`BuildContractAddressClassic`] for details.
126/// - `predictable_contract_address` generates predictable addresses for contracts using
127/// [`instantiate2_address`] function defined in `cosmwasm-std`.
128///
129/// [`BuildContractAddressClassic`]:https://github.com/CosmWasm/wasmd/blob/3b6512c9f154995188ead84ab3bd9e034b49a0f3/x/wasm/keeper/addresses.go#L35-L41
130/// [`instantiate2_address`]:https://github.com/CosmWasm/cosmwasm/blob/8a652d7cd8071f71139deca6be8194ed4a278b2c/packages/std/src/addresses.rs#L309-L318
131#[derive(Default)]
132pub struct MockAddressGenerator;
133
134impl AddressGenerator for MockAddressGenerator {
135 /// Generates a _non-predictable_ contract address, like `wasmd` does it in real-life chain.
136 ///
137 /// Note that addresses generated by `wasmd` may change and users **should not**
138 /// rely on this value in any extend.
139 ///
140 /// Returns the contract address after its instantiation.
141 /// Address generated by this function is returned as a result
142 /// of processing `WasmMsg::Instantiate` message.
143 ///
144 /// **NOTES**
145 /// > 👉 The canonical address generated by this function is humanized using the
146 /// > `Api::addr_humanize` function, so the resulting value depends on used `Api` implementation.
147 /// > The following example uses Bech32 format for humanizing canonical addresses.
148 ///
149 /// > 👉 Do NOT use this function **directly** to generate a contract address,
150 /// > pass this address generator to `WasmKeeper`:
151 /// > `WasmKeeper::new().with_address_generator(MockAddressGenerator::default());`
152 ///
153 /// # Example
154 ///
155 /// ```
156 /// # use cosmwasm_std::testing::MockStorage;
157 /// # use cw_multi_test::AddressGenerator;
158 /// # use cw_multi_test::{MockAddressGenerator, MockApiBech32};
159 /// // use `Api` that implements Bech32 format
160 /// let api = MockApiBech32::new("juno");
161 /// // prepare mock storage
162 /// let mut storage = MockStorage::default();
163 /// // initialize the address generator
164 /// let address_generator = MockAddressGenerator::default();
165 /// // generate the address
166 /// let addr = address_generator.contract_address(&api, &mut storage, 1, 1).unwrap();
167 ///
168 /// assert_eq!(addr.to_string(),
169 /// "juno14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9skjuwg8");
170 /// ```
171 fn contract_address(
172 &self,
173 api: &dyn Api,
174 _storage: &mut dyn Storage,
175 code_id: u64,
176 instance_id: u64,
177 ) -> AnyResult<Addr> {
178 let canonical_addr = instantiate_address(code_id, instance_id);
179 Ok(Addr::unchecked(api.addr_humanize(&canonical_addr)?))
180 }
181
182 /// Generates a _predictable_ contract address, like `wasmd` does it in real-life chain.
183 ///
184 /// Returns a contract address after its instantiation.
185 /// Address generated by this function is returned as a result
186 /// of processing `WasmMsg::Instantiate2` message.
187 ///
188 /// **NOTES**
189 /// > 👉 The canonical address generated by this function is humanized using the
190 /// > `Api::addr_humanize` function, so the resulting value depends on used `Api` implementation.
191 /// > The following example uses Bech32 format for humanizing canonical addresses.
192 ///
193 /// > 👉 Do NOT use this function **directly** to generate a contract address,
194 /// > pass this address generator to WasmKeeper:
195 /// > `WasmKeeper::new().with_address_generator(MockAddressGenerator::default());`
196 ///
197 /// # Example
198 ///
199 /// ```
200 /// # use cosmwasm_std::Api;
201 /// # use cosmwasm_std::testing::MockStorage;
202 /// # use cw_multi_test::AddressGenerator;
203 /// # use cw_multi_test::{MockAddressGenerator, MockApiBech32};
204 /// // use `Api` that implements Bech32 format
205 /// let api = MockApiBech32::new("juno");
206 /// // prepare mock storage
207 /// let mut storage = MockStorage::default();
208 /// // initialize the address generator
209 /// let address_generator = MockAddressGenerator::default();
210 /// // checksum of the contract code base
211 /// let checksum = [0, 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10,
212 /// 11,12,13,14,15,16,17,18,19,20,21,
213 /// 22,23,24,25,26,27,28,29,30,31];
214 /// // creator address
215 /// let creator = api.addr_canonicalize(api.addr_make("creator").as_str()).unwrap();
216 /// // salt
217 /// let salt = [10,11,12];
218 /// // generate the address
219 /// let addr = address_generator
220 /// .predictable_contract_address(&api, &mut storage, 1, 1, &checksum, &creator, &salt)
221 /// .unwrap();
222 ///
223 /// assert_eq!(addr.to_string(),
224 /// "juno1sv3gjp85m3xxluxreruards8ruxk5ykys8qfljwrdj5tv8kqxuhsmlfyud");
225 /// ```
226 fn predictable_contract_address(
227 &self,
228 api: &dyn Api,
229 _storage: &mut dyn Storage,
230 _code_id: u64,
231 _instance_id: u64,
232 checksum: &[u8],
233
234 creator: &CanonicalAddr,
235 salt: &[u8],
236 ) -> AnyResult<Addr> {
237 let canonical_addr = instantiate2_address(checksum, creator, salt)?;
238 Ok(Addr::unchecked(api.addr_humanize(&canonical_addr)?))
239 }
240}
241
242/// Returns non-predictable contract address.
243///
244/// Address is generated using the same algorithm as [`BuildContractAddressClassic`]
245/// implementation in `wasmd`.
246///
247/// [`BuildContractAddressClassic`]:https://github.com/CosmWasm/wasmd/blob/3b6512c9f154995188ead84ab3bd9e034b49a0f3/x/wasm/keeper/addresses.go#L35-L41
248fn instantiate_address(code_id: u64, instance_id: u64) -> CanonicalAddr {
249 let mut key = Vec::<u8>::new();
250 key.extend_from_slice(b"wasm\0");
251 key.extend_from_slice(&code_id.to_be_bytes());
252 key.extend_from_slice(&instance_id.to_be_bytes());
253 let module = Sha256::digest("module".as_bytes());
254 Sha256::new()
255 .chain(module)
256 .chain(key)
257 .finalize()
258 .to_vec()
259 .into()
260}