use base64::prelude::{Engine, BASE64_STANDARD}; use color_eyre::{eyre::Error, owo_colors::OwoColorize, Result}; use regex::Regex; use serde::{Deserialize, Serialize}; use secretrs::{ grpc_clients::{ComputeQueryClient, RegistrationQueryClient}, proto::secret::compute::v1beta1::{QueryByContractAddressRequest, QuerySecretContractRequest}, utils::encryption::EnigmaUtils, }; const GRPC_URL: &str = "http://grpc.testnet.secretsaturn.net:9090"; const CONTRACT_ADDRESS: &str = "secret19gtpkk25r0c36gtlyrc6repd3q52ngmkpfszw3"; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { color_eyre::install()?; println!("\n{}", "Registration Module".underline().blue()); println!("Creating `registration` query client..."); let mut secret_registration = RegistrationQueryClient::connect(GRPC_URL).await?; let enclave_key_bytes = secret_registration.tx_key(()).await?.into_inner().key; let enclave_key = hex::encode(&enclave_key_bytes); println!("Enclave Public Key => {:>4}", enclave_key.bright_blue()); println!("\n{}", "Compute Module".underline().blue()); println!("Creating `compute` query client..."); let mut secret_compute = ComputeQueryClient::connect(GRPC_URL).await?; let request = QueryByContractAddressRequest { contract_address: CONTRACT_ADDRESS.to_string(), }; let code_hash = secret_compute .code_hash_by_contract_address(request) .await? .into_inner() .code_hash; let query = QueryMsg::TokenInfo {}; println!("Query => {}", serde_json::to_string(&query)?.green()); let mut io_key = [0u8; 32]; io_key.copy_from_slice(&enclave_key_bytes[0..32]); let enigma_utils = EnigmaUtils::from_io_key(None, io_key); let encrypted = enigma_utils.encrypt(&code_hash, &query)?; let nonce = encrypted.nonce(); let query = encrypted.into_inner(); let display_request = format!( "QuerySecretContractRequest {{ contract_address: \"{}\", query: \"{}\" }}", CONTRACT_ADDRESS, BASE64_STANDARD.encode(&query) ); println!("Request => {}", display_request.green()); let request = QuerySecretContractRequest { contract_address: CONTRACT_ADDRESS.to_string(), query, }; let response = match secret_compute.query_secret_contract(request).await { Ok(response) => { let response = response.into_inner(); let decrypted_bytes = enigma_utils.decrypt(&nonce, &response.data)?; let decrypted_b64_string = String::from_utf8(decrypted_bytes)?; let decoded_bytes = BASE64_STANDARD.decode(decrypted_b64_string)?; let data = String::from_utf8(decoded_bytes)?; println!("Response => {}", data.green()); assert_eq!( data.trim(), r#"{"token_info":{"name":"Amber","symbol":"AMBER","decimals":6,"total_supply":"8888000000"}}"# ); Ok(data) } Err(status) => { let error_message = status.message(); let re = Regex::new(r"encrypted: (.*?):").unwrap(); if let Some(caps) = re.captures(error_message) { let encrypted_bytes = BASE64_STANDARD.decode(&caps[1])?; let decrypted_bytes = enigma_utils.decrypt(&nonce, &encrypted_bytes)?; let decrypted_string = String::from_utf8(decrypted_bytes)?; Err(Error::msg(decrypted_string)) } else { Err(Error::msg(error_message.to_string())) } } }?; let deserialized: QueryAnswer = serde_json::from_str(response.trim())?; println!("Deserialized Response => {:?}", deserialized.green()); Ok(()) } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "snake_case")] pub enum QueryMsg { TokenInfo {}, MemberCode { address: String, key: String }, ValidCodes { codes: Vec }, } #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "snake_case")] pub enum QueryAnswer { TokenInfo { name: String, symbol: String, decimals: u8, total_supply: String, }, MemberCode { code: String, }, ValidCodes { codes: Vec, }, ViewingKeyError { msg: String, }, }