ownable_std/
lib.rs

1use cosmwasm_std::{Addr, Api, BlockInfo, CanonicalAddr, ContractInfo, Empty, Env, MemoryStorage, OwnedDeps, Querier, RecoverPubkeyError, StdError, StdResult, Timestamp, VerificationError, Order, Storage, Uint128, Response};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use serde_json::to_string;
5use serde_with::serde_as;
6use std::collections::HashMap;
7use std::marker::PhantomData;
8use wasm_bindgen::{JsValue, JsError};
9
10const CANONICAL_LENGTH: usize = 54;
11
12pub fn create_env() -> Env {
13    create_ownable_env(String::new(), None)
14}
15
16pub fn create_ownable_env(chain_id: impl Into<String>, time: Option<Timestamp>) -> Env {
17    Env {
18        block: BlockInfo {
19            height: 0,
20            time: time.unwrap_or_else(|| Timestamp::from_seconds(0)),
21            chain_id: chain_id.into(),
22        },
23        contract: ContractInfo {
24            address: Addr::unchecked(""),
25        },
26        transaction: None,
27    }
28}
29
30pub fn load_owned_deps(state_dump: Option<IdbStateDump>) -> OwnedDeps<MemoryStorage, EmptyApi, EmptyQuerier, Empty> {
31    match state_dump {
32        None => OwnedDeps {
33            storage: MemoryStorage::default(),
34            api: EmptyApi::default(),
35            querier: EmptyQuerier::default(),
36            custom_query_type: PhantomData,
37        },
38        Some(dump) => {
39            let idb_storage = IdbStorage::load(dump);
40            OwnedDeps {
41                storage: idb_storage.storage,
42                api: EmptyApi::default(),
43                querier: EmptyQuerier::default(),
44                custom_query_type: PhantomData,
45            }
46        }
47    }
48}
49
50/// returns a hex color in string format from a hash
51pub fn get_random_color(hash: String) -> String {
52    let (red, green, blue) = derive_rgb_values(hash);
53    rgb_hex(red, green, blue)
54}
55
56/// takes a b58 hash and derives a seemingly-random rgb tuple
57pub fn derive_rgb_values(hash: String) -> (u8, u8, u8) {
58    let mut decoded_hash = bs58::decode(&hash).into_vec().unwrap();
59    decoded_hash.reverse();
60    (decoded_hash[0], decoded_hash[1], decoded_hash[2])
61}
62
63/// takes three u8 values representing rgb values (0-255)
64/// and returns a hex string
65pub fn rgb_hex(r: u8, g: u8, b: u8) -> String {
66    format!("#{:02X}{:02X}{:02X}", r, g, b)
67}
68
69/// takes a cw MemoryStorage and Response and returns a JsValue
70/// response that contains the memory state dump and response
71/// result
72pub fn get_json_response(storage: MemoryStorage, response: Response) -> Result<JsValue, JsError> {
73    let state_dump= IdbStateDump::from(storage);
74    let ownable_state = to_string(&response)?;
75    let response_map = js_sys::Map::new();
76    response_map.set(
77        &JsValue::from_str("mem"),
78        &JsValue::from(to_string(&state_dump)?)
79    );
80    response_map.set(
81        &JsValue::from_str("result"),
82        &JsValue::from(ownable_state)
83    );
84    Ok(JsValue::from(response_map))
85}
86
87pub struct IdbStorage {
88    pub storage: MemoryStorage,
89}
90
91impl IdbStorage {
92    pub fn load(idb: IdbStateDump) -> Self {
93        let mut store = IdbStorage {
94            storage: MemoryStorage::new(),
95        };
96        store.load_to_mem_storage(idb);
97        store
98    }
99
100    /// takes a IdbStateDump and loads the values into MemoryStorage
101    pub fn load_to_mem_storage(&mut self, idb_state: IdbStateDump) {
102        for (k, v) in idb_state.state_dump.into_iter() {
103            self.storage.set(&k, &v);
104        }
105    }
106}
107
108#[serde_as]
109#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
110pub struct IdbStateDump {
111    // map of the indexed db key value pairs of the state object store
112    #[serde_as(as = "Vec<(_, _)>")]
113    pub state_dump: HashMap<Vec<u8>, Vec<u8>>,
114}
115
116impl IdbStateDump {
117    /// generates a state dump from all key-value pairs in MemoryStorage
118    pub fn from(store: MemoryStorage) -> IdbStateDump {
119        let mut state: HashMap<Vec<u8>, Vec<u8>> = HashMap::new();
120
121        for (key, value) in store.range(None,None, Order::Ascending) {
122            state.insert(key, value);
123        }
124        IdbStateDump {
125            state_dump: state,
126        }
127    }
128}
129
130// EmptyApi that is meant to conform the traits by the cosmwasm standard contract syntax. The functions of this implementation are not meant to be used or produce any sensible results.
131#[derive(Copy, Clone)]
132pub struct EmptyApi {
133    /// Length of canonical addresses created with this API. Contracts should not make any assumtions
134    /// what this value is.
135    canonical_length: usize,
136}
137
138impl Default for EmptyApi {
139    fn default() -> Self {
140        EmptyApi {
141            canonical_length: CANONICAL_LENGTH,
142        }
143    }
144}
145
146impl Api for EmptyApi {
147    fn addr_validate(&self, human: &str) -> StdResult<Addr> {
148        self.addr_canonicalize(human).map(|_canonical| ())?;
149        Ok(Addr::unchecked(human))
150    }
151
152    fn addr_canonicalize(&self, human: &str) -> StdResult<CanonicalAddr> {
153        // Dummy input validation. This is more sophisticated for formats like bech32, where format and checksum are validated.
154        if human.len() < 3 {
155            return Err(StdError::generic_err(
156                "Invalid input: human address too short",
157            ));
158        }
159        if human.len() > self.canonical_length {
160            return Err(StdError::generic_err(
161                "Invalid input: human address too long",
162            ));
163        }
164
165        let mut out = Vec::from(human);
166
167        // pad to canonical length with NULL bytes
168        out.resize(self.canonical_length, 0x00);
169        // // content-dependent rotate followed by shuffle to destroy
170        // // the most obvious structure (https://github.com/CosmWasm/cosmwasm/issues/552)
171        // let rotate_by = digit_sum(&out) % self.canonical_length;
172        // out.rotate_left(rotate_by);
173        // for _ in 0..SHUFFLES_ENCODE {
174        //     out = riffle_shuffle(&out);
175        // }
176        Ok(out.into())
177    }
178
179    fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult<Addr> {
180        if canonical.len() != self.canonical_length {
181            return Err(StdError::generic_err(
182                "Invalid input: canonical address length not correct",
183            ));
184        }
185
186        let tmp: Vec<u8> = canonical.clone().into();
187        // // Shuffle two more times which restored the original value (24 elements are back to original after 20 rounds)
188        // for _ in 0..SHUFFLES_DECODE {
189        //     tmp = riffle_shuffle(&tmp);
190        // }
191        // // Rotate back
192        // let rotate_by = digit_sum(&tmp) % self.canonical_length;
193        // tmp.rotate_right(rotate_by);
194        // Remove NULL bytes (i.e. the padding)
195        let trimmed = tmp.into_iter().filter(|&x| x != 0x00).collect();
196        // decode UTF-8 bytes into string
197        let human = String::from_utf8(trimmed)?;
198        Ok(Addr::unchecked(human))
199    }
200
201    fn secp256k1_verify(
202        &self,
203        _message_hash: &[u8],
204        _signature: &[u8],
205        _public_key: &[u8],
206    ) -> Result<bool, VerificationError> {
207        Err(VerificationError::unknown_err(0))
208    }
209
210    fn secp256k1_recover_pubkey(
211        &self,
212        _message_hash: &[u8],
213        _signature: &[u8],
214        _recovery_param: u8,
215    ) -> Result<Vec<u8>, RecoverPubkeyError> {
216        Err(RecoverPubkeyError::unknown_err(0))
217    }
218
219    fn ed25519_verify(
220        &self,
221        _message: &[u8],
222        _signature: &[u8],
223        _public_key: &[u8],
224    ) -> Result<bool, VerificationError> {
225        Ok(true)
226    }
227
228    fn ed25519_batch_verify(
229        &self,
230        _messages: &[&[u8]],
231        _signatures: &[&[u8]],
232        _public_keys: &[&[u8]],
233    ) -> Result<bool, VerificationError> {
234        Ok(true)
235    }
236
237    fn debug(&self, message: &str) {
238        println!("{}", message);
239    }
240}
241
242/// Empty Querier that is meant to conform the traits expected by the cosmwasm standard contract syntax. It should not be used whatsoever
243#[derive(Default)]
244pub struct EmptyQuerier {}
245
246impl Querier for EmptyQuerier {
247    fn raw_query(&self, _bin_request: &[u8]) -> cosmwasm_std::QuerierResult {
248        todo!()
249    }
250}
251
252// from github.com/CosmWasm/cw-nfts/blob/main/contracts/cw721-metadata-onchain
253#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug, Default)]
254pub struct Metadata {
255    pub image: Option<String>,
256    pub image_data: Option<String>,
257    pub external_url: Option<String>,
258    pub description: Option<String>,
259    pub name: Option<String>,
260    // pub attributes: Option<Vec<Trait>>,
261    pub background_color: Option<String>,
262    pub animation_url: Option<String>,
263    pub youtube_url: Option<String>,
264}
265
266#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
267#[serde(rename_all = "snake_case")]
268pub struct ExternalEventMsg {
269    // CAIP-2 format: <namespace + ":" + reference>
270    // e.g. ethereum: eip155:1
271    pub network: Option<String>,
272    pub event_type: String,
273    pub attributes: HashMap<String, String>,
274}
275
276#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
277pub struct OwnableInfo {
278    pub owner: Addr,
279    pub issuer: Addr,
280    pub ownable_type: Option<String>,
281}
282
283#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
284pub struct NFT {
285    pub network: String,    // eip155:1
286    pub id: Uint128,
287    pub address: String, // 0x341...
288    pub lock_service: Option<String>,
289}
290
291#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
292pub struct InfoResponse {
293    pub owner: Addr,
294    pub issuer: Addr,
295    pub nft: Option<NFT>,
296    pub ownable_type: Option<String>,
297}