diff --git a/Cargo.lock b/Cargo.lock index a604438..dce62f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1662,11 +1662,9 @@ version = "2.4.0" dependencies = [ "cosmwasm-schema 1.1.11", "cw-denom", - "cw-multi-test", + "cw-hooks", "cw-paginate-storage 2.4.0", - "cw-storage-plus 1.2.0", "cw-utils 1.0.3", - "cw2 1.1.2", "cw20 1.1.2", "cw20-base 1.1.2", "cw4-group", @@ -1680,6 +1678,10 @@ dependencies = [ "dao-voting-cw4", "dao-voting-snip20-staked", "secret-cosmwasm-std", + "secret-cw2", + "secret-multi-test", + "secret-storage-plus", + "secret-toolkit", "thiserror", ] @@ -1689,10 +1691,6 @@ version = "2.4.0" dependencies = [ "cosmwasm-schema 1.1.11", "cw-denom", - "cw-multi-test", - "cw-storage-plus 1.2.0", - "cw-utils 1.0.3", - "cw2 1.1.2", "cw20 1.1.2", "cw20-base 1.1.2", "cw4-group", @@ -1707,6 +1705,11 @@ dependencies = [ "dao-voting-cw4", "dao-voting-snip20-staked", "secret-cosmwasm-std", + "secret-cw2", + "secret-multi-test", + "secret-storage-plus", + "secret-toolkit", + "secret-utils", ] [[package]] @@ -1735,9 +1738,6 @@ version = "2.4.0" dependencies = [ "cosmwasm-schema 1.1.11", "cw-denom", - "cw-multi-test", - "cw-utils 1.0.3", - "cw2 1.1.2", "cw20 1.1.2", "cw20-base 1.1.2", "cw4-group", @@ -1751,6 +1751,9 @@ dependencies = [ "dao-voting-cw4", "dao-voting-snip20-staked", "secret-cosmwasm-std", + "secret-cw2", + "secret-multi-test", + "secret-utils", ] [[package]] @@ -1790,6 +1793,7 @@ dependencies = [ "dao-voting-cw4", "schemars", "secret-cosmwasm-std", + "secret-cw-controllers", "secret-cw2", "secret-multi-test", "secret-storage-plus", @@ -1851,6 +1855,7 @@ dependencies = [ "schemars", "secret-cosmwasm-std", "secret-cosmwasm-storage", + "secret-cw-controllers", "secret-cw2", "secret-multi-test", "secret-storage-plus", @@ -1918,8 +1923,10 @@ version = "2.4.0" dependencies = [ "cosmwasm-schema 1.1.11", "cw4", + "dao-interface", "secret-cosmwasm-std", "secret-cw-controllers", + "secret-toolkit", ] [[package]] @@ -2039,16 +2046,18 @@ version = "2.4.0" dependencies = [ "cosmwasm-schema 1.1.11", "cw4", - "cw4-group", "dao-dao-macros", "dao-interface", + "schemars", "secret-cosmwasm-std", "secret-cosmwasm-storage", + "secret-cw-controllers", "secret-cw2", "secret-multi-test", "secret-storage-plus", "secret-toolkit", "secret-utils", + "serde", "thiserror", ] @@ -4348,7 +4357,6 @@ dependencies = [ "serde", "sha2 0.9.9", "snafu", - "snip20-reference-impl", "subtle", "thiserror", ] @@ -4373,7 +4381,6 @@ dependencies = [ "secret-toolkit", "secret-utils", "serde", - "snip20-reference-impl", "snip20-stake", "stake-cw20-external-rewards", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 81da740..7ea2c42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,6 @@ members = [ # "./contracts/test/*", "./contracts/voting/*", "./packages/*", - "./snip20-reference-impl/", - "./snip721-reference-impl/", # "./manager/" # "ci/*", @@ -169,6 +167,6 @@ schemars = "0.8.12" serde = { version = "1.0.158", default-features = false, features = ["derive"] } thiserror = { version = "1.0.21" } cosmwasm-schema = { git = "https://github.com/scrtlabs/cosmwasm/", branch = "secret" } -snip20-reference-impl = { path = "./snip20-reference-impl/" } -snip721-reference-impl = { path = "./snip721-reference-impl/" } +snip20-reference-impl = { path = "./contracts/external/snip20-reference-impl/" } +snip721-reference-impl = { path = "./contracts/external/snip721-reference-impl/" } cosmos-sdk-proto = { version = "0.20.0", default-features = false } diff --git a/contracts/dao-dao-core/src/contract.rs b/contracts/dao-dao-core/src/contract.rs index 69d7d13..5179b92 100644 --- a/contracts/dao-dao-core/src/contract.rs +++ b/contracts/dao-dao-core/src/contract.rs @@ -24,6 +24,7 @@ use secret_cw2::{get_contract_version, set_contract_version, ContractVersion}; use secret_cw_controllers::ReplyEvent; use secret_toolkit::{serialization::Json, storage::Keymap, utils::HandleCallback}; use secret_utils::{parse_reply_event_for_contract_address, Duration}; +use snip20_reference_impl::msg::ExecuteAnswer; use crate::state::{ ACTIVE_PROPOSAL_MODULE_COUNT, ADMIN, CONFIG, ITEMS, NOMINATED_ADMIN, PAUSED, PROPOSAL_MODULES, @@ -462,12 +463,14 @@ pub fn execute_update_snip20_list( do_update_addr_list(deps, SNIP20_LIST, to_add, to_remove, |addr, deps| { // Perform a balance query here as this is the query performed // by the `Cw20Balances` query. - let viewing_key = TOKEN_VIEWING_KEY.load(deps.storage).unwrap_or_default(); + let viewing_key = TOKEN_VIEWING_KEY + .get(deps.storage, &addr) + .unwrap_or_default(); let snip20_code_hash = SNIP20_CODE_HASH.load(deps.storage)?; - let _info: snip20_reference_impl::msg::Balance = deps.querier.query_wasm_smart( + let _info: secret_toolkit::snip20::query::Balance = deps.querier.query_wasm_smart( snip20_code_hash, addr, - &snip20_reference_impl::msg::QueryMsg::Balance { + &secret_toolkit::snip20::QueryMsg::Balance { address: env.contract.address.to_string(), key: viewing_key, }, @@ -489,10 +492,10 @@ pub fn execute_update_snip721_list( } do_update_addr_list(deps, SNIP721_LIST, to_add, to_remove, |addr, deps| { let snip721_code_hash = SNIP721_CODE_HASH.load(deps.storage)?; - let _info: snip721_reference_impl::msg::ContractInfo = deps.querier.query_wasm_smart( + let _info: secret_toolkit::snip721::query::ContractInfo = deps.querier.query_wasm_smart( snip721_code_hash, addr, - &snip721_reference_impl::msg::QueryMsg::ContractInfo {}, + &secret_toolkit::snip721::QueryMsg::ContractInfo {}, )?; Ok(()) })?; @@ -572,27 +575,37 @@ pub fn execute_receive_snip20( if !config.automatically_add_snip20s { Ok(Response::new()) } else { - let snip20_code_hash = SNIP20_CODE_HASH.load(deps.storage)?; - // Create Snip20 Token viewing key - let gen_viewing_key_msg = snip20_msg::Snip20ExecuteMsg::CreateViewingKey { - entropy: "entropy".to_string(), - padding: None, - }; - let reply_id = - REPLY_IDS.add_event(deps.storage, ReplyEvent::Snip20ModuleCreateViewingKey {})?; - let submsg = SubMsg::reply_always( - gen_viewing_key_msg.to_cosmos_msg( - snip20_code_hash.clone(), - sender.clone().to_string(), - None, - )?, - reply_id, - ); - SNIP20_LIST.insert(deps.storage, &sender.clone(), &Empty {})?; - Ok(Response::new() - .add_attribute("action", "receive_snip20") - .add_attribute("token", sender) - .add_submessage(submsg)) + let viewing_key = TOKEN_VIEWING_KEY + .get(deps.storage, &sender) + .unwrap_or_default(); + if viewing_key.is_empty() { + let snip20_code_hash = SNIP20_CODE_HASH.load(deps.storage)?; + // Create Snip20 Token viewing key + let gen_viewing_key_msg = snip20_msg::Snip20ExecuteMsg::CreateViewingKey { + entropy: "entropy".to_string(), + padding: None, + }; + let reply_id = + REPLY_IDS.add_event(deps.storage, ReplyEvent::Snip20ModuleCreateViewingKey {})?; + let submsg = SubMsg::reply_always( + gen_viewing_key_msg.to_cosmos_msg( + snip20_code_hash.clone(), + sender.clone().to_string(), + None, + )?, + reply_id, + ); + SNIP20_LIST.insert(deps.storage, &sender.clone(), &Empty {})?; + Ok(Response::new() + .add_attribute("action", "receive_snip20") + .add_attribute("token", sender) + .add_submessage(submsg)) + } else { + SNIP20_LIST.insert(deps.storage, &sender.clone(), &Empty {})?; + Ok(Response::new() + .add_attribute("action", "receive_snip20") + .add_attribute("token", sender)) + } } } @@ -643,9 +656,11 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::ProposalModuleCount {} => query_proposal_module_count(deps), QueryMsg::TotalPowerAtHeight { height } => query_total_power_at_height(deps, height), QueryMsg::VotingModule {} => query_voting_module(deps), - QueryMsg::VotingPowerAtHeight { address, height } => { - query_voting_power_at_height(deps, address, height) - } + QueryMsg::VotingPowerAtHeight { + address, + height, + key, + } => query_voting_power_at_height(deps, address, key, height), QueryMsg::ActiveProposalModules { start_after, limit } => { query_active_proposal_modules(deps, start_after, limit) } @@ -715,13 +730,8 @@ pub fn query_proposal_modules( } } if start.is_none() { - res.push(ProposalModule { - address: module.address, - prefix: module.prefix, - status: module.status, - code_hash: module.code_hash, - }); - if res.len() >= limit.unwrap() as usize { + res.push(module); + if res.len() >= limit.unwrap_or_default() as usize { break; // Break out of loop if limit reached } } @@ -749,13 +759,8 @@ pub fn query_active_proposal_modules( } } if start.is_none() { - res.push(ProposalModule { - address: module.clone().address, - prefix: module.clone().prefix, - status: module.clone().status, - code_hash: module.code_hash, - }); - if res.len() >= limit.unwrap() as usize { + res.push(module); + if res.len() >= limit.unwrap_or_default() as usize { break; // Break out of loop if limit reached } } @@ -818,13 +823,18 @@ pub fn query_dump_state(deps: Deps, env: Env) -> StdResult { pub fn query_voting_power_at_height( deps: Deps, address: String, + key: String, height: Option, ) -> StdResult { let voting_module = VOTING_MODULE.load(deps.storage)?; let voting_power: voting::VotingPowerAtHeightResponse = deps.querier.query_wasm_smart( voting_module.code_hash, voting_module.addr, - &voting::Query::VotingPowerAtHeight { height, address }, + &voting::Query::VotingPowerAtHeight { + height, + address, + key, + }, )?; to_binary(&voting_power) } @@ -869,7 +879,7 @@ pub fn query_list_items( } if start.is_none() { res.push((key.clone(), value.clone())); // Collect the key-value pair - if res.len() >= limit.unwrap() as usize { + if res.len() >= limit.unwrap_or_default() as usize { break; // Break out of loop if limit reached } } @@ -903,7 +913,7 @@ pub fn query_cw20_list( } if start.is_none() { res.push(addr.to_string()); - if res.len() >= limit.unwrap() as usize { + if res.len() >= limit.unwrap_or_default() as usize { break; // Break out of loop if limit reached } } @@ -937,7 +947,7 @@ pub fn query_cw721_list( } if start.is_none() { res.push(addr.to_string()); - if res.len() >= limit.unwrap() as usize { + if res.len() >= limit.unwrap_or_default() as usize { break; // Break out of loop if limit reached } } @@ -965,7 +975,7 @@ pub fn query_cw20_balances( } if start.is_none() { res.push(addr.to_string()); - if res.len() >= limit.unwrap() as usize { + if res.len() >= limit.unwrap_or_default() as usize { break; // Break out of loop if limit reached } } @@ -974,8 +984,10 @@ pub fn query_cw20_balances( let balances = res .into_iter() .map(|addr| { - let viewing_key = TOKEN_VIEWING_KEY.load(deps.storage).unwrap_or_default(); - let balance: snip20_reference_impl::msg::Balance = deps.querier.query_wasm_smart( + let viewing_key = TOKEN_VIEWING_KEY + .get(deps.storage, &deps.api.addr_validate(&addr)?) + .unwrap_or_default(); + let balance: secret_toolkit::snip20::query::Balance = deps.querier.query_wasm_smart( snip20_code_hash.clone(), addr.clone(), &snip20_reference_impl::msg::QueryMsg::Balance { @@ -1016,7 +1028,7 @@ pub fn query_list_sub_daos( } if start.is_none() { subdaos.push((addr, subdao)); - if subdaos.len() >= limit.unwrap() as usize { + if subdaos.len() >= limit.unwrap_or_default() as usize { break; // Break out of loop if limit reached } } @@ -1130,9 +1142,15 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { // let mut token_viewing_key=TOKEN_VIEWING_KEY.load(deps.storage).unwrap_or_default(); - let data: snip20_reference_impl::msg::CreateViewingKeyResponse = + let addr = parse_reply_event_for_contract_address(res.events)?; + let token_addr = deps.api.addr_validate(&addr)?; + let data: snip20_reference_impl::msg::ExecuteAnswer = from_binary(&res.data.unwrap())?; - TOKEN_VIEWING_KEY.save(deps.storage, &data.key)?; + let mut viewing_key = String::new(); + if let ExecuteAnswer::CreateViewingKey { key } = data { + viewing_key = key; + } + TOKEN_VIEWING_KEY.insert(deps.storage, &token_addr, &viewing_key)?; Ok(Response::new().add_attribute("action", "create_token_viewing_key")) } SubMsgResult::Err(_) => Err(ContractError::TokenExecuteError {}), diff --git a/contracts/dao-dao-core/src/state.rs b/contracts/dao-dao-core/src/state.rs index 1f35b1c..fe2bb83 100644 --- a/contracts/dao-dao-core/src/state.rs +++ b/contracts/dao-dao-core/src/state.rs @@ -38,7 +38,7 @@ pub const VOTING_MODULE: Item = Item::new("voting_module"); /// The proposal modules associated with this contract. /// When we change the data format of this map, we update the key (previously "proposal_modules") /// to create a new namespace for the changed state. -pub static PROPOSAL_MODULES: Keymap = +pub const PROPOSAL_MODULES: Keymap = Keymap::new(b"proposal_modules_v2"); /// The count of active proposal modules associated with this contract. @@ -48,7 +48,7 @@ pub const ACTIVE_PROPOSAL_MODULE_COUNT: Item = Item::new("active_proposal_m pub const TOTAL_PROPOSAL_MODULE_COUNT: Item = Item::new("total_proposal_module_count"); // General purpose KV store for DAO associated state. -pub static ITEMS: Keymap = Keymap::new(b"items"); +pub const ITEMS: Keymap = Keymap::new(b"items"); /// Set of snip20 tokens that have been registered with this contract's /// treasury. @@ -60,7 +60,7 @@ pub const SNIP721_LIST: Keymap = Keymap::new(b"snip721s"); /// List of SubDAOs associated to this DAO. Each SubDAO has an optional charter. pub const SUBDAO_LIST: Keymap = Keymap::new(b"sub_daos"); -pub const TOKEN_VIEWING_KEY: Item = Item::new("token_viewing_key"); +pub const TOKEN_VIEWING_KEY: Keymap = Keymap::new(b"token_viewing_key"); pub const SNIP20_CODE_HASH: Item = Item::new("snip20_code_hash"); pub const SNIP721_CODE_HASH: Item = Item::new("snip721_code_hash"); diff --git a/contracts/external/cw-admin-factory/Cargo.toml b/contracts/external/cw-admin-factory/Cargo.toml index f88dfcd..2fca8c9 100644 --- a/contracts/external/cw-admin-factory/Cargo.toml +++ b/contracts/external/cw-admin-factory/Cargo.toml @@ -24,6 +24,7 @@ secret-storage-plus = { workspace = true } secret-cw2 = { workspace = true } thiserror = { workspace = true } secret-utils = { workspace = true } +dao-interface ={ workspace = true } [dev-dependencies] cosmwasm-schema = { workspace = true } diff --git a/contracts/external/cw-admin-factory/src/contract.rs b/contracts/external/cw-admin-factory/src/contract.rs index 4c0ae1c..ace436d 100644 --- a/contracts/external/cw-admin-factory/src/contract.rs +++ b/contracts/external/cw-admin-factory/src/contract.rs @@ -1,10 +1,11 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, SubMsg, WasmMsg, + Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, SubMsg, WasmMsg,SubMsgResult }; +use dao_interface::state::ModuleInstantiateInfo; use secret_cw2::set_contract_version; -use secret_utils::parse_reply_instantiate_data; +use secret_utils::parse_reply_event_for_contract_address; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; @@ -34,34 +35,21 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::InstantiateContractWithSelfAdmin { - instantiate_msg: msg, - code_id, - code_hash, - label, - } => instantiate_contract(env, info, msg, code_id, code_hash,label), + ExecuteMsg::InstantiateContractWithSelfAdmin { module_info } => { + instantiate_contract(env, info, module_info) + } } } pub fn instantiate_contract( env: Env, - info: MessageInfo, - instantiate_msg: Binary, - code_id: u64, - code_hash: String, - label: String, + _info: MessageInfo, + module_info: ModuleInstantiateInfo, ) -> Result { // Instantiate the specified contract with factory as the admin. - let instantiate = WasmMsg::Instantiate { - admin: Some(env.contract.address.to_string()), - code_id, - code_hash, - msg: instantiate_msg, - funds: info.funds, - label, - }; + let msg = module_info.to_cosmos_msg(env.contract.address); - let msg = SubMsg::reply_on_success(instantiate, INSTANTIATE_CONTRACT_REPLY_ID); + let msg = SubMsg::reply_on_success(msg, INSTANTIATE_CONTRACT_REPLY_ID); Ok(Response::default() .add_attribute("action", "instantiate_cw_core") .add_submessage(msg)) @@ -75,19 +63,35 @@ pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { match msg.id { - INSTANTIATE_CONTRACT_REPLY_ID => { - let res = parse_reply_instantiate_data(msg)?; - let contract_addr = deps.api.addr_validate(&res.contract_address)?; - // Make the contract its own admin. - let msg = WasmMsg::UpdateAdmin { - contract_addr: contract_addr.to_string(), - admin: contract_addr.to_string(), - }; + INSTANTIATE_CONTRACT_REPLY_ID => match msg.result { + cosmwasm_std::SubMsgResult::Ok(res) => { + let address = parse_reply_event_for_contract_address(res.events)?; + let contract_addr = deps.api.addr_validate(&address)?; + // Make the contract its own admin. + let msg = WasmMsg::UpdateAdmin { + contract_addr: contract_addr.to_string(), + admin: contract_addr.to_string(), + }; - Ok(Response::default() - .add_attribute("set contract admin as itself", contract_addr) - .add_message(msg)) - } + Ok(Response::default() + .add_attribute("set contract admin as itself", contract_addr) + .add_message(msg)) + } + SubMsgResult::Err(err) => Err(ContractError::Std(StdError::GenericErr { msg: err })), + }, + // { + // let res = parse_reply_instantiate_data(msg)?; + // let contract_addr = deps.api.addr_validate(&res.contract_address)?; + // // Make the contract its own admin. + // let msg = WasmMsg::UpdateAdmin { + // contract_addr: contract_addr.to_string(), + // admin: contract_addr.to_string(), + // }; + + // Ok(Response::default() + // .add_attribute("set contract admin as itself", contract_addr) + // .add_message(msg)) + // } _ => Err(ContractError::UnknownReplyID {}), } } diff --git a/contracts/external/cw-admin-factory/src/msg.rs b/contracts/external/cw-admin-factory/src/msg.rs index 365487c..f5b1fcf 100644 --- a/contracts/external/cw-admin-factory/src/msg.rs +++ b/contracts/external/cw-admin-factory/src/msg.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::Binary; +use dao_interface::state::ModuleInstantiateInfo; #[cw_serde] pub struct InstantiateMsg {} @@ -8,12 +8,7 @@ pub struct InstantiateMsg {} pub enum ExecuteMsg { /// Instantiates the target contract with the provided instantiate message and code id and /// updates the contract's admin to be itself. - InstantiateContractWithSelfAdmin { - instantiate_msg: Binary, - code_id: u64, - code_hash: String, - label: String, - }, + InstantiateContractWithSelfAdmin { module_info: ModuleInstantiateInfo }, } #[cw_serde] diff --git a/contracts/external/cw-tokenfactory-issuer/src/msg.rs b/contracts/external/cw-tokenfactory-issuer/src/msg.rs index e158b1f..f949028 100644 --- a/contracts/external/cw-tokenfactory-issuer/src/msg.rs +++ b/contracts/external/cw-tokenfactory-issuer/src/msg.rs @@ -1,4 +1,3 @@ -use crate::state::BeforeSendHookInfo; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Coin, Uint128}; @@ -186,7 +185,7 @@ pub enum QueryMsg { /// - The address of the BeforeSendHook contract if configured. /// /// Response: BeforeSendHookInfo - #[returns(BeforeSendHookInfo)] + #[returns(crate::state::BeforeSendHookInfo)] BeforeSendHookInfo {}, } diff --git a/snip20-reference-impl/.cargo/config b/contracts/external/snip20-reference-impl/.cargo/config similarity index 100% rename from snip20-reference-impl/.cargo/config rename to contracts/external/snip20-reference-impl/.cargo/config diff --git a/contracts/external/snip20-reference-impl/.circleci/config.yml b/contracts/external/snip20-reference-impl/.circleci/config.yml new file mode 100644 index 0000000..161e927 --- /dev/null +++ b/contracts/external/snip20-reference-impl/.circleci/config.yml @@ -0,0 +1,52 @@ +version: 2.1 + +jobs: + build: + docker: + - image: rust:1.46 + steps: + - checkout + - run: + name: Version information + command: rustc --version; cargo --version; rustup --version + - restore_cache: + keys: + - v4-cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }} + - run: + name: Add wasm32 target + command: rustup target add wasm32-unknown-unknown + - run: + name: Build + command: make compile-optimized + - run: + name: Unit tests + env: RUST_BACKTRACE=1 + command: cargo unit-test --locked + - run: + name: Integration tests + command: cargo integration-test --locked + - run: + name: Format source code + command: cargo fmt + - run: + name: Build and run schema generator + command: cargo schema --locked + - run: + name: Ensure checked-in source code and schemas are up-to-date + command: | + CHANGES_IN_REPO=$(git status --porcelain) + if [[ -n "$CHANGES_IN_REPO" ]]; then + echo "Repository is dirty. Showing 'git status' and 'git --no-pager diff' for debugging now:" + git status && git --no-pager diff + exit 1 + fi + - save_cache: + paths: + - /usr/local/cargo/registry + - target/debug/.fingerprint + - target/debug/build + - target/debug/deps + - target/wasm32-unknown-unknown/release/.fingerprint + - target/wasm32-unknown-unknown/release/build + - target/wasm32-unknown-unknown/release/deps + key: v4-cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }} diff --git a/contracts/external/snip20-reference-impl/.github/dependabot.yml b/contracts/external/snip20-reference-impl/.github/dependabot.yml new file mode 100644 index 0000000..9ffcf5b --- /dev/null +++ b/contracts/external/snip20-reference-impl/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + # Maintain dependencies for Cargo + - package-ecosystem: "cargo" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/contracts/external/snip20-reference-impl/.github/workflows/test.yml b/contracts/external/snip20-reference-impl/.github/workflows/test.yml new file mode 100644 index 0000000..ef186b9 --- /dev/null +++ b/contracts/external/snip20-reference-impl/.github/workflows/test.yml @@ -0,0 +1,88 @@ +# Based on https://github.com/actions-rs/example/blob/master/.github/workflows/quickstart.yml + +on: [push, pull_request] + +name: Tests + +jobs: + + + unit-test: + name: Unit Tests + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + - name: Run Unit Tests + run: cargo unit-test + + integration-tests: + name: Integration Tests + runs-on: ubuntu-latest + services: + # Label used to access the service container + secretdev: + # Docker Hub image + options: --name secretdev + image: ghcr.io/scrtlabs/localsecret:v1.6.0-alpha.4 + volumes: + - "/home/runner/work/snip20-reference-impl/snip20-reference-impl/:/root/code" + ports: + # Opens tcp port + - 5000:5000 + - 9091:9091 + - 1317:1317 + + + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + - name: Compile WASM contract + run: | + sudo apt-get install -y binaryen + make compile-optimized && make compile-optimized-receiver +# - name: Compile WASM contract +# uses: actions-rs/cargo@v1 +# with: +# command: build +# args: --release --target wasm32-unknown-unknown --locked +# env: +# RUSTFLAGS: "-C link-arg=-s" + + - name: Run Integration Tests + run: | + ./tests/integration.sh + + lints: + name: Lints + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Run cargo fmt + run: cargo fmt --all -- --check + + - name: Run cargo clippy + run: cargo clippy -- -D warnings + + # TODO: we should check + # CHANGES_IN_REPO=$(git status --porcelain) + # after this, but I don't know how + - name: Generate Schema + run: cargo schema --locked diff --git a/snip20-reference-impl/.gitignore b/contracts/external/snip20-reference-impl/.gitignore similarity index 100% rename from snip20-reference-impl/.gitignore rename to contracts/external/snip20-reference-impl/.gitignore diff --git a/snip20-reference-impl/Cargo.toml b/contracts/external/snip20-reference-impl/Cargo.toml similarity index 99% rename from snip20-reference-impl/Cargo.toml rename to contracts/external/snip20-reference-impl/Cargo.toml index 8c56312..3fa1cb7 100644 --- a/snip20-reference-impl/Cargo.toml +++ b/contracts/external/snip20-reference-impl/Cargo.toml @@ -45,4 +45,3 @@ schemars = "0.8.12" serde = { version = "1.0.158", default-features = false, features = ["derive"] } base64 = "0.21.0" cosmwasm-schema = {workspace=true} - diff --git a/contracts/external/snip20-reference-impl/Developing.md b/contracts/external/snip20-reference-impl/Developing.md new file mode 100644 index 0000000..45baccd --- /dev/null +++ b/contracts/external/snip20-reference-impl/Developing.md @@ -0,0 +1,153 @@ +# Developing + +If you have recently created a contract with this template, you probably could use some +help on how to build and test the contract, as well as prepare it for production. This +file attempts to provide a brief overview, assuming you have installed a recent +version of Rust already (eg. 1.41+). + +## Prerequisites + +Before starting, make sure you have [rustup](https://rustup.rs/) along with a +recent `rustc` and `cargo` version installed. Currently, we are testing on 1.41+. + +And you need to have the `wasm32-unknown-unknown` target installed as well. + +You can check that via: + +```sh +rustc --version +cargo --version +rustup target list --installed +# if wasm32 is not listed above, run this +rustup target add wasm32-unknown-unknown +``` + +### Using macos? + +You'll need to install LLVM using Homebrew: +```sh +brew install llvm +echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.profile +echo 'export CC=/usr/local/opt/llvm/bin/clang' >> ~/.profile +echo 'export AR=/usr/local/opt/llvm/bin/llvm-ar' >> ~/.profile +source ~/.profile +``` + +## Compiling and running tests + +Now that you created your custom contract, make sure you can compile and run it before +making any changes. Go into the + +```sh +# this will produce a wasm build in ./target/wasm32-unknown-unknown/release/YOUR_NAME_HERE.wasm +cargo wasm + +# this runs unit tests with helpful backtraces +RUST_BACKTRACE=1 cargo unit-test + +# this runs integration tests with cranelift backend (uses rust stable) +cargo integration-test + +# this runs integration tests with singlepass backend (needs rust nightly) +cargo integration-test --no-default-features --features singlepass + +# auto-generate json schema +cargo schema +``` + +The wasmer engine, embedded in `cosmwasm-vm` supports multiple backends: +singlepass and cranelift. Singlepass has fast compile times and slower run times, +and supportes gas metering. It also requires rust `nightly`. This is used as default +when embedding `cosmwasm-vm` in `go-cosmwasm` and is needed to use if you want to +check the gas usage. + +However, when just building contacts, if you don't want to worry about installing +two rust toolchains, you can run all tests with cranelift. The integration tests +may take a small bit longer, but the results will be the same. The only difference +is that you can not check gas usage here, so if you wish to optimize gas, you must +switch to nightly and run with cranelift. + +### Understanding the tests + +The main code is in `src/contract.rs` and the unit tests there run in pure rust, +which makes them very quick to execute and give nice output on failures, especially +if you do `RUST_BACKTRACE=1 cargo unit-test`. + +However, we don't just want to test the logic rust, but also the compiled Wasm artifact +inside a VM. You can look in `tests/integration.rs` to see some examples there. They +load the Wasm binary into the vm and call the contract externally. Effort has been +made that the syntax is very similar to the calls in the native rust contract and +quite easy to code. In fact, usually you can just copy a few unit tests and modify +a few lines to make an integration test (this should get even easier in a future release). + +To run the latest integration tests, you need to explicitely rebuild the Wasm file with +`cargo wasm` and then run `cargo integration-test`. + +We consider testing critical for anything on a blockchain, and recommend to always keep +the tests up to date. While doing active development, it is often simplest to disable +the integration tests completely and iterate rapidly on the code in `contract.rs`, +both the logic and the tests. Once the code is finalized, you can copy over some unit +tests into the integration.rs and make the needed changes. This ensures the compiled +Wasm also behaves as desired in the real system. + +## Generating JSON Schema + +While the Wasm calls (`init`, `handle`, `query`) accept JSON, this is not enough +information to use it. We need to expose the schema for the expected messages to the +clients. You can generate this schema by calling `cargo schema`, which will output +4 files in `./schema`, corresponding to the 3 message types the contract accepts, +as well as the internal `State`. + +These files are in standard json-schema format, which should be usable by various +client side tools, either to auto-generate codecs, or just to validate incoming +json wrt. the defined schema. + +## Preparing the Wasm bytecode for production + +Before we upload it to a chain, we need to ensure the smallest output size possible, +as this will be included in the body of a transaction. We also want to have a +reproducible build process, so third parties can verify that the uploaded Wasm +code did indeed come from the claimed rust code. + +To solve both these issues, we have produced `rust-optimizer`, a docker image to +produce an extremely small build output in a consistent manner. The suggest way +to run it is this: + +```sh +docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/rust-optimizer:0.8.0 +``` + +We must mount the contract code to `/code`. You can use a absolute path instead +of `$(pwd)` if you don't want to `cd` to the directory first. The other two +volumes are nice for speedup. Mounting `/code/target` in particular is useful +to avoid docker overwriting your local dev files with root permissions. +Note the `/code/target` cache is unique for each contract being compiled to limit +interference, while the registry cache is global. + +This is rather slow compared to local compilations, especially the first compile +of a given contract. The use of the two volume caches is very useful to speed up +following compiles of the same contract. + +This produces a `contract.wasm` file in the current directory (which must be the root +directory of your rust project, the one with `Cargo.toml` inside). As well as +`hash.txt` containing the Sha256 hash of `contract.wasm`, and it will rebuild +your schema files as well. + +### Testing production build + +Once we have this compressed `contract.wasm`, we may want to ensure it is actually +doing everything it is supposed to (as it is about 4% of the original size). +If you update the "WASM" line in `tests/integration.rs`, it will run the integration +steps on the optimized build, not just the normal build. I have never seen a different +behavior, but it is nice to verify sometimes. + +```rust +static WASM: &[u8] = include_bytes!("../contract.wasm"); +``` + +Note that this is the same (deterministic) code you will be uploading to +a blockchain to test it out, as we need to shrink the size and produce a +clear mapping from wasm hash back to the source code. diff --git a/contracts/external/snip20-reference-impl/Importing.md b/contracts/external/snip20-reference-impl/Importing.md new file mode 100644 index 0000000..e367b65 --- /dev/null +++ b/contracts/external/snip20-reference-impl/Importing.md @@ -0,0 +1,62 @@ +# Importing + +In [Publishing](./Publishing.md), we discussed how you can publish your contract to the world. +This looks at the flip-side, how can you use someone else's contract (which is the same +question as how they will use your contract). Let's go through the various stages. + +## Verifying Artifacts + +Before using remote code, you most certainly want to verify it is honest. + +The simplest audit of the repo is to simply check that the artifacts in the repo +are correct. This involves recompiling the claimed source with the claimed builder +and validating that the locally compiled code (hash) matches the code hash that was +uploaded. This will verify that the source code is the correct preimage. Which allows +one to audit the original (Rust) source code, rather than looking at wasm bytecode. + +We have a script to do this automatic verification steps that can +easily be run by many individuals. Please check out +[`cosmwasm-verify`](https://github.com/CosmWasm/cosmwasm-verify/blob/master/README.md) +to see a simple shell script that does all these steps and easily allows you to verify +any uploaded contract. + +## Reviewing + +Once you have done the quick programatic checks, it is good to give at least a quick +look through the code. A glance at `examples/schema.rs` to make sure it is outputing +all relevant structs from `contract.rs`, and also ensure `src/lib.rs` is just the +default wrapper (nothing funny going on there). After this point, we can dive into +the contract code itself. Check the flows for the handle methods, any invariants and +permission checks that should be there, and a reasonable data storage format. + +You can dig into the contract as far as you want, but it is important to make sure there +are no obvious backdoors at least. + +## Decentralized Verification + +It's not very practical to do a deep code review on every dependency you want to use, +which is a big reason for the popularity of code audits in the blockchain world. We trust +some experts review in lieu of doing the work ourselves. But wouldn't it be nice to do this +in a decentralized manner and peer-review each other's contracts? Bringing in deeper domain +knowledge and saving fees. + +Luckily, there is an amazing project called [crev](https://github.com/crev-dev/cargo-crev/blob/master/cargo-crev/README.md) +that provides `A cryptographically verifiable code review system for the cargo (Rust) package manager`. + +I highly recommend that CosmWasm contract developers get set up with this. At minimum, we +can all add a review on a package that programmatically checked out that the json schemas +and wasm bytecode do match the code, and publish our claim, so we don't all rely on some +central server to say it validated this. As we go on, we can add deeper reviews on standard +packages. + +If you want to use `cargo-crev`, please follow their +[getting started guide](https://github.com/crev-dev/cargo-crev/blob/master/cargo-crev/src/doc/getting_started.md) +and once you have made your own *proof repository* with at least one *trust proof*, +please make a PR to the [`cawesome-wasm`]() repo with a link to your repo and +some public name or pseudonym that people know you by. This allows people who trust you +to also reuse your proofs. + +There is a [standard list of proof repos](https://github.com/crev-dev/cargo-crev/wiki/List-of-Proof-Repositories) +with some strong rust developers in there. This may cover dependencies like `serde` and `snafu` +but will not hit any CosmWasm-related modules, so we look to bootstrap a very focused +review community. diff --git a/contracts/external/snip20-reference-impl/LICENSE b/contracts/external/snip20-reference-impl/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/contracts/external/snip20-reference-impl/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/snip20-reference-impl/Makefile b/contracts/external/snip20-reference-impl/Makefile similarity index 100% rename from snip20-reference-impl/Makefile rename to contracts/external/snip20-reference-impl/Makefile diff --git a/contracts/external/snip20-reference-impl/NOTICE b/contracts/external/snip20-reference-impl/NOTICE new file mode 100644 index 0000000..f18150b --- /dev/null +++ b/contracts/external/snip20-reference-impl/NOTICE @@ -0,0 +1,13 @@ +Copyright 2020 Itzik + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/contracts/external/snip20-reference-impl/Publishing.md b/contracts/external/snip20-reference-impl/Publishing.md new file mode 100644 index 0000000..35f5212 --- /dev/null +++ b/contracts/external/snip20-reference-impl/Publishing.md @@ -0,0 +1,115 @@ +# Publishing Contracts + +This is an overview of how to publish the contract's source code in this repo. +We use Cargo's default registry [crates.io](https://crates.io/) for publishing contracts written in Rust. + +## Preparation + +Ensure the `Cargo.toml` file in the repo is properly configured. In particular, you want to +choose a name starting with `cw-`, which will help a lot finding CosmWasm contracts when +searching on crates.io. For the first publication, you will probably want version `0.1.0`. +If you have tested this on a public net already and/or had an audit on the code, +you can start with `1.0.0`, but that should imply some level of stability and confidence. +You will want entries like the following in `Cargo.toml`: + +```toml +name = "cw-escrow" +version = "0.1.0" +description = "Simple CosmWasm contract for an escrow with arbiter and timeout" +repository = "https://github.com/confio/cosmwasm-examples" +``` + +You will also want to add a valid [SPDX license statement](https://spdx.org/licenses/), +so others know the rules for using this crate. You can use any license you wish, +even a commercial license, but we recommend choosing one of the following, unless you have +specific requirements. + +* Permissive: [`Apache-2.0`](https://spdx.org/licenses/Apache-2.0.html#licenseText) or [`MIT`](https://spdx.org/licenses/MIT.html#licenseText) +* Copyleft: [`GPL-3.0-or-later`](https://spdx.org/licenses/GPL-3.0-or-later.html#licenseText) or [`AGPL-3.0-or-later`](https://spdx.org/licenses/AGPL-3.0-or-later.html#licenseText) +* Commercial license: `Commercial` (not sure if this works, I cannot find examples) + +It is also helpful to download the LICENSE text (linked to above) and store this +in a LICENSE file in your repo. Now, you have properly configured your crate for use +in a larger ecosystem. + +### Updating schema + +To allow easy use of the contract, we can publish the schema (`schema/*.json`) together +with the source code. + +```sh +cargo schema +``` + +Ensure you check in all the schema files, and make a git commit with the final state. +This commit will be published and should be tagged. Generally, you will want to +tag with the version (eg. `v0.1.0`), but in the `cosmwasm-examples` repo, we have +multiple contracts and label it like `escrow-0.1.0`. Don't forget a +`git push && git push --tags` + +### Note on build results + +Build results like Wasm bytecode or expected hash don't need to be updated since +the don't belong to the source publication. However, they are excluded from packaging +in `Cargo.toml` which allows you to commit them to your git repository if you like. + +```toml +exclude = ["contract.wasm", "hash.txt"] +``` + +A single source code can be built with multiple different optimizers, so +we should not make any strict assumptions on the tooling that will be used. + +## Publishing + +Now that your package is properly configured and all artifacts are committed, it +is time to share it with the world. +Please refer to the [complete instructions for any questions](https://rurust.github.io/cargo-docs-ru/crates-io.html), +but I will try to give a quick overview of the happy path here. + +### Registry + +You will need an account on [crates.io](https://crates.io) to publish a rust crate. +If you don't have one already, just click on "Log in with GitHub" in the top-right +to quickly set up a free account. Once inside, click on your username (top-right), +then "Account Settings". On the bottom, there is a section called "API Access". +If you don't have this set up already, create a new token and use `cargo login` +to set it up. This will now authenticate you with the `cargo` cli tool and allow +you to publish. + +### Uploading + +Once this is set up, make sure you commit the current state you want to publish. +Then try `cargo publish --dry-run`. If that works well, review the files that +will be published via `cargo package --list`. If you are satisfied, you can now +officially publish it via `cargo publish`. + +Congratulations, your package is public to the world. + +### Sharing + +Once you have published your package, people can now find it by +[searching for "cw-" on crates.io](https://crates.io/search?q=cw). +But that isn't exactly the simplest way. To make things easier and help +keep the ecosystem together, we suggest making a PR to add your package +to the [`cawesome-wasm`](https://github.com/cosmwasm/cawesome-wasm) list. + +### Organizations + +Many times you are writing a contract not as a solo developer, but rather as +part of an organization. You will want to allow colleagues to upload new +versions of the contract to crates.io when you are on holiday. +[These instructions show how]() you can set up your crate to allow multiple maintainers. + +You can add another owner to the crate by specifying their github user. Note, you will +now both have complete control of the crate, and they can remove you: + +`cargo owner --add ethanfrey` + +You can also add an existing github team inside your organization: + +`cargo owner --add github:confio:developers` + +The team will allow anyone who is currently in the team to publish new versions of the crate. +And this is automatically updated when you make changes on github. However, it will not allow +anyone in the team to add or remove other owners. diff --git a/contracts/external/snip20-reference-impl/README.md b/contracts/external/snip20-reference-impl/README.md new file mode 100644 index 0000000..294b59d --- /dev/null +++ b/contracts/external/snip20-reference-impl/README.md @@ -0,0 +1,177 @@ +# SNIP-20 Reference Implementation + +This is an implementation of a [SNIP-20](https://github.com/SecretFoundation/SNIPs/blob/master/SNIP-20.md), [SNIP-21](https://github.com/SecretFoundation/SNIPs/blob/master/SNIP-21.md), [SNIP-22](https://github.com/SecretFoundation/SNIPs/blob/master/SNIP-22.md), [SNIP-23](https://github.com/SecretFoundation/SNIPs/blob/master/SNIP-23.md), [SNIP-24](https://github.com/SecretFoundation/SNIPs/blob/master/SNIP-24.md), [SNIP-25](https://github.com/SecretFoundation/SNIPs/blob/master/SNIP-25.md) and [SNIP-26](https://github.com/SecretFoundation/SNIPs/blob/master/SNIP-26.md) compliant token contract. + +> **Note:** +> The master branch contains new features not covered by officially-released SNIPs and may be subject to change. When releasing a token on mainnet, we recommend you start with a [tagged release](https://github.com/scrtlabs/snip20-reference-impl/tags) to ensure compatibility with SNIP standards. + +At the time of token creation you may configure: +* Public Total Supply: If you enable this, the token's total supply will be displayed whenever a TokenInfo query is performed. DEFAULT: false +* Enable Deposit: If you enable this, you will be able to convert from SCRT to the token.* DEFAULT: false +* Enable Redeem: If you enable this, you will be able to redeem your token for SCRT.* It should be noted that if you have redeem enabled, but deposit disabled, all redeem attempts will fail unless someone has sent SCRT to the token contract. DEFAULT: false +* Enable Mint: If you enable this, any address in the list of minters will be able to mint new tokens. The admin address is the default minter, but can use the set/add/remove_minters functions to change the list of approved minting addresses. DEFAULT: false +* Enable Burn: If you enable this, addresses will be able to burn tokens. DEFAULT: false + + +\*:The conversion rate will be 1 uscrt for 1 minimum denomination of the token. This means that if your token has 6 decimal places, it will convert 1:1 with SCRT. If your token has 10 decimal places, it will have an exchange rate of 10000 SCRT for 1 token. If your token has 3 decimal places, it will have an exchange rate of 1000 tokens for 1 SCRT. You can use the exchange_rate query to view the exchange rate for the token. The query response will display either how many tokens are worth 1 SCRT, or how many SCRT are worth 1 token. That is, the response lists the symbol of the coin that has less value (either SCRT or the token), and the number of those coins that are worth 1 of the other. + +## Usage examples: + +To create a new token: + +```secretcli tx compute instantiate '{"name":"","symbol":"","admin":"","decimals":,"initial_balances":[{"address":"","amount":""}],"prng_seed":"","config":{"public_total_supply":,"enable_deposit":,"enable_redeem":,"enable_mint":,"enable_burn":}}' --label --from ``` + +The `admin` field is optional and will default to the "--from" address if you do not specify it. The `initial_balances` field is optional, and you can specify as many addresses/balances as you like. The `config` field as well as every field in the `config` is optional. Any `config` fields not specified will default to `false`. + +To deposit: ***(This is public)*** + +```secretcli tx compute execute '{"deposit": {}}' --amount 1000000uscrt --from ``` + +To send SSCRT: + +```secretcli tx compute execute '{"transfer": {"recipient": "", "amount": ""}}' --from ``` + +To set your viewing key: + +```secretcli tx compute execute '{"create_viewing_key": {"entropy": ""}}' --from ``` + +To check your balance: + +```secretcli q compute query '{"balance": {"address":"", "key":"your_viewing_key"}}'``` + +To view your transfer history: + +```secretcli q compute query '{"transfer_history": {"address": "", "key": "", "page": , "page_size": , "should_filter_decoys":}}'``` + +To view your transaction history: + +```secretcli q compute query '{"transaction_history": {"address": "", "key": "", "page": , "page_size": , "should_filter_decoys":}}'``` + +To withdraw: ***(This is public)*** + +```secretcli tx compute execute '{"redeem": {"amount": ""}}' --from ``` + +To view the token contract's configuration: + +```secretcli q compute query '{"token_config": {}}'``` + +To view the deposit/redeem exchange rate: + +```secretcli q compute query '{"exchange_rate": {}}'``` + + +## Troubleshooting + +All transactions are encrypted, so if you want to see the error returned by a failed transaction, you need to use the command + +`secretcli q compute tx ` + +# SNIP 25 Security Update + +## Security Changes +1. Implemented the ability to have decoy addresses for every operation that access account's balance +2. Converted every add operation related to account's balance and total supply +3. Started using u128 instead of Uint128 + +## Decoys +### Transaction That Support Decoys +1. Redeem +2. Deposit +3. Transfer +4. TransferFrom +5. Send +6. SendFrom +7. Burn +8. BurnFrom +9. Mint +10. BatchTransfer - For every action (The strength of the decoys will be the minimal strength of all of the actions) +11. BatchSend - For every action (The strength of the decoys will be the minimal strength of all of the actions) +12. BatchTransferFrom - For every action (The strength of the decoys will be the minimal strength of all of the actions) +13. BatchSendFrom - For every action (The strength of the decoys will be the minimal strength of all of the actions) +14. BatchMint - For every action (The strength of the decoys will be the minimal strength of all of the actions) +15. BatchBurnFrom - For every action (The strength of the decoys will be the minimal strength of all of the actions) + +### Example +```secretcli tx compute execute '{"transfer":{"recipient":"
","amount":"", "entropy":"", "decoys":<[addresses_list]>}}' --from ``` + +## Future Work +| Topic | Immediate-term solution | Medium-term solution | Long-term solution | +| --- | --- | --- | --- | +| Receiver privacy | Decoys - offer limited privacy, since it depends a lot on how you choose decoys. There’s probably no way to select decoys effectively enough, and thus it only makes it a bit harder but effectively doesn’t provide receiver privacy to a sophisticated long-term attacker | Some sort of bucketing? - still no clear path forward| ORAM? - still no clear path forward | +| Transfer amount privacy - subtractions (Transfer/Send/Burn) | None | None | Merkle proofs for storage reads - will make it very difficult to simulate transactions and play with storage. | + +# SNIP 25 Other Updates + +## All Allowances +Adds the ability for an owner to query for all allowances they have given out, as well as for a spender to query for all allowances they have received. + +## Queries + +### AllowancesGiven + +This query MUST be authenticated. + +Returns the list of allowances given out by the current account as an owner, as well as the total count of allowances given out. + +Results SHOULD be paginated. Results MUST be sorted in reverse chronological order by the datetime at which the allowance was first created (i.e., order is not determined by expiration, nor by last modified). + +#### Request + +| Name | Type | Description | optional | +| ---- | ---- | ----------- | -------- | +| [with_permit].query.allowances_given.owner | string | Account from which tokens are allowed to be taken | no | +| [with_permit].query.allowances_given.page_size | number | Number of allowances to return, starting from the latest. i.e. n=1 will return only the latest allowance | no | +| [with_permit].query.allowances_given.page | number | Defaults to 0. Specifying a positive number will skip page * page_size txs from the start. | yes | + +#### Response +```json +{ + "allowances_given": { + "owner": "
", + "allowances": [ + { + "spender": "
", + "allowance": "Uint128", + "expiration": 1234, + }, + { "...": "..." } + ], + "count": 200 + } +} +``` + +### AllowancesReceived + +This query MUST be authenticated. + +Returns the list of allowances given to the current account as a spender, as well as the total count of allowances received. + +Results SHOULD be paginated. Results MUST be sorted in reverse chronological order by the datetime at which the allowance was first created (i.e., order is not determined by expiration). + +#### Request + +| Name | Type | Description | optional | +| ---- | ---- | ----------- | -------- | +| [with_permit.]query.allowances_received.spender | string | Account which is allowed to spend tokens on behalf of the owner | no | +| [with_permit.]query.allowances_received.page_size | number | Number of allowances to return, starting from the latest. i.e. n=1 will return only the latest allowance | no | +| [with_permit.]query.allowances_received.page | number | Defaults to 0. Specifying a positive number will skip page * page_size txs from the start. | yes | + +#### Response + +```json +{ + "allowances_received": { + "spender": "
", + "allowances": [ + { + "owner": "
", + "allowance": "Uint128", + "expiration": 1234, + }, + { "...": "..." } + ], + "count": 200 + } +} +``` diff --git a/snip20-reference-impl/examples/schema.rs b/contracts/external/snip20-reference-impl/examples/schema.rs similarity index 89% rename from snip20-reference-impl/examples/schema.rs rename to contracts/external/snip20-reference-impl/examples/schema.rs index f20762b..d076a3b 100644 --- a/snip20-reference-impl/examples/schema.rs +++ b/contracts/external/snip20-reference-impl/examples/schema.rs @@ -4,7 +4,7 @@ use std::fs::create_dir_all; use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; use snip20_reference_impl::msg::{ - ExecuteAnswer, ExecuteMsg, InstantiateMsg, QueryMsg, + ExecuteAnswer, ExecuteMsg, InstantiateMsg, QueryAnswer, QueryMsg, }; fn main() { diff --git a/snip20-reference-impl/tests/example-receiver/rustfmt.toml b/contracts/external/snip20-reference-impl/rustfmt.toml similarity index 100% rename from snip20-reference-impl/tests/example-receiver/rustfmt.toml rename to contracts/external/snip20-reference-impl/rustfmt.toml diff --git a/snip20-reference-impl/schema/handle_answer.json b/contracts/external/snip20-reference-impl/schema/handle_answer.json similarity index 100% rename from snip20-reference-impl/schema/handle_answer.json rename to contracts/external/snip20-reference-impl/schema/handle_answer.json diff --git a/snip20-reference-impl/schema/handle_msg.json b/contracts/external/snip20-reference-impl/schema/handle_msg.json similarity index 100% rename from snip20-reference-impl/schema/handle_msg.json rename to contracts/external/snip20-reference-impl/schema/handle_msg.json diff --git a/snip20-reference-impl/schema/init_msg.json b/contracts/external/snip20-reference-impl/schema/init_msg.json similarity index 100% rename from snip20-reference-impl/schema/init_msg.json rename to contracts/external/snip20-reference-impl/schema/init_msg.json diff --git a/snip20-reference-impl/schema/query_answer.json b/contracts/external/snip20-reference-impl/schema/query_answer.json similarity index 100% rename from snip20-reference-impl/schema/query_answer.json rename to contracts/external/snip20-reference-impl/schema/query_answer.json diff --git a/snip20-reference-impl/schema/query_msg.json b/contracts/external/snip20-reference-impl/schema/query_msg.json similarity index 100% rename from snip20-reference-impl/schema/query_msg.json rename to contracts/external/snip20-reference-impl/schema/query_msg.json diff --git a/snip20-reference-impl/src/batch.rs b/contracts/external/snip20-reference-impl/src/batch.rs similarity index 100% rename from snip20-reference-impl/src/batch.rs rename to contracts/external/snip20-reference-impl/src/batch.rs diff --git a/snip20-reference-impl/src/contract.rs b/contracts/external/snip20-reference-impl/src/contract.rs similarity index 99% rename from snip20-reference-impl/src/contract.rs rename to contracts/external/snip20-reference-impl/src/contract.rs index b255fcc..0f6b7e5 100644 --- a/snip20-reference-impl/src/contract.rs +++ b/contracts/external/snip20-reference-impl/src/contract.rs @@ -11,10 +11,9 @@ use secret_toolkit::viewing_key::{ViewingKey, ViewingKeyStore}; use secret_toolkit_crypto::{sha_256, Prng, SHA256_HASH_SIZE}; use crate::batch; -use crate::msg::{Allowance, AllowancesGiven, AllowancesReceived, Balance, ContractStatus,CreateViewingKeyResponse, ExchangeRate, InitResponse, Minters, TokenConfig, TokenInfo, TransactionHistory, TransferHistory, ViewingKeyError}; use crate::msg::{ AllowanceGivenResult, AllowanceReceivedResult, ContractStatusLevel, Decoyable, ExecuteAnswer, - ExecuteMsg, InstantiateMsg, QueryMsg, QueryWithPermit, ResponseStatus::Success, + ExecuteMsg, InstantiateMsg, QueryAnswer, QueryMsg, QueryWithPermit, ResponseStatus::Success, }; use crate::receiver::Snip20ReceiveMsg; use crate::state::{ @@ -119,7 +118,7 @@ pub fn instantiate( redeem_is_enabled: init_config.redeem_enabled(), mint_is_enabled: init_config.mint_enabled(), burn_is_enabled: init_config.burn_enabled(), - contract_address: env.contract.address.clone(), + contract_address: env.contract.address, supported_denoms, can_modify_denoms: init_config.can_modify_denoms(), }, @@ -127,7 +126,7 @@ pub fn instantiate( TOTAL_SUPPLY.save(deps.storage, &total_supply)?; CONTRACT_STATUS.save(deps.storage, &ContractStatusLevel::NormalRun)?; let minters = if init_config.mint_enabled() { - Vec::from([admin.clone()]) + Vec::from([admin]) } else { Vec::new() }; @@ -135,12 +134,7 @@ pub fn instantiate( ViewingKey::set_seed(deps.storage, &prng_seed_hashed); - - Ok(Response::new().set_data(to_binary(&InitResponse{ - owner: admin, - contract_address: env.contract.address.to_string(), - code_hash: env.contract.code_hash, - })?)) + Ok(Response::default()) } fn get_address_position( @@ -579,7 +573,7 @@ pub fn viewing_keys_queries(deps: Deps, msg: QueryMsg) -> StdResult { } } - to_binary(&ViewingKeyError { + to_binary(&QueryAnswer::ViewingKeyError { msg: "Wrong viewing key for this address or viewing key not set".to_string(), }) } @@ -599,9 +593,9 @@ fn query_exchange_rate(storage: &dyn Storage) -> StdResult { rate = Uint128::new(10u128.pow(6 - constants.decimals as u32)); denom = constants.symbol; } - return to_binary(&ExchangeRate { rate, denom }); + return to_binary(&QueryAnswer::ExchangeRate { rate, denom }); } - to_binary(&ExchangeRate { + to_binary(&QueryAnswer::ExchangeRate { rate: Uint128::zero(), denom: String::new(), }) @@ -616,7 +610,7 @@ fn query_token_info(storage: &dyn Storage) -> StdResult { None }; - to_binary(&TokenInfo { + to_binary(&QueryAnswer::TokenInfo { name: constants.name, symbol: constants.symbol, decimals: constants.decimals, @@ -627,7 +621,7 @@ fn query_token_info(storage: &dyn Storage) -> StdResult { fn query_token_config(storage: &dyn Storage) -> StdResult { let constants = CONFIG.load(storage)?; - to_binary(&TokenConfig { + to_binary(&QueryAnswer::TokenConfig { public_total_supply: constants.total_supply_is_public, deposit_enabled: constants.deposit_is_enabled, redeem_enabled: constants.redeem_is_enabled, @@ -640,7 +634,7 @@ fn query_token_config(storage: &dyn Storage) -> StdResult { fn query_contract_status(storage: &dyn Storage) -> StdResult { let contract_status = CONTRACT_STATUS.load(storage)?; - to_binary(&ContractStatus { + to_binary(&QueryAnswer::ContractStatus { status: contract_status, }) } @@ -666,7 +660,7 @@ pub fn query_transfers( should_filter_decoys, )?; - let result = TransferHistory { + let result = QueryAnswer::TransferHistory { txs, total: Some(total), }; @@ -689,7 +683,7 @@ pub fn query_transactions( let (txs, total) = StoredExtendedTx::get_txs(deps.storage, account, page, page_size, should_filter_decoys)?; - let result = TransactionHistory { + let result = QueryAnswer::TransactionHistory { txs, total: Some(total), }; @@ -704,14 +698,14 @@ pub fn query_balance(deps: Deps, account: String) -> StdResult { let account = Addr::unchecked(account); let amount = Uint128::new(BalancesStore::load(deps.storage, &account)); - let response =Balance { amount }; + let response = QueryAnswer::Balance { amount }; to_binary(&response) } fn query_minters(deps: Deps) -> StdResult { let minters = MintersStore::load(deps.storage)?; - let response = Minters { minters }; + let response = QueryAnswer::Minters { minters }; to_binary(&response) } @@ -940,7 +934,7 @@ pub fn try_create_key( entropy.as_ref(), ); - Ok(Response::new().set_data(to_binary(&CreateViewingKeyResponse { key })?)) + Ok(Response::new().set_data(to_binary(&ExecuteAnswer::CreateViewingKey { key })?)) } fn set_contract_status( @@ -970,7 +964,7 @@ pub fn query_allowance(deps: Deps, owner: String, spender: String) -> StdResult< let allowance = AllowancesStore::load(deps.storage, &owner, &spender); - let response = Allowance { + let response = QueryAnswer::Allowance { owner, spender, allowance: Uint128::new(allowance.amount), @@ -1003,7 +997,7 @@ pub fn query_allowances_given( }) .collect(); - let response = AllowancesGiven { + let response = QueryAnswer::AllowancesGiven { owner: owner.clone(), allowances: allowances_result, count: AllowancesStore::num_allowances(deps.storage, &owner), @@ -1035,7 +1029,7 @@ pub fn query_allowances_received( }) .collect(); - let response = AllowancesReceived { + let response = QueryAnswer::AllowancesReceived { spender: spender.clone(), allowances, count: AllowancesStore::num_allowed(deps.storage, &spender), @@ -1290,7 +1284,6 @@ fn try_batch_transfer( #[allow(clippy::too_many_arguments)] fn try_add_receiver_api_callback( storage: &dyn Storage, - _env: Env, messages: &mut Vec, recipient: Addr, recipient_code_hash: Option, @@ -1321,7 +1314,6 @@ fn try_add_receiver_api_callback( #[allow(clippy::too_many_arguments)] fn try_send_impl( deps: &mut DepsMut, - env: Env, messages: &mut Vec, sender: Addr, recipient: Addr, @@ -1346,7 +1338,6 @@ fn try_send_impl( try_add_receiver_api_callback( deps.storage, - env, messages, recipient, recipient_code_hash, @@ -1378,7 +1369,6 @@ fn try_send( let mut messages = vec![]; try_send_impl( &mut deps, - env.clone(), &mut messages, info.sender, recipient, @@ -1386,7 +1376,7 @@ fn try_send( amount, memo, msg, - &env.clone().block, + &env.block, decoys, account_random_pos, )?; @@ -1408,7 +1398,6 @@ fn try_batch_send( let recipient = deps.api.addr_validate(action.recipient.as_str())?; try_send_impl( &mut deps, - env.clone(), &mut messages, info.sender.clone(), recipient, @@ -1416,7 +1405,7 @@ fn try_batch_send( action.amount, action.memo, action.msg, - &env.clone().block, + &env.block, action.decoys, account_random_pos, )?; @@ -1600,7 +1589,6 @@ fn try_send_from_impl( try_add_receiver_api_callback( deps.storage, - env, messages, recipient, recipient_code_hash, @@ -2915,9 +2903,9 @@ mod tests { height: 12_345, time: Timestamp::from_seconds(1_571_797_420), chain_id: "cosmos-testnet-14002".to_string(), - random: None, + random:None, }, - transaction: Some(TransactionInfo { index: 3, hash: todo!() }), + transaction: Some(TransactionInfo { index: 3, hash:"hash".to_string() }), contract: ContractInfo { address: Addr::unchecked(MOCK_CONTRACT_ADDR.to_string()), code_hash: "".to_string(), diff --git a/snip20-reference-impl/src/lib.rs b/contracts/external/snip20-reference-impl/src/lib.rs similarity index 100% rename from snip20-reference-impl/src/lib.rs rename to contracts/external/snip20-reference-impl/src/lib.rs diff --git a/snip20-reference-impl/src/msg.rs b/contracts/external/snip20-reference-impl/src/msg.rs similarity index 86% rename from snip20-reference-impl/src/msg.rs rename to contracts/external/snip20-reference-impl/src/msg.rs index f12d188..cc583dd 100644 --- a/snip20-reference-impl/src/msg.rs +++ b/contracts/external/snip20-reference-impl/src/msg.rs @@ -34,13 +34,6 @@ impl InstantiateMsg { } } -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] -pub struct InitResponse { - pub owner : Addr, - pub contract_address : String, - pub code_hash: String, -} - /// This type represents optional configuration values which can be overridden. /// All values are optional and have defaults which are more private by default, /// but can be overridden if necessary @@ -366,7 +359,9 @@ pub enum ExecuteAnswer { RegisterReceive { status: ResponseStatus, }, - + CreateViewingKey { + key: String, + }, SetViewingKey { status: ResponseStatus, }, @@ -558,87 +553,64 @@ pub enum QueryWithPermit { }, } -// #[derive(Serialize, Deserialize, JsonSchema, Debug)] -// #[serde(rename_all = "snake_case")] -// pub enum QueryAnswer { -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct TokenInfo { - pub name: String, - pub symbol: String, - pub decimals: u8, - pub total_supply: Option, - } - -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct TokenConfig { - pub public_total_supply: bool, - pub deposit_enabled: bool, - pub redeem_enabled: bool, - pub mint_enabled: bool, - pub burn_enabled: bool, - pub supported_denoms: Vec, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct ContractStatus { - pub status: ContractStatusLevel, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct ExchangeRate { - pub rate: Uint128, - pub denom: String, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct Allowance { - pub spender: Addr, - pub owner: Addr, - pub allowance: Uint128, - pub expiration: Option, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct AllowancesGiven { - pub owner: Addr, - pub allowances: Vec, - pub count: u32, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct AllowancesReceived { - pub spender: Addr, - pub allowances: Vec, - pub count: u32, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct Balance { - pub amount: Uint128, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct TransferHistory { - pub txs: Vec, - pub total: Option, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct TransactionHistory { - pub txs: Vec, - pub total: Option, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct ViewingKeyError { - pub msg: String, - } - - #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] - pub struct Minters { - pub minters: Vec, - } -// } +#[derive(Serialize, Deserialize, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +pub enum QueryAnswer { + TokenInfo { + name: String, + symbol: String, + decimals: u8, + total_supply: Option, + }, + TokenConfig { + public_total_supply: bool, + deposit_enabled: bool, + redeem_enabled: bool, + mint_enabled: bool, + burn_enabled: bool, + supported_denoms: Vec, + }, + ContractStatus { + status: ContractStatusLevel, + }, + ExchangeRate { + rate: Uint128, + denom: String, + }, + Allowance { + spender: Addr, + owner: Addr, + allowance: Uint128, + expiration: Option, + }, + AllowancesGiven { + owner: Addr, + allowances: Vec, + count: u32, + }, + AllowancesReceived { + spender: Addr, + allowances: Vec, + count: u32, + }, + Balance { + amount: Uint128, + }, + TransferHistory { + txs: Vec, + total: Option, + }, + TransactionHistory { + txs: Vec, + total: Option, + }, + ViewingKeyError { + msg: String, + }, + Minters { + minters: Vec, + }, +} #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] pub struct AllowanceGivenResult { @@ -654,11 +626,6 @@ pub struct AllowanceReceivedResult { pub expiration: Option, } -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] -pub struct CreateViewingKeyResponse { - pub key: String, -} - #[derive(Serialize, Deserialize, Clone, JsonSchema, Debug)] #[cfg_attr(test, derive(Eq, PartialEq))] #[serde(rename_all = "snake_case")] diff --git a/snip20-reference-impl/src/receiver.rs b/contracts/external/snip20-reference-impl/src/receiver.rs similarity index 100% rename from snip20-reference-impl/src/receiver.rs rename to contracts/external/snip20-reference-impl/src/receiver.rs diff --git a/snip20-reference-impl/src/state.rs b/contracts/external/snip20-reference-impl/src/state.rs similarity index 100% rename from snip20-reference-impl/src/state.rs rename to contracts/external/snip20-reference-impl/src/state.rs diff --git a/snip20-reference-impl/src/transaction_history.rs b/contracts/external/snip20-reference-impl/src/transaction_history.rs similarity index 100% rename from snip20-reference-impl/src/transaction_history.rs rename to contracts/external/snip20-reference-impl/src/transaction_history.rs diff --git a/snip20-reference-impl/tests/example-receiver/.gitignore b/contracts/external/snip20-reference-impl/tests/example-receiver/.gitignore similarity index 100% rename from snip20-reference-impl/tests/example-receiver/.gitignore rename to contracts/external/snip20-reference-impl/tests/example-receiver/.gitignore diff --git a/snip20-reference-impl/tests/example-receiver/Cargo.toml b/contracts/external/snip20-reference-impl/tests/example-receiver/Cargo.toml similarity index 100% rename from snip20-reference-impl/tests/example-receiver/Cargo.toml rename to contracts/external/snip20-reference-impl/tests/example-receiver/Cargo.toml diff --git a/snip20-reference-impl/tests/example-receiver/Makefile b/contracts/external/snip20-reference-impl/tests/example-receiver/Makefile similarity index 100% rename from snip20-reference-impl/tests/example-receiver/Makefile rename to contracts/external/snip20-reference-impl/tests/example-receiver/Makefile diff --git a/snip20-reference-impl/tests/example-receiver/README.md b/contracts/external/snip20-reference-impl/tests/example-receiver/README.md similarity index 100% rename from snip20-reference-impl/tests/example-receiver/README.md rename to contracts/external/snip20-reference-impl/tests/example-receiver/README.md diff --git a/contracts/external/snip20-reference-impl/tests/example-receiver/rustfmt.toml b/contracts/external/snip20-reference-impl/tests/example-receiver/rustfmt.toml new file mode 100644 index 0000000..11a85e6 --- /dev/null +++ b/contracts/external/snip20-reference-impl/tests/example-receiver/rustfmt.toml @@ -0,0 +1,15 @@ +# stable +newline_style = "unix" +hard_tabs = false +tab_spaces = 4 + +# unstable... should we require `rustup run nightly cargo fmt` ? +# or just update the style guide when they are stable? +#fn_single_line = true +#format_code_in_doc_comments = true +#overflow_delimited_expr = true +#reorder_impl_items = true +#struct_field_align_threshold = 20 +#struct_lit_single_line = true +#report_todo = "Always" + diff --git a/snip20-reference-impl/tests/example-receiver/src/contract.rs b/contracts/external/snip20-reference-impl/tests/example-receiver/src/contract.rs similarity index 100% rename from snip20-reference-impl/tests/example-receiver/src/contract.rs rename to contracts/external/snip20-reference-impl/tests/example-receiver/src/contract.rs diff --git a/snip20-reference-impl/tests/example-receiver/src/lib.rs b/contracts/external/snip20-reference-impl/tests/example-receiver/src/lib.rs similarity index 100% rename from snip20-reference-impl/tests/example-receiver/src/lib.rs rename to contracts/external/snip20-reference-impl/tests/example-receiver/src/lib.rs diff --git a/snip20-reference-impl/tests/example-receiver/src/msg.rs b/contracts/external/snip20-reference-impl/tests/example-receiver/src/msg.rs similarity index 100% rename from snip20-reference-impl/tests/example-receiver/src/msg.rs rename to contracts/external/snip20-reference-impl/tests/example-receiver/src/msg.rs diff --git a/snip20-reference-impl/tests/example-receiver/src/state.rs b/contracts/external/snip20-reference-impl/tests/example-receiver/src/state.rs similarity index 100% rename from snip20-reference-impl/tests/example-receiver/src/state.rs rename to contracts/external/snip20-reference-impl/tests/example-receiver/src/state.rs diff --git a/snip20-reference-impl/tests/integration.rs b/contracts/external/snip20-reference-impl/tests/integration.rs similarity index 100% rename from snip20-reference-impl/tests/integration.rs rename to contracts/external/snip20-reference-impl/tests/integration.rs diff --git a/snip20-reference-impl/tests/integration.sh b/contracts/external/snip20-reference-impl/tests/integration.sh similarity index 100% rename from snip20-reference-impl/tests/integration.sh rename to contracts/external/snip20-reference-impl/tests/integration.sh diff --git a/snip721-reference-impl/.cargo/config b/contracts/external/snip721-reference-impl/.cargo/config similarity index 100% rename from snip721-reference-impl/.cargo/config rename to contracts/external/snip721-reference-impl/.cargo/config diff --git a/snip721-reference-impl/.gitignore b/contracts/external/snip721-reference-impl/.gitignore similarity index 100% rename from snip721-reference-impl/.gitignore rename to contracts/external/snip721-reference-impl/.gitignore diff --git a/snip721-reference-impl/Cargo.toml b/contracts/external/snip721-reference-impl/Cargo.toml similarity index 100% rename from snip721-reference-impl/Cargo.toml rename to contracts/external/snip721-reference-impl/Cargo.toml diff --git a/snip721-reference-impl/Makefile b/contracts/external/snip721-reference-impl/Makefile similarity index 100% rename from snip721-reference-impl/Makefile rename to contracts/external/snip721-reference-impl/Makefile diff --git a/snip721-reference-impl/examples/schema.rs b/contracts/external/snip721-reference-impl/examples/schema.rs similarity index 100% rename from snip721-reference-impl/examples/schema.rs rename to contracts/external/snip721-reference-impl/examples/schema.rs diff --git a/snip721-reference-impl/schema/execute_answer.json b/contracts/external/snip721-reference-impl/schema/execute_answer.json similarity index 100% rename from snip721-reference-impl/schema/execute_answer.json rename to contracts/external/snip721-reference-impl/schema/execute_answer.json diff --git a/snip721-reference-impl/schema/execute_msg.json b/contracts/external/snip721-reference-impl/schema/execute_msg.json similarity index 100% rename from snip721-reference-impl/schema/execute_msg.json rename to contracts/external/snip721-reference-impl/schema/execute_msg.json diff --git a/snip721-reference-impl/schema/instantiate_msg.json b/contracts/external/snip721-reference-impl/schema/instantiate_msg.json similarity index 100% rename from snip721-reference-impl/schema/instantiate_msg.json rename to contracts/external/snip721-reference-impl/schema/instantiate_msg.json diff --git a/snip721-reference-impl/schema/query_answer.json b/contracts/external/snip721-reference-impl/schema/query_answer.json similarity index 100% rename from snip721-reference-impl/schema/query_answer.json rename to contracts/external/snip721-reference-impl/schema/query_answer.json diff --git a/snip721-reference-impl/schema/query_msg.json b/contracts/external/snip721-reference-impl/schema/query_msg.json similarity index 100% rename from snip721-reference-impl/schema/query_msg.json rename to contracts/external/snip721-reference-impl/schema/query_msg.json diff --git a/snip721-reference-impl/src/contract.rs b/contracts/external/snip721-reference-impl/src/contract.rs similarity index 100% rename from snip721-reference-impl/src/contract.rs rename to contracts/external/snip721-reference-impl/src/contract.rs diff --git a/snip721-reference-impl/src/expiration.rs b/contracts/external/snip721-reference-impl/src/expiration.rs similarity index 100% rename from snip721-reference-impl/src/expiration.rs rename to contracts/external/snip721-reference-impl/src/expiration.rs diff --git a/snip721-reference-impl/src/inventory.rs b/contracts/external/snip721-reference-impl/src/inventory.rs similarity index 100% rename from snip721-reference-impl/src/inventory.rs rename to contracts/external/snip721-reference-impl/src/inventory.rs diff --git a/snip721-reference-impl/src/lib.rs b/contracts/external/snip721-reference-impl/src/lib.rs similarity index 100% rename from snip721-reference-impl/src/lib.rs rename to contracts/external/snip721-reference-impl/src/lib.rs diff --git a/snip721-reference-impl/src/mint_run.rs b/contracts/external/snip721-reference-impl/src/mint_run.rs similarity index 100% rename from snip721-reference-impl/src/mint_run.rs rename to contracts/external/snip721-reference-impl/src/mint_run.rs diff --git a/snip721-reference-impl/src/msg.rs b/contracts/external/snip721-reference-impl/src/msg.rs similarity index 100% rename from snip721-reference-impl/src/msg.rs rename to contracts/external/snip721-reference-impl/src/msg.rs diff --git a/snip721-reference-impl/src/receiver.rs b/contracts/external/snip721-reference-impl/src/receiver.rs similarity index 100% rename from snip721-reference-impl/src/receiver.rs rename to contracts/external/snip721-reference-impl/src/receiver.rs diff --git a/snip721-reference-impl/src/royalties.rs b/contracts/external/snip721-reference-impl/src/royalties.rs similarity index 100% rename from snip721-reference-impl/src/royalties.rs rename to contracts/external/snip721-reference-impl/src/royalties.rs diff --git a/snip721-reference-impl/src/state.rs b/contracts/external/snip721-reference-impl/src/state.rs similarity index 100% rename from snip721-reference-impl/src/state.rs rename to contracts/external/snip721-reference-impl/src/state.rs diff --git a/snip721-reference-impl/src/token.rs b/contracts/external/snip721-reference-impl/src/token.rs similarity index 100% rename from snip721-reference-impl/src/token.rs rename to contracts/external/snip721-reference-impl/src/token.rs diff --git a/snip721-reference-impl/src/unittest_handles.rs b/contracts/external/snip721-reference-impl/src/unittest_handles.rs similarity index 100% rename from snip721-reference-impl/src/unittest_handles.rs rename to contracts/external/snip721-reference-impl/src/unittest_handles.rs diff --git a/snip721-reference-impl/src/unittest_inventory.rs b/contracts/external/snip721-reference-impl/src/unittest_inventory.rs similarity index 100% rename from snip721-reference-impl/src/unittest_inventory.rs rename to contracts/external/snip721-reference-impl/src/unittest_inventory.rs diff --git a/snip721-reference-impl/src/unittest_mint_run.rs b/contracts/external/snip721-reference-impl/src/unittest_mint_run.rs similarity index 100% rename from snip721-reference-impl/src/unittest_mint_run.rs rename to contracts/external/snip721-reference-impl/src/unittest_mint_run.rs diff --git a/snip721-reference-impl/src/unittest_non_transferable.rs b/contracts/external/snip721-reference-impl/src/unittest_non_transferable.rs similarity index 100% rename from snip721-reference-impl/src/unittest_non_transferable.rs rename to contracts/external/snip721-reference-impl/src/unittest_non_transferable.rs diff --git a/snip721-reference-impl/src/unittest_queries.rs b/contracts/external/snip721-reference-impl/src/unittest_queries.rs similarity index 100% rename from snip721-reference-impl/src/unittest_queries.rs rename to contracts/external/snip721-reference-impl/src/unittest_queries.rs diff --git a/snip721-reference-impl/src/unittest_royalties.rs b/contracts/external/snip721-reference-impl/src/unittest_royalties.rs similarity index 100% rename from snip721-reference-impl/src/unittest_royalties.rs rename to contracts/external/snip721-reference-impl/src/unittest_royalties.rs diff --git a/snip721-reference-impl/tests/integration.rs b/contracts/external/snip721-reference-impl/tests/integration.rs similarity index 100% rename from snip721-reference-impl/tests/integration.rs rename to contracts/external/snip721-reference-impl/tests/integration.rs diff --git a/contracts/external/snip721-roles/src/contract.rs b/contracts/external/snip721-roles/src/contract.rs index ef397d6..a4ba1da 100644 --- a/contracts/external/snip721-roles/src/contract.rs +++ b/contracts/external/snip721-roles/src/contract.rs @@ -5,7 +5,7 @@ use cosmwasm_schema::serde; use cosmwasm_std::entry_point; use cosmwasm_std::{ from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, - StdResult, SubMsg, SubMsgResult, Uint64, WasmMsg, + StdError, StdResult, SubMsg, SubMsgResult, Uint64, WasmMsg, }; use cw4::{ Member, MemberChangedHookMsg, MemberDiff, MemberListResponse, MemberResponse, @@ -13,12 +13,16 @@ use cw4::{ }; use schemars::JsonSchema; use secret_cw_controllers::HookItem; +use secret_toolkit::permit::{Permit, RevokedPermits, TokenPermissions}; use secret_toolkit::utils::InitCallback; +use secret_toolkit::viewing_key::{ViewingKey, ViewingKeyStore}; use serde::{Deserialize, Serialize}; // use cw721_base::Cw721Contract; // use snip721_reference_impl::msg::InstantiateMsg as Cw721BaseInstantiateMsg; -use dao_snip721_extensions::roles::{ExecuteExt, QueryExt}; +use dao_snip721_extensions::roles::{ + CreateViewingKey, ExecuteExt, QueryExt, QueryWithPermit, ViewingKeyError, +}; use std::cmp::Ordering; // use snip721_reference_impl::msg::{ExecuteMsg as Snip721ExecuteMsg}; @@ -33,6 +37,8 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const MAX_LIMIT: u32 = 30; const DEFAULT_LIMIT: u32 = 10; +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; + #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] #[serde(rename_all = "snake_case")] pub enum Snip721ReceiveMsg { @@ -217,6 +223,11 @@ pub fn execute( ExecuteExt::UpdateTokenRole { token_id, role } => { execute_update_token_role(deps, env, info, token_id, role) } + ExecuteExt::CreateViewingKey { entropy, .. } => { + try_create_key(deps, env, info, entropy) + } + ExecuteExt::SetViewingKey { key, .. } => try_set_key(deps, info, key), + ExecuteExt::RevokePermit { permit_name, .. } => revoke_permit(deps, info, permit_name), }, } } @@ -874,18 +885,58 @@ pub fn execute_update_token_weight( .add_message(exec_msg)) } +pub fn try_set_key( + deps: DepsMut, + info: MessageInfo, + key: String, +) -> Result { + ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); + Ok(Response::default()) +} + +pub fn try_create_key( + deps: DepsMut, + env: Env, + info: MessageInfo, + entropy: String, +) -> Result { + let key = ViewingKey::create( + deps.storage, + &info, + &env, + info.sender.as_str(), + entropy.as_ref(), + ); + + Ok(Response::new().set_data(to_binary(&CreateViewingKey { key })?)) +} + +fn revoke_permit( + deps: DepsMut, + info: MessageInfo, + permit_name: String, +) -> Result { + RevokedPermits::revoke_permit( + deps.storage, + PREFIX_REVOKED_PERMITS, + info.sender.as_str(), + &permit_name, + ); + + Ok(Response::default()) +} + #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::ExtensionQuery(extension_query) => match extension_query { QueryExt::Hooks {} => to_binary(&HOOKS.query_hooks(deps)?), QueryExt::ListMembers { start_after, limit } => { to_binary(&query_list_members(deps, start_after, limit)?) } - QueryExt::Member { addr, at_height } => { - to_binary(&query_member(deps, addr, at_height)?) - } QueryExt::TotalWeight { at_height } => to_binary(&query_total_weight(deps, at_height)?), + QueryExt::WithPermit { permit, query } => permit_queries(deps, env, permit, query), + _ => viewing_keys_queries(deps, env, extension_query), }, QueryMsg::GetNftContractInfo {} => to_binary(&get_info(deps)?), _ => { @@ -900,6 +951,58 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } } +fn permit_queries( + deps: Deps, + env: Env, + permit: Permit, + query: QueryWithPermit, +) -> Result { + // Validate permit content + + let _account = secret_toolkit::permit::validate( + deps, + PREFIX_REVOKED_PERMITS, + &permit, + env.contract.address.clone().into_string(), + None, + )?; + + // Permit validated! We can now execute the query. + match query { + QueryWithPermit::Member { addr, at_height } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query memeber, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_member(deps, addr, at_height)?) + } + } +} + +pub fn viewing_keys_queries(deps: Deps, _env: Env, msg: QueryExt) -> StdResult { + let (addresses, key) = msg.get_validation_params(deps.api)?; + + for address in addresses { + let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); + if result.is_ok() { + return match msg { + // Base + QueryExt::Member { + addr, at_height, .. + } => to_binary(&query_member(deps, addr, at_height)?), + _ => panic!("This query type does not require authentication"), + }; + } + } + + to_binary(&ViewingKeyError { + msg: "Wrong viewing key for this address or viewing key not set".to_string(), + }) +} + pub fn query_total_weight(deps: Deps, height: Option) -> StdResult { if height.is_some() { let weight = TotalStore::may_load_at_height(deps.storage, height.unwrap())?; diff --git a/contracts/pre-propose/dao-pre-propose-approval-single/Cargo.toml b/contracts/pre-propose/dao-pre-propose-approval-single/Cargo.toml index 2deadfa..e4fe763 100644 --- a/contracts/pre-propose/dao-pre-propose-approval-single/Cargo.toml +++ b/contracts/pre-propose/dao-pre-propose-approval-single/Cargo.toml @@ -19,17 +19,19 @@ library = [] [dependencies] cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } -cw-storage-plus = { workspace = true } -cw2 = { workspace = true } +secret-storage-plus = { workspace = true } +secret-cw2 = { workspace = true } cw-paginate-storage = { workspace = true } dao-pre-propose-base = { workspace = true } dao-voting = { workspace = true } thiserror = { workspace = true } dao-interface = { workspace = true } +secret-toolkit ={ workspace = true } +cw-hooks ={ workspace = true } [dev-dependencies] cw-denom = { workspace = true } -cw-multi-test = { workspace = true } +secret-multi-test = { workspace = true } cw-utils = { workspace = true } cw4-group = { workspace = true } cw20 = { workspace = true } diff --git a/contracts/pre-propose/dao-pre-propose-approval-single/src/contract.rs b/contracts/pre-propose/dao-pre-propose-approval-single/src/contract.rs index 215335d..205113f 100644 --- a/contracts/pre-propose/dao-pre-propose-approval-single/src/contract.rs +++ b/contracts/pre-propose/dao-pre-propose-approval-single/src/contract.rs @@ -1,16 +1,15 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Order, Response, StdResult, - SubMsg, WasmMsg, + to_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, SubMsg, WasmMsg, }; -use cw2::set_contract_version; -use cw_paginate_storage::paginate_map_values; +use cw_hooks::HookItem; use dao_pre_propose_base::{ error::PreProposeError, msg::ExecuteMsg as ExecuteBase, state::PreProposeContract, }; use dao_voting::deposit::DepositRefundPolicy; use dao_voting::proposal::SingleChoiceProposeMsg as ProposeMsg; +use secret_cw2::set_contract_version; use crate::msg::{ ApproverProposeMessage, ExecuteExt, ExecuteMsg, InstantiateExt, InstantiateMsg, ProposeMessage, @@ -49,13 +48,13 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::Propose { msg } => execute_propose(deps, env, info, msg), + ExecuteMsg::Propose { msg, key } => execute_propose(deps, env, info, msg, key), - ExecuteMsg::AddProposalSubmittedHook { address } => { - execute_add_approver_hook(deps, info, address) + ExecuteMsg::AddProposalSubmittedHook { address, code_hash } => { + execute_add_approver_hook(deps, info, address, code_hash) } - ExecuteMsg::RemoveProposalSubmittedHook { address } => { - execute_remove_approver_hook(deps, info, address) + ExecuteMsg::RemoveProposalSubmittedHook { address, code_hash } => { + execute_remove_approver_hook(deps, info, address, code_hash) } ExecuteMsg::Extension { msg } => match msg { @@ -73,11 +72,12 @@ pub fn execute_propose( env: Env, info: MessageInfo, msg: ProposeMessage, + key: String, ) -> Result { let pre_propose_base = PrePropose::default(); let config = pre_propose_base.config.load(deps.storage)?; - pre_propose_base.check_can_submit(deps.as_ref(), info.sender.clone())?; + pre_propose_base.check_can_submit(deps.as_ref(), info.sender.clone(), key.clone())?; // Take deposit, if configured. let deposit_messages = if let Some(ref deposit_info) = config.deposit_info { @@ -109,13 +109,15 @@ pub fn execute_propose( .proposal_submitted_hooks .prepare_hooks(deps.storage, |a| { let execute_msg = WasmMsg::Execute { - contract_addr: a.into_string(), - msg: to_json_binary(&ExecuteBase::::Propose { + contract_addr: a.addr.into_string(), + code_hash: a.code_hash, + msg: to_binary(&ExecuteBase::::Propose { msg: ApproverProposeMessage::Propose { title: propose_msg_internal.title.clone(), description: propose_msg_internal.description.clone(), approval_id, }, + key: key.clone(), })?, funds: vec![], }; @@ -123,9 +125,9 @@ pub fn execute_propose( })?; // Save the proposal and its information as pending. - PENDING_PROPOSALS.save( + PENDING_PROPOSALS.insert( deps.storage, - approval_id, + &approval_id, &Proposal { status: ProposalStatus::Pending {}, approval_id, @@ -154,7 +156,7 @@ pub fn execute_approve( } // Load proposal and send propose message to the proposal module - let proposal = PENDING_PROPOSALS.may_load(deps.storage, id)?; + let proposal = PENDING_PROPOSALS.get(deps.storage, &id); match proposal { Some(proposal) => { let proposal_module = PrePropose::default().proposal_module.load(deps.storage)?; @@ -162,24 +164,26 @@ pub fn execute_approve( // Snapshot the deposit for the proposal that we're about // to create. let proposal_id = deps.querier.query_wasm_smart( - &proposal_module, + &proposal_module.code_hash.clone(), + proposal_module.addr.clone().to_string(), &dao_interface::proposal::Query::NextProposalId {}, )?; - PrePropose::default().deposits.save( + PrePropose::default().deposits.insert( deps.storage, - proposal_id, + &proposal_id, &(proposal.deposit.clone(), proposal.proposer.clone()), )?; let propose_messsage = WasmMsg::Execute { - contract_addr: proposal_module.into_string(), - msg: to_json_binary(&ProposeMessageInternal::Propose(proposal.msg.clone()))?, + contract_addr: proposal_module.addr.into_string(), + code_hash: proposal_module.code_hash, + msg: to_binary(&ProposeMessageInternal::Propose(proposal.msg.clone()))?, funds: vec![], }; - COMPLETED_PROPOSALS.save( + COMPLETED_PROPOSALS.insert( deps.storage, - id, + &id, &Proposal { status: ProposalStatus::Approved { created_proposal_id: proposal_id, @@ -190,8 +194,8 @@ pub fn execute_approve( deposit: proposal.deposit, }, )?; - CREATED_PROPOSAL_TO_COMPLETED_PROPOSAL.save(deps.storage, proposal_id, &id)?; - PENDING_PROPOSALS.remove(deps.storage, id); + CREATED_PROPOSAL_TO_COMPLETED_PROPOSAL.insert(deps.storage, &proposal_id, &id)?; + PENDING_PROPOSALS.remove(deps.storage, &id)?; Ok(Response::default() .add_message(propose_messsage) @@ -221,12 +225,12 @@ pub fn execute_reject( deposit, .. } = PENDING_PROPOSALS - .may_load(deps.storage, id)? + .get(deps.storage, &id) .ok_or(PreProposeError::ProposalNotFound {})?; - COMPLETED_PROPOSALS.save( + COMPLETED_PROPOSALS.insert( deps.storage, - id, + &id, &Proposal { status: ProposalStatus::Rejected {}, approval_id, @@ -235,7 +239,7 @@ pub fn execute_reject( deposit: deposit.clone(), }, )?; - PENDING_PROPOSALS.remove(deps.storage, id); + PENDING_PROPOSALS.remove(deps.storage, &id)?; let messages = if let Some(ref deposit_info) = deposit { // Refund can be issued if proposal if deposits are always @@ -246,7 +250,7 @@ pub fn execute_reject( } else { // If the proposer doesn't get the deposit, the DAO does. let dao = PrePropose::default().dao.load(deps.storage)?; - deposit_info.get_return_deposit_message(&dao)? + deposit_info.get_return_deposit_message(&dao.addr)? } } else { vec![] @@ -255,7 +259,7 @@ pub fn execute_reject( Ok(Response::default() .add_attribute("method", "proposal_rejected") .add_attribute("proposal", id.to_string()) - .add_attribute("deposit_info", to_json_binary(&deposit)?.to_string()) + .add_attribute("deposit_info", to_binary(&deposit)?.to_string()) .add_messages(messages)) } @@ -281,6 +285,7 @@ pub fn execute_add_approver_hook( deps: DepsMut, info: MessageInfo, address: String, + code_hash: String, ) -> Result { let pre_propose_base = PrePropose::default(); @@ -288,14 +293,14 @@ pub fn execute_add_approver_hook( let approver = APPROVER.load(deps.storage)?; // Check sender is the approver or the parent DAO - if approver != info.sender && dao != info.sender { + if approver != info.sender && dao.addr != info.sender { return Err(PreProposeError::Unauthorized {}); } let addr = deps.api.addr_validate(&address)?; pre_propose_base .proposal_submitted_hooks - .add_hook(deps.storage, addr)?; + .add_hook(deps.storage, HookItem { addr, code_hash })?; Ok(Response::default()) } @@ -304,6 +309,7 @@ pub fn execute_remove_approver_hook( deps: DepsMut, info: MessageInfo, address: String, + code_hash: String, ) -> Result { let pre_propose_base = PrePropose::default(); @@ -311,7 +317,7 @@ pub fn execute_remove_approver_hook( let approver = APPROVER.load(deps.storage)?; // Check sender is the approver or the parent DAO - if approver != info.sender && dao != info.sender { + if approver != info.sender && dao.addr != info.sender { return Err(PreProposeError::Unauthorized {}); } @@ -321,7 +327,7 @@ pub fn execute_remove_approver_hook( // remove hook pre_propose_base .proposal_submitted_hooks - .remove_hook(deps.storage, addr)?; + .remove_hook(deps.storage, HookItem { addr, code_hash })?; Ok(Response::default()) } @@ -330,72 +336,129 @@ pub fn execute_remove_approver_hook( pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::QueryExtension { msg } => match msg { - QueryExt::Approver {} => to_json_binary(&APPROVER.load(deps.storage)?), + QueryExt::Approver {} => to_binary(&APPROVER.load(deps.storage)?), QueryExt::IsPending { id } => { - let pending = PENDING_PROPOSALS.may_load(deps.storage, id)?.is_some(); + let pending = PENDING_PROPOSALS.get(deps.storage, &id).is_some(); // Force load completed proposal if not pending, throwing error // if not found. if !pending { - COMPLETED_PROPOSALS.load(deps.storage, id)?; + COMPLETED_PROPOSALS.get(deps.storage, &id); } - to_json_binary(&pending) + to_binary(&pending) } QueryExt::Proposal { id } => { - if let Some(pending) = PENDING_PROPOSALS.may_load(deps.storage, id)? { - to_json_binary(&pending) + if let Some(pending) = PENDING_PROPOSALS.get(deps.storage, &id) { + to_binary(&pending) } else { // Force load completed proposal if not pending, throwing // error if not found. - to_json_binary(&COMPLETED_PROPOSALS.load(deps.storage, id)?) + to_binary(&COMPLETED_PROPOSALS.get(deps.storage, &id)) } } QueryExt::PendingProposal { id } => { - to_json_binary(&PENDING_PROPOSALS.load(deps.storage, id)?) + to_binary(&PENDING_PROPOSALS.get(deps.storage, &id)) } QueryExt::PendingProposals { start_after, limit } => { - to_json_binary(&paginate_map_values( - deps, - &PENDING_PROPOSALS, - start_after, - limit, - Order::Ascending, - )?) + let mut res: Vec = Vec::new(); + let mut start = start_after.clone(); + let binding = &PENDING_PROPOSALS; + let iter = binding.iter(deps.storage)?; + for item in iter { + let (id, proposal) = item?; + if let Some(start_after) = &start { + if &id == start_after { + // If we found the start point, reset it to start iterating + start = None; + } + } + if start.is_none() { + res.push(proposal); + if res.len() >= limit.unwrap_or_default() as usize { + break; // Break out of loop if limit reached + } + } + } + to_binary(&res) } QueryExt::ReversePendingProposals { start_before, limit, - } => to_json_binary(&paginate_map_values( - deps, - &PENDING_PROPOSALS, - start_before, - limit, - Order::Descending, - )?), + } => { + let mut res: Vec = Vec::new(); + let mut start = start_before.clone(); + let binding = &PENDING_PROPOSALS; + let iter = binding.iter(deps.storage)?.rev(); // Iterate in reverse + for item in iter { + let (id, proposal) = item?; + if let Some(start_before) = &start { + if &id == start_before { + // If we found the start point, reset it to start iterating + start = None; + } + } + if start.is_none() { + res.push(proposal); + if res.len() >= limit.unwrap_or_default() as usize { + break; // Break out of loop if limit reached + } + } + } + to_binary(&res) + } + QueryExt::CompletedProposal { id } => { - to_json_binary(&COMPLETED_PROPOSALS.load(deps.storage, id)?) + to_binary(&COMPLETED_PROPOSALS.get(deps.storage, &id)) } QueryExt::CompletedProposals { start_after, limit } => { - to_json_binary(&paginate_map_values( - deps, - &COMPLETED_PROPOSALS, - start_after, - limit, - Order::Ascending, - )?) + let mut res: Vec = Vec::new(); + let mut start = start_after.clone(); + let binding = &COMPLETED_PROPOSALS; + let iter = binding.iter(deps.storage)?; + for item in iter { + let (id, proposal) = item?; + if let Some(start_after) = &start { + if &id == start_after { + // If we found the start point, reset it to start iterating + start = None; + } + } + if start.is_none() { + res.push(proposal); + if res.len() >= limit.unwrap_or_default() as usize { + break; // Break out of loop if limit reached + } + } + } + to_binary(&res) } QueryExt::ReverseCompletedProposals { start_before, limit, - } => to_json_binary(&paginate_map_values( - deps, - &COMPLETED_PROPOSALS, - start_before, - limit, - Order::Descending, - )?), + } => { + let mut res: Vec = Vec::new(); + let mut start = start_before.clone(); + let binding = &COMPLETED_PROPOSALS; + let iter = binding.iter(deps.storage)?.rev(); // Iterate in reverse + for item in iter { + let (id, proposal) = item?; + if let Some(start_before) = &start { + if &id == start_before { + // If we found the start point, reset it to start iterating + start = None; + } + } + if start.is_none() { + res.push(proposal); + if res.len() >= limit.unwrap_or_default() as usize { + break; // Break out of loop if limit reached + } + } + } + to_binary(&res) + } QueryExt::CompletedProposalIdForCreatedProposalId { id } => { - to_json_binary(&CREATED_PROPOSAL_TO_COMPLETED_PROPOSAL.may_load(deps.storage, id)?) + to_binary(&CREATED_PROPOSAL_TO_COMPLETED_PROPOSAL.get(deps.storage, &id)) } }, _ => PrePropose::default().query(deps, env, msg), diff --git a/contracts/pre-propose/dao-pre-propose-approval-single/src/lib.rs b/contracts/pre-propose/dao-pre-propose-approval-single/src/lib.rs index 47ba417..9b62a89 100644 --- a/contracts/pre-propose/dao-pre-propose-approval-single/src/lib.rs +++ b/contracts/pre-propose/dao-pre-propose-approval-single/src/lib.rs @@ -4,8 +4,8 @@ pub mod contract; pub mod msg; pub mod state; -#[cfg(test)] -mod tests; +// #[cfg(test)] +// mod tests; // Exporting these means that contracts interacting with this one don't // need an explicit dependency on the base contract to read queries. diff --git a/contracts/pre-propose/dao-pre-propose-approval-single/src/state.rs b/contracts/pre-propose/dao-pre-propose-approval-single/src/state.rs index 5ceb766..553a73b 100644 --- a/contracts/pre-propose/dao-pre-propose-approval-single/src/state.rs +++ b/contracts/pre-propose/dao-pre-propose-approval-single/src/state.rs @@ -1,6 +1,8 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, StdResult, Storage}; -use cw_storage_plus::{Item, Map}; +use secret_storage_plus::Item; +use secret_toolkit::serialization::Json; +use secret_toolkit::storage::Keymap; use dao_voting::deposit::CheckedDepositInfo; use dao_voting::proposal::SingleChoiceProposeMsg as ProposeMsg; @@ -35,10 +37,10 @@ pub struct Proposal { } pub const APPROVER: Item = Item::new("approver"); -pub const PENDING_PROPOSALS: Map = Map::new("pending_proposals"); -pub const COMPLETED_PROPOSALS: Map = Map::new("completed_proposals"); -pub const CREATED_PROPOSAL_TO_COMPLETED_PROPOSAL: Map = - Map::new("created_to_completed_proposal"); +pub const PENDING_PROPOSALS: Keymap = Keymap::new(b"pending_proposals"); +pub const COMPLETED_PROPOSALS: Keymap = Keymap::new(b"completed_proposals"); +pub const CREATED_PROPOSAL_TO_COMPLETED_PROPOSAL: Keymap = + Keymap::new(b"created_to_completed_proposal"); /// Used internally to track the current approval_id. const CURRENT_ID: Item = Item::new("current_id"); diff --git a/contracts/pre-propose/dao-pre-propose-approver/Cargo.toml b/contracts/pre-propose/dao-pre-propose-approver/Cargo.toml index 8378f65..7ae6e54 100644 --- a/contracts/pre-propose/dao-pre-propose-approver/Cargo.toml +++ b/contracts/pre-propose/dao-pre-propose-approver/Cargo.toml @@ -19,18 +19,19 @@ library = [] [dependencies] cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } -cw-storage-plus = { workspace = true } -cw-utils = { workspace = true } -cw2 = { workspace = true } +secret-storage-plus = { workspace = true } +secret-utils = { workspace = true } +secret-cw2 = { workspace = true } dao-interface = { workspace = true } dao-pre-propose-base = { workspace = true } dao-pre-propose-approval-single = { workspace = true, features = ["library"] } dao-voting = { workspace = true } +secret-toolkit = { workspace = true } [dev-dependencies] cw-denom = { workspace = true } -cw-multi-test = { workspace = true } -cw-utils = { workspace = true } +secret-multi-test = { workspace = true } +secret-utils = { workspace = true } cw4-group = { workspace = true } cw20 = { workspace = true } cw20-base = { workspace = true } diff --git a/contracts/pre-propose/dao-pre-propose-approver/src/contract.rs b/contracts/pre-propose/dao-pre-propose-approver/src/contract.rs index 1199283..bb8b6a7 100644 --- a/contracts/pre-propose/dao-pre-propose-approver/src/contract.rs +++ b/contracts/pre-propose/dao-pre-propose-approver/src/contract.rs @@ -1,12 +1,12 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, + to_binary, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, WasmMsg, }; -use cw2::set_contract_version; +use secret_cw2::set_contract_version; -use dao_interface::state::ModuleInstantiateCallback; +use dao_interface::state::{AnyContractInfo, ModuleInstantiateCallback}; use dao_pre_propose_approval_single::msg::{ ApproverProposeMessage, ExecuteExt as ApprovalExt, ExecuteMsg as PreProposeApprovalExecuteMsg, }; @@ -39,6 +39,7 @@ pub fn instantiate( deposit_info: None, open_proposal_submission: false, extension: Empty {}, + proposal_module_code_hash: msg.proposal_module_code_hash, }; // Default pre-propose-base instantiation let resp = PrePropose::default().instantiate( @@ -51,25 +52,34 @@ pub fn instantiate( // Validate and save the address of the pre-propose-approval-single contract let addr = deps.api.addr_validate(&msg.pre_propose_approval_contract)?; - PRE_PROPOSE_APPROVAL_CONTRACT.save(deps.storage, &addr)?; + PRE_PROPOSE_APPROVAL_CONTRACT.save( + deps.storage, + &AnyContractInfo { + addr: addr.clone(), + code_hash: msg.pre_propose_approval_contract_code_hash.clone(), + }, + )?; - Ok(resp.set_data(to_json_binary(&ModuleInstantiateCallback { + Ok(resp.set_data(to_binary(&ModuleInstantiateCallback { msgs: vec![ CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: addr.to_string(), - msg: to_json_binary(&PreProposeApprovalExecuteMsg::AddProposalSubmittedHook { + contract_addr: addr.clone().to_string(), + code_hash: msg.pre_propose_approval_contract_code_hash.clone(), + msg: to_binary(&PreProposeApprovalExecuteMsg::AddProposalSubmittedHook { address: env.contract.address.to_string(), + code_hash: env.contract.code_hash.clone(), })?, funds: vec![], }), CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: addr.to_string(), - msg: to_json_binary(&PreProposeApprovalExecuteMsg::Extension { + msg: to_binary(&PreProposeApprovalExecuteMsg::Extension { msg: ApprovalExt::UpdateApprover { address: env.contract.address.to_string(), }, })?, funds: vec![], + code_hash: msg.pre_propose_approval_contract_code_hash, }), ], })?)) @@ -84,7 +94,7 @@ pub fn execute( ) -> Result { match msg { // Override default pre-propose-base behavior - ExecuteMsg::Propose { msg } => execute_propose(deps, info, msg), + ExecuteMsg::Propose { msg, .. } => execute_propose(deps, info, msg), ExecuteMsg::ProposalCompletedHook { proposal_id, new_status, @@ -103,7 +113,7 @@ pub fn execute_propose( ) -> Result { // Check that this is coming from the expected approval contract let approval_contract = PRE_PROPOSE_APPROVAL_CONTRACT.load(deps.storage)?; - if info.sender != approval_contract { + if info.sender != approval_contract.addr { return Err(PreProposeError::Unauthorized {}); } @@ -127,15 +137,17 @@ pub fn execute_propose( let proposal_module = PrePropose::default().proposal_module.load(deps.storage)?; let proposal_id = deps.querier.query_wasm_smart( - &proposal_module, + proposal_module.code_hash.clone(), + proposal_module.addr.clone(), &dao_interface::proposal::Query::NextProposalId {}, )?; - PROPOSAL_ID_TO_PRE_PROPOSE_ID.save(deps.storage, proposal_id, &pre_propose_id)?; - PRE_PROPOSE_ID_TO_PROPOSAL_ID.save(deps.storage, pre_propose_id, &proposal_id)?; + PROPOSAL_ID_TO_PRE_PROPOSE_ID.insert(deps.storage, &proposal_id, &pre_propose_id)?; + PRE_PROPOSE_ID_TO_PROPOSAL_ID.insert(deps.storage, &pre_propose_id, &proposal_id)?; let propose_messsage = WasmMsg::Execute { - contract_addr: proposal_module.into_string(), - msg: to_json_binary(&sanitized_msg)?, + contract_addr: proposal_module.addr.into_string(), + code_hash: proposal_module.code_hash, + msg: to_binary(&sanitized_msg)?, funds: vec![], }; Ok(Response::default().add_message(propose_messsage)) @@ -149,12 +161,12 @@ pub fn execute_proposal_completed( ) -> Result { // Safety check, this message can only come from the proposal module let proposal_module = PrePropose::default().proposal_module.load(deps.storage)?; - if info.sender != proposal_module { + if info.sender != proposal_module.addr { return Err(PreProposeError::NotModule {}); } // Get approval pre-propose id - let pre_propose_id = PROPOSAL_ID_TO_PRE_PROPOSE_ID.load(deps.storage, proposal_id)?; + let pre_propose_id = PROPOSAL_ID_TO_PRE_PROPOSE_ID.get(deps.storage, &proposal_id); // Get approval contract address let approval_contract = PRE_PROPOSE_APPROVAL_CONTRACT.load(deps.storage)?; @@ -162,16 +174,22 @@ pub fn execute_proposal_completed( // On completion send rejection or approval message let msg = match new_status { Status::Closed => Some(WasmMsg::Execute { - contract_addr: approval_contract.into_string(), - msg: to_json_binary(&PreProposeApprovalExecuteMsg::Extension { - msg: ApprovalExt::Reject { id: pre_propose_id }, + contract_addr: approval_contract.addr.clone().into_string(), + code_hash: approval_contract.code_hash.clone(), + msg: to_binary(&PreProposeApprovalExecuteMsg::Extension { + msg: ApprovalExt::Reject { + id: pre_propose_id.unwrap(), + }, })?, funds: vec![], }), Status::Executed => Some(WasmMsg::Execute { - contract_addr: approval_contract.into_string(), - msg: to_json_binary(&PreProposeApprovalExecuteMsg::Extension { - msg: ApprovalExt::Approve { id: pre_propose_id }, + contract_addr: approval_contract.addr.into_string(), + code_hash: approval_contract.code_hash, + msg: to_binary(&PreProposeApprovalExecuteMsg::Extension { + msg: ApprovalExt::Approve { + id: pre_propose_id.unwrap(), + }, })?, funds: vec![], }), @@ -195,7 +213,7 @@ pub fn execute_reset_approver( ) -> Result { // Check that this is coming from the DAO. let dao = PrePropose::default().dao.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr { return Err(PreProposeError::Unauthorized {}); } @@ -204,21 +222,24 @@ pub fn execute_reset_approver( let reset_messages = vec![ // Remove the proposal submitted hook. CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: pre_propose_approval_contract.to_string(), - msg: to_json_binary(&PreProposeApprovalExecuteMsg::RemoveProposalSubmittedHook { + contract_addr: pre_propose_approval_contract.addr.clone().to_string(), + msg: to_binary(&PreProposeApprovalExecuteMsg::RemoveProposalSubmittedHook { address: env.contract.address.to_string(), + code_hash: env.contract.code_hash, })?, funds: vec![], + code_hash: pre_propose_approval_contract.code_hash.clone(), }), // Set pre-propose-approval approver to the DAO. CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: pre_propose_approval_contract.to_string(), - msg: to_json_binary(&PreProposeApprovalExecuteMsg::Extension { + contract_addr: pre_propose_approval_contract.addr.to_string(), + msg: to_binary(&PreProposeApprovalExecuteMsg::Extension { msg: ApprovalExt::UpdateApprover { - address: dao.to_string(), + address: dao.addr.to_string(), }, })?, funds: vec![], + code_hash: pre_propose_approval_contract.code_hash, }), ]; @@ -230,13 +251,13 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::QueryExtension { msg } => match msg { QueryExt::PreProposeApprovalContract {} => { - to_json_binary(&PRE_PROPOSE_APPROVAL_CONTRACT.load(deps.storage)?) + to_binary(&PRE_PROPOSE_APPROVAL_CONTRACT.load(deps.storage)?) } QueryExt::PreProposeApprovalIdForApproverProposalId { id } => { - to_json_binary(&PROPOSAL_ID_TO_PRE_PROPOSE_ID.may_load(deps.storage, id)?) + to_binary(&PROPOSAL_ID_TO_PRE_PROPOSE_ID.get(deps.storage, &id)) } QueryExt::ApproverProposalIdForPreProposeApprovalId { id } => { - to_json_binary(&PRE_PROPOSE_ID_TO_PROPOSAL_ID.may_load(deps.storage, id)?) + to_binary(&PRE_PROPOSE_ID_TO_PROPOSAL_ID.get(deps.storage, &id)) } }, _ => PrePropose::default().query(deps, env, msg), diff --git a/contracts/pre-propose/dao-pre-propose-approver/src/lib.rs b/contracts/pre-propose/dao-pre-propose-approver/src/lib.rs index 47ba417..9b62a89 100644 --- a/contracts/pre-propose/dao-pre-propose-approver/src/lib.rs +++ b/contracts/pre-propose/dao-pre-propose-approver/src/lib.rs @@ -4,8 +4,8 @@ pub mod contract; pub mod msg; pub mod state; -#[cfg(test)] -mod tests; +// #[cfg(test)] +// mod tests; // Exporting these means that contracts interacting with this one don't // need an explicit dependency on the base contract to read queries. diff --git a/contracts/pre-propose/dao-pre-propose-approver/src/msg.rs b/contracts/pre-propose/dao-pre-propose-approver/src/msg.rs index 7d45298..aa3f55f 100644 --- a/contracts/pre-propose/dao-pre-propose-approver/src/msg.rs +++ b/contracts/pre-propose/dao-pre-propose-approver/src/msg.rs @@ -8,6 +8,8 @@ use dao_pre_propose_base::msg::{ #[cw_serde] pub struct InstantiateMsg { pub pre_propose_approval_contract: String, + pub pre_propose_approval_contract_code_hash: String, + pub proposal_module_code_hash: String, } #[cw_serde] @@ -20,7 +22,7 @@ pub enum ExecuteExt { #[cw_serde] #[derive(QueryResponses)] pub enum QueryExt { - #[returns(cosmwasm_std::Addr)] + #[returns(dao_interface::state::AnyContractInfo)] PreProposeApprovalContract {}, #[returns(::std::option::Option)] PreProposeApprovalIdForApproverProposalId { id: u64 }, diff --git a/contracts/pre-propose/dao-pre-propose-approver/src/state.rs b/contracts/pre-propose/dao-pre-propose-approver/src/state.rs index b09012e..baa9b97 100644 --- a/contracts/pre-propose/dao-pre-propose-approver/src/state.rs +++ b/contracts/pre-propose/dao-pre-propose-approver/src/state.rs @@ -1,9 +1,13 @@ -use cosmwasm_std::Addr; -use cw_storage_plus::{Item, Map}; +use dao_interface::state::AnyContractInfo; +use secret_storage_plus::Item; +use secret_toolkit::{serialization::Json, storage::Keymap}; // Stores the address of the pre-propose approval contract -pub const PRE_PROPOSE_APPROVAL_CONTRACT: Item = Item::new("pre_propose_approval_contract"); +pub const PRE_PROPOSE_APPROVAL_CONTRACT: Item = + Item::new("pre_propose_approval_contract"); // Maps proposal ids to pre-propose ids -pub const PROPOSAL_ID_TO_PRE_PROPOSE_ID: Map = Map::new("proposal_to_pre_propose"); +pub const PROPOSAL_ID_TO_PRE_PROPOSE_ID: Keymap = + Keymap::new(b"proposal_to_pre_propose"); // Maps pre-propose ids to proposal ids -pub const PRE_PROPOSE_ID_TO_PROPOSAL_ID: Map = Map::new("pre_propose_to_proposal"); +pub const PRE_PROPOSE_ID_TO_PROPOSAL_ID: Keymap = + Keymap::new(b"pre_propose_to_proposal"); diff --git a/contracts/pre-propose/dao-pre-propose-multiple/Cargo.toml b/contracts/pre-propose/dao-pre-propose-multiple/Cargo.toml index 0f13099..be0e154 100644 --- a/contracts/pre-propose/dao-pre-propose-multiple/Cargo.toml +++ b/contracts/pre-propose/dao-pre-propose-multiple/Cargo.toml @@ -19,13 +19,13 @@ library = [] [dependencies] cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } -cw2 = { workspace = true } +secret-cw2 = { workspace = true } dao-pre-propose-base = { workspace = true } dao-voting = { workspace = true } [dev-dependencies] -cw-multi-test = { workspace = true } -cw-utils = { workspace = true } +secret-multi-test = { workspace = true } +secret-utils = { workspace = true } cw4-group = { workspace = true } cw20 = { workspace = true } cw20-base = { workspace = true } diff --git a/contracts/pre-propose/dao-pre-propose-multiple/src/contract.rs b/contracts/pre-propose/dao-pre-propose-multiple/src/contract.rs index cfe0676..651138e 100644 --- a/contracts/pre-propose/dao-pre-propose-multiple/src/contract.rs +++ b/contracts/pre-propose/dao-pre-propose-multiple/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult}; -use cw2::set_contract_version; +use secret_cw2::set_contract_version; use dao_pre_propose_base::{ error::PreProposeError, @@ -74,6 +74,7 @@ pub fn execute( description, choices, }, + key, } => ExecuteInternal::Propose { msg: ProposeMessageInternal::Propose { proposer: Some(info.sender.to_string()), @@ -81,9 +82,10 @@ pub fn execute( description, choices, }, + key, }, ExecuteMsg::Extension { msg } => ExecuteInternal::Extension { msg }, - ExecuteMsg::Withdraw { denom } => ExecuteInternal::Withdraw { denom }, + ExecuteMsg::Withdraw { denom, key } => ExecuteInternal::Withdraw { denom, key }, ExecuteMsg::UpdateConfig { deposit_info, open_proposal_submission, @@ -91,11 +93,11 @@ pub fn execute( deposit_info, open_proposal_submission, }, - ExecuteMsg::AddProposalSubmittedHook { address } => { - ExecuteInternal::AddProposalSubmittedHook { address } + ExecuteMsg::AddProposalSubmittedHook { address, code_hash } => { + ExecuteInternal::AddProposalSubmittedHook { address, code_hash } } - ExecuteMsg::RemoveProposalSubmittedHook { address } => { - ExecuteInternal::RemoveProposalSubmittedHook { address } + ExecuteMsg::RemoveProposalSubmittedHook { address, code_hash } => { + ExecuteInternal::RemoveProposalSubmittedHook { address, code_hash } } ExecuteBase::ProposalCompletedHook { proposal_id, diff --git a/contracts/pre-propose/dao-pre-propose-multiple/src/lib.rs b/contracts/pre-propose/dao-pre-propose-multiple/src/lib.rs index 16d6ba5..ff777d6 100644 --- a/contracts/pre-propose/dao-pre-propose-multiple/src/lib.rs +++ b/contracts/pre-propose/dao-pre-propose-multiple/src/lib.rs @@ -2,8 +2,8 @@ pub mod contract; -#[cfg(test)] -mod tests; +// #[cfg(test)] +// mod tests; pub use contract::{ExecuteMsg, InstantiateMsg, ProposeMessage, QueryMsg}; diff --git a/contracts/pre-propose/dao-pre-propose-single/src/contract.rs b/contracts/pre-propose/dao-pre-propose-single/src/contract.rs index 9fa4fc2..abd5370 100644 --- a/contracts/pre-propose/dao-pre-propose-single/src/contract.rs +++ b/contracts/pre-propose/dao-pre-propose-single/src/contract.rs @@ -75,14 +75,15 @@ pub fn execute( description, msgs, }, + key, } => ExecuteInternal::Propose { msg: ProposeMessageInternal::Propose(ProposeMsg { - // Fill in proposer based on message sender. proposer: Some(info.sender.to_string()), title, description, msgs, }), + key, }, ExecuteMsg::Extension { msg } => ExecuteInternal::Extension { msg }, ExecuteMsg::Withdraw { denom, key } => ExecuteInternal::Withdraw { denom, key }, diff --git a/contracts/proposal/dao-proposal-condorcet/Cargo.toml b/contracts/proposal/dao-proposal-condorcet/Cargo.toml index 0efb57e..712a9b9 100644 --- a/contracts/proposal/dao-proposal-condorcet/Cargo.toml +++ b/contracts/proposal/dao-proposal-condorcet/Cargo.toml @@ -29,6 +29,7 @@ thiserror = { workspace = true } serde ={ workspace = true } schemars ={ workspace = true } secret-toolkit ={ workspace = true } +secret-cw-controllers ={ workspace = true } [dev-dependencies] cosmwasm-schema = { workspace = true } diff --git a/contracts/proposal/dao-proposal-condorcet/src/config.rs b/contracts/proposal/dao-proposal-condorcet/src/config.rs index e592733..73969e5 100644 --- a/contracts/proposal/dao-proposal-condorcet/src/config.rs +++ b/contracts/proposal/dao-proposal-condorcet/src/config.rs @@ -1,9 +1,9 @@ use cosmwasm_schema::cw_serde; -use secret_utils::Duration; use dao_voting::{ threshold::{validate_quorum, PercentageThreshold}, voting::validate_voting_period, }; +use secret_utils::Duration; use crate::ContractError; @@ -22,7 +22,6 @@ pub(crate) struct Config { pub voting_period: Duration, pub min_voting_period: Option, pub close_proposals_on_execution_failure: bool, - pub dao_code_hash: String, } impl UncheckedConfig { @@ -35,7 +34,6 @@ impl UncheckedConfig { close_proposals_on_execution_failure: self.close_proposals_on_execution_failure, voting_period, min_voting_period, - dao_code_hash: self.dao_code_hash }) } } diff --git a/contracts/proposal/dao-proposal-condorcet/src/contract.rs b/contracts/proposal/dao-proposal-condorcet/src/contract.rs index a85e179..3a25521 100644 --- a/contracts/proposal/dao-proposal-condorcet/src/contract.rs +++ b/contracts/proposal/dao-proposal-condorcet/src/contract.rs @@ -1,18 +1,20 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, + to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, + SubMsgResult, }; -use secret_cw2::set_contract_version; -use dao_voting::reply::TaggedReplyId; +use dao_interface::state::AnyContractInfo; use dao_voting::voting::{get_total_power, get_voting_power}; +use secret_cw2::set_contract_version; +use secret_cw_controllers::ReplyEvent; use crate::config::UncheckedConfig; use crate::error::ContractError; use crate::msg::{Choice, ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::proposal::{Proposal, ProposalResponse, Status}; -use crate::state::{next_proposal_id, CONFIG, DAO, PROPOSAL, TALLY, VOTE}; +use crate::state::{next_proposal_id, CONFIG, DAO, PROPOSAL, REPLY_IDS, TALLY, VOTE}; use crate::tally::Tally; use crate::vote::Vote; @@ -28,7 +30,13 @@ pub fn instantiate( ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - DAO.save(deps.storage, &info.sender)?; + DAO.save( + deps.storage, + &AnyContractInfo { + addr: info.sender.clone(), + code_hash: msg.dao_code_hash.clone(), + }, + )?; CONFIG.save(deps.storage, &msg.into_checked()?)?; Ok(Response::default() @@ -88,9 +96,15 @@ pub fn execute( msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::Propose { choices } => execute_propose(deps, env, info, choices), - ExecuteMsg::Vote { proposal_id, vote } => execute_vote(deps, env, info, proposal_id, vote), - ExecuteMsg::Execute { proposal_id } => execute_execute(deps, env, info, proposal_id), + ExecuteMsg::Propose { choices, key } => execute_propose(deps, env, info, choices, key), + ExecuteMsg::Vote { + proposal_id, + vote, + key, + } => execute_vote(deps, env, info, proposal_id, vote, key), + ExecuteMsg::Execute { proposal_id, key } => { + execute_execute(deps, env, info, proposal_id, key) + } ExecuteMsg::Close { proposal_id } => execute_close(deps, env, info, proposal_id), ExecuteMsg::SetConfig(config) => execute_set_config(deps, info, config), @@ -102,16 +116,24 @@ fn execute_propose( env: Env, info: MessageInfo, choices: Vec, + key: String, ) -> Result { let dao = DAO.load(deps.storage)?; let config = CONFIG.load(deps.storage)?; - let sender_voting_power = get_voting_power(deps.as_ref(), config.dao_code_hash.clone(),info.sender.clone(), &dao, None)?; + let sender_voting_power = get_voting_power( + deps.as_ref(), + dao.code_hash.clone(), + info.sender.clone(), + key, + &dao.addr.clone(), + None, + )?; if sender_voting_power.is_zero() { return Err(ContractError::ZeroVotingPower {}); } let id = next_proposal_id(deps.storage)?; - let total_power = get_total_power(deps.as_ref(), config.dao_code_hash.clone(),&dao, None)?; + let total_power = get_total_power(deps.as_ref(), dao.code_hash.clone(), &dao.addr, None)?; if choices.is_empty() { return Err(ContractError::ZeroChoices {}); @@ -145,13 +167,15 @@ fn execute_vote( info: MessageInfo, proposal_id: u32, vote: Vec, + key: String, ) -> Result { let tally = TALLY.get(deps.storage, &proposal_id); let sender_power = get_voting_power( deps.as_ref(), - CONFIG.load(deps.storage)?.dao_code_hash.clone(), + DAO.load(deps.storage)?.code_hash.clone(), info.sender.clone(), - &DAO.load(deps.storage)?, + key, + &DAO.load(deps.storage)?.addr, Some(tally.clone().unwrap().start_height), )?; if sender_power.is_zero() { @@ -164,7 +188,7 @@ fn execute_vote( let vote = Vote::new(vote, tally.clone().unwrap().candidates())?; VOTE.insert(deps.storage, &(proposal_id, info.sender.clone()), &vote)?; - let tally = tally; + let tally = tally; tally.clone().unwrap().add_vote(vote, sender_power); TALLY.insert(deps.storage, &proposal_id, &tally.clone().unwrap())?; @@ -181,24 +205,35 @@ fn execute_execute( env: Env, info: MessageInfo, proposal_id: u32, + key: String, ) -> Result { let tally = TALLY.get(deps.storage, &proposal_id); let dao = DAO.load(deps.storage)?; let sender_power = get_voting_power( deps.as_ref(), - CONFIG.load(deps.storage)?.dao_code_hash.clone(), + dao.code_hash.clone(), info.sender.clone(), - &dao, + key, + &dao.addr.clone(), Some(tally.clone().unwrap().start_height), )?; if sender_power.is_zero() { return Err(ContractError::ZeroVotingPower {}); } - let proposal = PROPOSAL.get(deps.storage, &proposal_id); - if let Status::Passed { winner } = proposal.clone().unwrap().update_status(&env.block, &tally.clone().unwrap()) { - let msgs = proposal.clone().unwrap().set_executed(dao, CONFIG.load(deps.storage)?.dao_code_hash.clone(),winner)?; - PROPOSAL.insert(deps.storage,&proposal_id, &proposal.clone().unwrap())?; + let proposal = PROPOSAL.get(deps.storage, &proposal_id); + if let Status::Passed { winner } = proposal + .clone() + .unwrap() + .update_status(&env.block, &tally.clone().unwrap()) + { + let msgs = proposal.clone().unwrap().set_executed( + deps.storage, + dao.addr, + dao.code_hash.clone(), + winner, + )?; + PROPOSAL.insert(deps.storage, &proposal_id, &proposal.clone().unwrap())?; Ok(Response::default() .add_attribute("method", "execute") @@ -217,8 +252,12 @@ fn execute_close( proposal_id: u32, ) -> Result { let tally = TALLY.get(deps.storage, &proposal_id); - let proposal = PROPOSAL.get(deps.storage, &proposal_id); - if let Status::Rejected = proposal.clone().unwrap().update_status(&env.block, &tally.unwrap()) { + let proposal = PROPOSAL.get(deps.storage, &proposal_id); + if let Status::Rejected = proposal + .clone() + .unwrap() + .update_status(&env.block, &tally.unwrap()) + { proposal.clone().unwrap().set_closed(); PROPOSAL.insert(deps.storage, &proposal_id, &proposal.clone().unwrap())?; @@ -236,7 +275,7 @@ fn execute_set_config( info: MessageInfo, config: UncheckedConfig, ) -> Result { - if info.sender != DAO.load(deps.storage)? { + if info.sender != DAO.load(deps.storage)?.addr { Err(ContractError::NotDao {}) } else { CONFIG.save(deps.storage, &config.into_checked()?)?; @@ -250,10 +289,16 @@ fn execute_set_config( pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::Proposal { id } => { - let proposal = PROPOSAL.get(deps.storage, &id); + let proposal = PROPOSAL.get(deps.storage, &id); let tally = TALLY.get(deps.storage, &id); - proposal.clone().unwrap().update_status(&env.block, &tally.clone().unwrap()); - to_binary(&ProposalResponse { proposal:proposal.unwrap(), tally:tally.unwrap() }) + proposal + .clone() + .unwrap() + .update_status(&env.block, &tally.clone().unwrap()); + to_binary(&ProposalResponse { + proposal: proposal.unwrap(), + tally: tally.unwrap(), + }) } QueryMsg::Config {} => to_binary(&CONFIG.load(deps.storage)?), QueryMsg::NextProposalId {} => to_binary(&next_proposal_id(deps.storage)?), @@ -266,17 +311,24 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { - let repl = TaggedReplyId::new(msg.id)?; + let repl = REPLY_IDS.get_event(deps.storage, msg.id)?; match repl { - TaggedReplyId::FailedProposalExecution(proposal_id) => { - let proposal = PROPOSAL.get(deps.storage, &(proposal_id as u32)); - proposal.clone().unwrap().set_execution_failed(); - PROPOSAL.insert(deps.storage, &(proposal_id as u32), &proposal.clone().unwrap())?; + ReplyEvent::FailedProposalExecution { proposal_id } => match msg.clone().result { + SubMsgResult::Err(err) => Err(ContractError::Std(StdError::GenericErr { msg: err })), + SubMsgResult::Ok(_) => { + let proposal = PROPOSAL.get(deps.storage, &(proposal_id as u32)); + proposal.clone().unwrap().set_execution_failed(); + PROPOSAL.insert( + deps.storage, + &(proposal_id as u32), + &proposal.clone().unwrap(), + )?; - Ok(Response::default() - .add_attribute("proposal_execution_failed", proposal_id.to_string()) - .add_attribute("error", msg.result.into_result().err().unwrap_or_default())) - } + Ok(Response::default() + .add_attribute("proposal_execution_failed", proposal_id.to_string()) + .add_attribute("error", msg.result.into_result().err().unwrap_or_default())) + } + }, _ => unimplemented!("pre-propose and hooks not yet supported"), } } diff --git a/contracts/proposal/dao-proposal-condorcet/src/error.rs b/contracts/proposal/dao-proposal-condorcet/src/error.rs index 6d280bf..9d0eacb 100644 --- a/contracts/proposal/dao-proposal-condorcet/src/error.rs +++ b/contracts/proposal/dao-proposal-condorcet/src/error.rs @@ -1,5 +1,7 @@ use cosmwasm_std::StdError; use dao_voting::{error::VotingError, reply::error::TagError, threshold::ThresholdError}; +use secret_cw_controllers::ReplyError; +use secret_utils::ParseReplyError; use thiserror::Error; use crate::vote::VoteError; @@ -17,6 +19,12 @@ pub enum ContractError { #[error(transparent)] Tag(#[from] TagError), + #[error(transparent)] + ReplyUdError(#[from] ReplyError), + + #[error(transparent)] + ParseReplyError(#[from] ParseReplyError), + #[error("non-zero voting power required to perform this action")] ZeroVotingPower {}, diff --git a/contracts/proposal/dao-proposal-condorcet/src/msg.rs b/contracts/proposal/dao-proposal-condorcet/src/msg.rs index ab6f5aa..e3b4ac6 100644 --- a/contracts/proposal/dao-proposal-condorcet/src/msg.rs +++ b/contracts/proposal/dao-proposal-condorcet/src/msg.rs @@ -14,10 +14,22 @@ pub struct Choice { #[cw_serde] pub enum ExecuteMsg { - Propose { choices: Vec }, - Vote { proposal_id: u32, vote: Vec }, - Execute { proposal_id: u32 }, - Close { proposal_id: u32 }, + Propose { + choices: Vec, + key: String, + }, + Vote { + proposal_id: u32, + vote: Vec, + key: String, + }, + Execute { + proposal_id: u32, + key: String, + }, + Close { + proposal_id: u32, + }, SetConfig(UncheckedConfig), } diff --git a/contracts/proposal/dao-proposal-condorcet/src/proposal.rs b/contracts/proposal/dao-proposal-condorcet/src/proposal.rs index 91e9784..0f1f393 100644 --- a/contracts/proposal/dao-proposal-condorcet/src/proposal.rs +++ b/contracts/proposal/dao-proposal-condorcet/src/proposal.rs @@ -1,14 +1,14 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{to_binary, Addr, BlockInfo, StdResult, SubMsg, Uint128, WasmMsg}; +use cosmwasm_std::{Addr, BlockInfo, StdResult, Storage, SubMsg, Uint128}; +use dao_voting::{threshold::PercentageThreshold, voting::does_vote_count_pass}; +use secret_cw_controllers::ReplyEvent; +use secret_toolkit::utils::HandleCallback; use secret_utils::Expiration; -use dao_voting::{ - reply::mask_proposal_execution_proposal_id, threshold::PercentageThreshold, - voting::does_vote_count_pass, -}; use crate::{ config::Config, msg::Choice, + state::REPLY_IDS, tally::{Tally, Winner}, }; @@ -153,23 +153,33 @@ impl Proposal { /// Sets the proposal's status to executed and returns a /// submessage to be executed. - pub(crate) fn set_executed(&mut self, dao: Addr,dao_code_hash: String, winner: u32) -> StdResult { + pub(crate) fn set_executed( + &mut self, + store: &mut dyn Storage, + dao: Addr, + dao_code_hash: String, + winner: u32, + ) -> StdResult { debug_assert_eq!(self.last_status, Status::Passed { winner }); self.last_status = Status::Executed; let msgs = self.choices[winner as usize].msgs.clone(); - let core_exec = WasmMsg::Execute { - code_hash:dao_code_hash, - contract_addr: dao.into_string(), - msg: to_binary(&dao_interface::msg::ExecuteMsg::ExecuteProposalHook { msgs })?, - funds: vec![], - }; + let core_exec = dao_interface::msg::ExecuteMsg::ExecuteProposalHook { msgs }; + Ok(if self.close_on_execution_failure { - let masked_id = mask_proposal_execution_proposal_id(self.id as u64); - SubMsg::reply_on_error(core_exec, masked_id) + let reply_id = REPLY_IDS.add_event( + store, + ReplyEvent::FailedProposalExecution { + proposal_id: self.id as u64, + }, + ); + SubMsg::reply_on_error( + core_exec.to_cosmos_msg(dao_code_hash.clone(), dao.clone().to_string(), None)?, + reply_id.unwrap(), + ) } else { - SubMsg::new(core_exec) + SubMsg::new(core_exec.to_cosmos_msg(dao_code_hash, dao.to_string(), None)?) }) } diff --git a/contracts/proposal/dao-proposal-condorcet/src/state.rs b/contracts/proposal/dao-proposal-condorcet/src/state.rs index 93bfd51..f32adce 100644 --- a/contracts/proposal/dao-proposal-condorcet/src/state.rs +++ b/contracts/proposal/dao-proposal-condorcet/src/state.rs @@ -1,18 +1,22 @@ use cosmwasm_std::{Addr, StdResult, Storage}; +use dao_interface::state::AnyContractInfo; +use secret_cw_controllers::ReplyIds; use secret_storage_plus::Item; use secret_toolkit::{serialization::Json, storage::Keymap}; use crate::{config::Config, proposal::Proposal, tally::Tally, vote::Vote}; -pub(crate) const DAO: Item = Item::new("dao"); +pub(crate) const DAO: Item = Item::new("dao"); pub(crate) const CONFIG: Item = Item::new("config"); -pub(crate) const TALLY: Keymap = Keymap::new(b"tallys"); -pub(crate) const PROPOSAL: Keymap = Keymap::new(b"proposals"); -pub(crate) const VOTE: Keymap<(u32, Addr), Vote,Json> = Keymap::new(b"votes"); +pub(crate) const TALLY: Keymap = Keymap::new(b"tallys"); +pub(crate) const PROPOSAL: Keymap = Keymap::new(b"proposals"); +pub(crate) const VOTE: Keymap<(u32, Addr), Vote, Json> = Keymap::new(b"votes"); +pub(crate) const REPLY_IDS: ReplyIds = ReplyIds::new(b"reply_ids", b"reply_ids_count"); pub(crate) fn next_proposal_id(storage: &dyn Storage) -> StdResult { - PROPOSAL.iter_keys(storage)? + PROPOSAL + .iter_keys(storage)? .next() .transpose() .map(|id| id.unwrap_or(0) + 1) diff --git a/contracts/proposal/dao-proposal-multiple/Cargo.toml b/contracts/proposal/dao-proposal-multiple/Cargo.toml index d7d1dde..0c52e19 100644 --- a/contracts/proposal/dao-proposal-multiple/Cargo.toml +++ b/contracts/proposal/dao-proposal-multiple/Cargo.toml @@ -44,6 +44,7 @@ voting-v1 = { workspace = true } secret-toolkit = { workspace = true } serde ={ workspace=true } schemars ={ workspace = true } +secret-cw-controllers ={ workspace = true } [dev-dependencies] anyhow = { workspace = true } diff --git a/contracts/proposal/dao-proposal-multiple/src/contract.rs b/contracts/proposal/dao-proposal-multiple/src/contract.rs index d89feb9..8a2b68b 100644 --- a/contracts/proposal/dao-proposal-multiple/src/contract.rs +++ b/contracts/proposal/dao-proposal-multiple/src/contract.rs @@ -1,20 +1,17 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, Response, - StdResult, Storage, SubMsg, + to_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, Response, StdError, + StdResult, Storage, SubMsg, SubMsgResult, }; -use secret_cw2::set_contract_version; -use cw_hooks::Hooks; -use secret_toolkit::utils::HandleCallback; -use secret_utils::{parse_reply_instantiate_data, Duration}; +use cw_hooks::{HookItem, Hooks}; use dao_hooks::proposal::{ new_proposal_hooks, proposal_completed_hooks, proposal_status_changed_hooks, }; use dao_hooks::vote::new_vote_hooks; +use dao_interface::state::{AnyContractInfo, VotingModuleInfo}; use dao_interface::voting::IsActiveResponse; -use dao_interface::state::VotingModuleInfo; use dao_voting::veto::{VetoConfig, VetoError}; use dao_voting::{ multiple_choice::{ @@ -22,27 +19,32 @@ use dao_voting::{ }, pre_propose::{PreProposeInfo, ProposalCreationPolicy}, proposal::{DEFAULT_LIMIT, MAX_PROPOSAL_SIZE}, - reply::{ - failed_pre_propose_module_hook_id, mask_proposal_execution_proposal_id, TaggedReplyId, - }, status::Status, voting::{get_total_power, get_voting_power, validate_voting_period}, }; +use secret_cw2::set_contract_version; +use secret_cw_controllers::ReplyEvent; +use secret_toolkit::permit::{Permit, RevokedPermits}; +use secret_toolkit::utils::HandleCallback; +use secret_toolkit::viewing_key::{ViewingKey, ViewingKeyStore}; +use secret_utils::{parse_reply_event_for_contract_address, Duration}; +use crate::msg::{CreateViewingKey, QueryWithPermit, ViewingKeyError}; +use crate::state::{DAO, REPLY_IDS}; use crate::{msg::MigrateMsg, state::CREATION_POLICY}; use crate::{ msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, proposal::{MultipleChoiceProposal, VoteResult}, query::{ProposalListResponse, ProposalResponse, VoteInfo, VoteListResponse, VoteResponse}, - state::{ - Config, BALLOTS, CONFIG, PROPOSALS, PROPOSAL_COUNT, PROPOSAL_HOOKS, VOTE_HOOKS, - }, + state::{Config, BALLOTS, CONFIG, PROPOSALS, PROPOSAL_COUNT, PROPOSAL_HOOKS, VOTE_HOOKS}, ContractError, }; pub const CONTRACT_NAME: &str = "crates.io:dao-proposal-multiple"; pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, @@ -54,14 +56,20 @@ pub fn instantiate( msg.voting_strategy.validate()?; - let dao = info.sender; + DAO.save( + deps.storage, + &AnyContractInfo { + code_hash: msg.dao_code_hash, + addr: info.sender.clone(), + }, + )?; let (min_voting_period, max_voting_period) = validate_voting_period(msg.min_voting_period, msg.max_voting_period)?; let (initial_policy, pre_propose_messages) = msg .pre_propose_info - .into_initial_policy_and_messages(dao.clone())?; + .into_initial_policy_and_messages(deps.storage, info.sender.clone(), REPLY_IDS)?; // if veto is configured, validate its fields if let Some(veto_config) = &msg.veto { @@ -74,8 +82,6 @@ pub fn instantiate( max_voting_period, only_members_execute: msg.only_members_execute, allow_revoting: msg.allow_revoting, - dao, - dao_code_hash:msg.code_hash, close_proposal_on_execution_failure: msg.close_proposal_on_execution_failure, veto: msg.veto, }; @@ -89,7 +95,7 @@ pub fn instantiate( Ok(Response::default() .add_submessages(pre_propose_messages) .add_attribute("action", "instantiate") - .add_attribute("dao", config.dao)) + .add_attribute("dao", info.sender.to_string())) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -115,11 +121,14 @@ pub fn execute( proposer, ), ExecuteMsg::Vote { + key, proposal_id, vote, rationale, - } => execute_vote(deps, env, info, proposal_id, vote, rationale), - ExecuteMsg::Execute { proposal_id } => execute_execute(deps, env, info, proposal_id), + } => execute_vote(deps, env, info, key, proposal_id, vote, rationale), + ExecuteMsg::Execute { key, proposal_id } => { + execute_execute(deps, env, info, key, proposal_id) + } ExecuteMsg::Veto { proposal_id } => execute_veto(deps, env, info, proposal_id), ExecuteMsg::Close { proposal_id } => execute_close(deps, env, info, proposal_id), ExecuteMsg::UpdateConfig { @@ -148,20 +157,25 @@ pub fn execute( ExecuteMsg::UpdatePreProposeInfo { info: new_info } => { execute_update_proposal_creation_policy(deps, info, new_info) } - ExecuteMsg::AddProposalHook { address } => { - execute_add_proposal_hook(deps, env, info, address) + ExecuteMsg::AddProposalHook { address, code_hash } => { + execute_add_proposal_hook(deps, env, info, address, code_hash) + } + ExecuteMsg::RemoveProposalHook { address, code_hash } => { + execute_remove_proposal_hook(deps, env, info, address, code_hash) } - ExecuteMsg::RemoveProposalHook { address } => { - execute_remove_proposal_hook(deps, env, info, address) + ExecuteMsg::AddVoteHook { address, code_hash } => { + execute_add_vote_hook(deps, env, info, address, code_hash) } - ExecuteMsg::AddVoteHook { address } => execute_add_vote_hook(deps, env, info, address), - ExecuteMsg::RemoveVoteHook { address } => { - execute_remove_vote_hook(deps, env, info, address) + ExecuteMsg::RemoveVoteHook { address, code_hash } => { + execute_remove_vote_hook(deps, env, info, address, code_hash) } ExecuteMsg::UpdateRationale { proposal_id, rationale, } => execute_update_rationale(deps, info, proposal_id, rationale), + ExecuteMsg::CreateViewingKey { entropy, .. } => try_create_key(deps, env, info, entropy), + ExecuteMsg::SetViewingKey { key, .. } => try_set_key(deps, info, key), + ExecuteMsg::RevokePermit { permit_name, .. } => revoke_permit(deps, info, permit_name), } } @@ -175,6 +189,7 @@ pub fn execute_propose( proposer: Option, ) -> Result, ContractError> { let config = CONFIG.load(deps.storage)?; + let dao_info = DAO.load(deps.storage)?; let proposal_creation_policy = CREATION_POLICY.load(deps.storage)?; // Check that the sender is permitted to create proposals. @@ -196,8 +211,8 @@ pub fn execute_propose( }; let voting_module: VotingModuleInfo = deps.querier.query_wasm_smart( - config.dao_code_hash.clone(), - config.dao.clone(), + dao_info.code_hash.clone(), + dao_info.addr.clone(), &dao_interface::msg::QueryMsg::VotingModule {}, )?; @@ -205,7 +220,11 @@ pub fn execute_propose( // query. Lacking an implementation they are active by default. let active_resp: IsActiveResponse = deps .querier - .query_wasm_smart(voting_module.code_hash.clone(),voting_module.addr.to_string().clone(), &dao_interface::voting::Query::IsActive {}) + .query_wasm_smart( + voting_module.code_hash.clone(), + voting_module.addr.to_string().clone(), + &dao_interface::voting::Query::IsActive {}, + ) .unwrap_or(IsActiveResponse { active: true }); if !active_resp.active { @@ -216,7 +235,7 @@ pub fn execute_propose( let checked_multiple_choice_options = options.into_checked()?.options; let expiration = config.max_voting_period.after(&env.block); - let total_power = get_total_power(deps.as_ref(), config.dao_code_hash,&config.dao, None)?; + let total_power = get_total_power(deps.as_ref(), dao_info.code_hash, &dao_info.addr, None)?; let proposal = { // Limit mutability to this block. @@ -266,7 +285,7 @@ pub fn execute_propose( PROPOSALS.insert(deps.storage, &id, &proposal)?; - let hooks = new_proposal_hooks(PROPOSAL_HOOKS, deps.storage, id, proposer.as_str(),env.contract.code_hash)?; + let hooks = new_proposal_hooks(PROPOSAL_HOOKS, deps.storage, id, proposer.as_str())?; Ok(Response::default() .add_submessages(hooks) @@ -335,13 +354,12 @@ pub fn execute_veto( proposal_id, old_status.to_string(), prop.status.to_string(), - env.contract.code_hash.clone(), )?; // Add prepropose / deposit module hook which will handle deposit refunds. let proposal_creation_policy = CREATION_POLICY.load(deps.storage)?; let proposal_completed_hooks = - proposal_completed_hooks(proposal_creation_policy, proposal_id, prop.status,env.contract.code_hash.clone())?; + proposal_completed_hooks(proposal_creation_policy, proposal_id, prop.status)?; Ok(Response::new() .add_attribute("action", "veto") @@ -354,11 +372,12 @@ pub fn execute_vote( deps: DepsMut, env: Env, info: MessageInfo, + key: String, proposal_id: u64, vote: MultipleChoiceVote, rationale: Option, ) -> Result, ContractError> { - let config = CONFIG.load(deps.storage)?; + let dao_info = DAO.load(deps.storage)?; let mut prop = PROPOSALS .get(deps.storage, &proposal_id) .ok_or(ContractError::NoSuchProposal { id: proposal_id })?; @@ -381,9 +400,10 @@ pub fn execute_vote( let vote_power = get_voting_power( deps.as_ref(), - config.dao_code_hash.clone(), + dao_info.code_hash.clone(), info.sender.clone(), - &config.dao, + key, + &dao_info.addr, Some(prop.start_height), )?; if vote_power.is_zero() { @@ -451,7 +471,6 @@ pub fn execute_vote( ¤t_ballot.unwrap(), )?; - let old_status = prop.status; prop.votes.add_vote(vote, vote_power)?; @@ -464,7 +483,6 @@ pub fn execute_vote( proposal_id, old_status.to_string(), new_status.to_string(), - env.contract.code_hash.clone() )?; let vote_hooks = new_vote_hooks( VOTE_HOOKS, @@ -472,7 +490,6 @@ pub fn execute_vote( proposal_id, info.sender.to_string(), vote.to_string(), - env.contract.code_hash.clone() )?; Ok(Response::default() .add_submessages(change_hooks) @@ -488,6 +505,7 @@ pub fn execute_execute( deps: DepsMut, env: Env, info: MessageInfo, + key: String, proposal_id: u64, ) -> Result { let mut prop = PROPOSALS @@ -495,15 +513,17 @@ pub fn execute_execute( .ok_or(ContractError::NoSuchProposal { id: proposal_id })?; let config = CONFIG.load(deps.storage)?; + let dao_info = DAO.load(deps.storage)?; // determine if this sender can execute let mut sender_can_execute = true; if config.only_members_execute { let power = get_voting_power( deps.as_ref(), - config.dao_code_hash.clone(), + dao_info.code_hash.clone(), info.sender.clone(), - &config.dao.clone(), + key, + &dao_info.addr.clone(), Some(prop.start_height), )?; @@ -558,18 +578,29 @@ pub fn execute_execute( VoteResult::Tie => Err(ContractError::Tie {}), // We don't anticipate this case as the proposal would not be in passed state, checked above. VoteResult::SingleWinner(winning_choice) => { let response = if !winning_choice.msgs.is_empty() { - let execute_message =dao_interface::msg::ExecuteMsg::ExecuteProposalHook { - msgs: winning_choice.msgs, - }; + let execute_message = dao_interface::msg::ExecuteMsg::ExecuteProposalHook { + msgs: winning_choice.msgs, + }; match config.close_proposal_on_execution_failure { true => { - let masked_proposal_id = mask_proposal_execution_proposal_id(proposal_id); + let reply_id = REPLY_IDS.add_event( + deps.storage, + ReplyEvent::FailedProposalExecution { proposal_id }, + )?; Response::default().add_submessage(SubMsg::reply_on_error( - execute_message.to_cosmos_msg(config.dao_code_hash.clone(), config.dao.clone().to_string(), None)?, - masked_proposal_id, + execute_message.to_cosmos_msg( + dao_info.code_hash.clone(), + dao_info.addr.clone().to_string(), + None, + )?, + reply_id, )) } - false => Response::default().add_message(execute_message.to_cosmos_msg(config.dao_code_hash.clone(), config.dao.clone().to_string(), None)?), + false => Response::default().add_message(execute_message.to_cosmos_msg( + dao_info.code_hash.clone(), + dao_info.addr.clone().to_string(), + None, + )?), } } else { Response::default() @@ -581,13 +612,12 @@ pub fn execute_execute( proposal_id, old_status.to_string(), prop.status.to_string(), - env.contract.code_hash.clone() )?; // Add prepropose / deposit module hook which will handle deposit refunds. let proposal_creation_policy = CREATION_POLICY.load(deps.storage)?; let proposal_completed_hooks = - proposal_completed_hooks(proposal_creation_policy, proposal_id, prop.status,env.contract.code_hash.clone())?; + proposal_completed_hooks(proposal_creation_policy, proposal_id, prop.status)?; Ok(response .add_submessages(proposal_status_changed_hooks) @@ -595,7 +625,7 @@ pub fn execute_execute( .add_attribute("action", "execute") .add_attribute("sender", info.sender) .add_attribute("proposal_id", proposal_id.to_string()) - .add_attribute("dao", config.dao)) + .add_attribute("dao", dao_info.addr.to_string())) } } } @@ -625,13 +655,12 @@ pub fn execute_close( proposal_id, old_status.to_string(), prop.status.to_string(), - env.contract.code_hash.clone() )?; // Add prepropose / deposit module hook which will handle deposit refunds. let proposal_creation_policy = CREATION_POLICY.load(deps.storage)?; let proposal_completed_hooks = - proposal_completed_hooks(proposal_creation_policy, proposal_id, prop.status,env.contract.code_hash.clone())?; + proposal_completed_hooks(proposal_creation_policy, proposal_id, prop.status)?; Ok(Response::default() .add_submessages(proposal_status_changed_hooks) @@ -655,10 +684,10 @@ pub fn execute_update_config( close_proposal_on_execution_failure: bool, veto: Option, ) -> Result { - let config = CONFIG.load(deps.storage)?; + let dao_info = DAO.load(deps.storage)?; // Only the DAO may call this method. - if info.sender != config.dao { + if info.sender != dao_info.addr { return Err(ContractError::Unauthorized {}); } @@ -682,13 +711,19 @@ pub fn execute_update_config( max_voting_period, only_members_execute, allow_revoting, - dao, - dao_code_hash:code_hash, close_proposal_on_execution_failure, veto, }, )?; + DAO.save( + deps.storage, + &AnyContractInfo { + addr: dao, + code_hash, + }, + )?; + Ok(Response::default() .add_attribute("action", "update_config") .add_attribute("sender", info.sender)) @@ -699,12 +734,13 @@ pub fn execute_update_proposal_creation_policy( info: MessageInfo, new_info: PreProposeInfo, ) -> Result { - let config = CONFIG.load(deps.storage)?; - if config.dao != info.sender { + let dao_info = DAO.load(deps.storage)?; + if dao_info.addr != info.sender { return Err(ContractError::Unauthorized {}); } - let (initial_policy, messages) = new_info.into_initial_policy_and_messages(config.dao)?; + let (initial_policy, messages) = + new_info.into_initial_policy_and_messages(deps.storage, dao_info.addr, REPLY_IDS)?; CREATION_POLICY.save(deps.storage, &initial_policy)?; Ok(Response::default() @@ -752,7 +788,6 @@ pub fn execute_update_rationale( &ballot.unwrap(), )?; - Ok(Response::default() .add_attribute("action", "update_rationale") .add_attribute("sender", info.sender) @@ -765,16 +800,17 @@ pub fn execute_add_proposal_hook( _env: Env, info: MessageInfo, address: String, + code_hash: String, ) -> Result { - let config = CONFIG.load(deps.storage)?; - if config.dao != info.sender { + let dao_info = DAO.load(deps.storage)?; + if dao_info.addr != info.sender { // Only DAO can add hooks return Err(ContractError::Unauthorized {}); } let validated_address = deps.api.addr_validate(&address)?; - add_hook(PROPOSAL_HOOKS, deps.storage, validated_address)?; + add_hook(PROPOSAL_HOOKS, deps.storage, validated_address, code_hash)?; Ok(Response::default() .add_attribute("action", "add_proposal_hook") @@ -786,16 +822,17 @@ pub fn execute_remove_proposal_hook( _env: Env, info: MessageInfo, address: String, + code_hash: String, ) -> Result { - let config = CONFIG.load(deps.storage)?; - if config.dao != info.sender { + let dao_info = DAO.load(deps.storage)?; + if dao_info.addr != info.sender { // Only DAO can remove hooks return Err(ContractError::Unauthorized {}); } let validated_address = deps.api.addr_validate(&address)?; - remove_hook(PROPOSAL_HOOKS, deps.storage, validated_address)?; + remove_hook(PROPOSAL_HOOKS, deps.storage, validated_address, code_hash)?; Ok(Response::default() .add_attribute("action", "remove_proposal_hook") @@ -807,16 +844,17 @@ pub fn execute_add_vote_hook( _env: Env, info: MessageInfo, address: String, + code_hash: String, ) -> Result { - let config = CONFIG.load(deps.storage)?; - if config.dao != info.sender { + let dao_info = DAO.load(deps.storage)?; + if dao_info.addr != info.sender { // Only DAO can add hooks return Err(ContractError::Unauthorized {}); } let validated_address = deps.api.addr_validate(&address)?; - add_hook(VOTE_HOOKS, deps.storage, validated_address)?; + add_hook(VOTE_HOOKS, deps.storage, validated_address, code_hash)?; Ok(Response::default() .add_attribute("action", "add_vote_hook") @@ -828,16 +866,17 @@ pub fn execute_remove_vote_hook( _env: Env, info: MessageInfo, address: String, + code_hash: String, ) -> Result { - let config = CONFIG.load(deps.storage)?; - if config.dao != info.sender { + let dao_info = DAO.load(deps.storage)?; + if dao_info.addr != info.sender { // Only DAO can remove hooks return Err(ContractError::Unauthorized {}); } let validated_address = deps.api.addr_validate(&address)?; - remove_hook(VOTE_HOOKS, deps.storage, validated_address)?; + remove_hook(VOTE_HOOKS, deps.storage, validated_address, code_hash)?; Ok(Response::default() .add_attribute("action", "remove_vote_hook") @@ -848,9 +887,16 @@ pub fn add_hook( hooks: Hooks, storage: &mut dyn Storage, validated_address: Addr, + code_hash: String, ) -> Result<(), ContractError> { hooks - .add_hook(storage, validated_address) + .add_hook( + storage, + HookItem { + addr: validated_address, + code_hash, + }, + ) .map_err(ContractError::HookError)?; Ok(()) } @@ -858,10 +904,17 @@ pub fn add_hook( pub fn remove_hook( hooks: Hooks, storage: &mut dyn Storage, - validate_address: Addr, + validated_address: Addr, + code_hash: String, ) -> Result<(), ContractError> { hooks - .remove_hook(storage, validate_address) + .remove_hook( + storage, + HookItem { + addr: validated_address, + code_hash, + }, + ) .map_err(ContractError::HookError)?; Ok(()) } @@ -876,6 +929,47 @@ pub fn advance_proposal_id(store: &mut dyn Storage) -> StdResult { Ok(id) } +pub fn try_set_key( + deps: DepsMut, + info: MessageInfo, + key: String, +) -> Result { + ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); + Ok(Response::default()) +} + +pub fn try_create_key( + deps: DepsMut, + env: Env, + info: MessageInfo, + entropy: String, +) -> Result { + let key = ViewingKey::create( + deps.storage, + &info, + &env, + info.sender.as_str(), + entropy.as_ref(), + ); + + Ok(Response::new().set_data(to_binary(&CreateViewingKey { key })?)) +} + +fn revoke_permit( + deps: DepsMut, + info: MessageInfo, + permit_name: String, +) -> Result { + RevokedPermits::revoke_permit( + deps.storage, + PREFIX_REVOKED_PERMITS, + info.sender.as_str(), + &permit_name, + ); + + Ok(Response::default()) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { @@ -886,7 +980,6 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } QueryMsg::NextProposalId {} => query_next_proposal_id(deps), QueryMsg::ProposalCount {} => query_proposal_count(deps), - QueryMsg::GetVote { proposal_id, voter } => query_vote(deps, proposal_id, voter), QueryMsg::ListVotes { proposal_id, start_after, @@ -901,7 +994,61 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::ProposalHooks {} => to_binary(&PROPOSAL_HOOKS.query_hooks(deps)?), QueryMsg::VoteHooks {} => to_binary(&VOTE_HOOKS.query_hooks(deps)?), QueryMsg::Dao {} => query_dao(deps), + QueryMsg::WithPermit { permit, query } => permit_queries(deps, env, permit, query), + _ => viewing_keys_queries(deps, env, msg), + } +} + +fn permit_queries( + deps: Deps, + env: Env, + permit: Permit, + query: QueryWithPermit, +) -> Result { + // Validate permit content + + let _account = secret_toolkit::permit::validate( + deps, + PREFIX_REVOKED_PERMITS, + &permit, + env.contract.address.clone().into_string(), + None, + )?; + + // Permit validated! We can now execute the query. + match query { + QueryWithPermit::GetVote { proposal_id, voter } => { + if !permit.check_permission(&secret_toolkit::permit::TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query get vote, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_vote(deps, proposal_id, voter)?) + } + } +} + +pub fn viewing_keys_queries(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + let (addresses, key) = msg.get_validation_params(deps.api)?; + + for address in addresses { + let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); + if result.is_ok() { + return match msg { + // Base + QueryMsg::GetVote { + voter, proposal_id, .. + } => to_binary(&query_vote(deps, proposal_id, voter)?), + _ => panic!("This query type does not require authentication"), + }; + } } + + to_binary(&ViewingKeyError { + msg: "Wrong viewing key for this address or viewing key not set".to_string(), + }) } pub fn query_config(deps: Deps) -> StdResult { @@ -910,8 +1057,8 @@ pub fn query_config(deps: Deps) -> StdResult { } pub fn query_dao(deps: Deps) -> StdResult { - let config = CONFIG.load(deps.storage)?; - to_binary(&config.dao) + let dao = DAO.load(deps.storage)?; + to_binary(&dao) } pub fn query_proposal(deps: Deps, env: Env, id: u64) -> StdResult { @@ -1025,7 +1172,7 @@ pub fn query_vote(deps: Deps, proposal_id: u64, voter: String) -> StdResult StdResult { #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { - let repl = TaggedReplyId::new(msg.id)?; + let repl = REPLY_IDS.get_event(deps.storage, msg.id)?; match repl { - TaggedReplyId::FailedProposalExecution(proposal_id) => { - // PROPOSALS.update(deps.storage, proposal_id, |prop| match prop { - // Some(mut prop) => { - // prop.status = Status::ExecutionFailed; - // Ok(prop) - // } - // None => Err(ContractError::NoSuchProposal { id: proposal_id }), - // })?; - let proposals = PROPOSALS.get(deps.storage, &proposal_id); - if proposals.clone().is_some() { - proposals.clone().unwrap().status = Status::ExecutionFailed; - } else { - return Err(ContractError::NoSuchProposal { id: proposal_id }); - } - PROPOSALS.insert(deps.storage, &proposal_id, &proposals.unwrap())?; + ReplyEvent::FailedProposalExecution { proposal_id } => match msg.clone().result { + SubMsgResult::Err(err) => Err(ContractError::Std(StdError::GenericErr { msg: err })), + SubMsgResult::Ok(_) => { + // PROPOSALS.update(deps.storage, proposal_id, |prop| match prop { + // Some(mut prop) => { + // prop.status = Status::ExecutionFailed; + + // Ok(prop) + // } + // None => Err(ContractError::NoSuchProposal { id: proposal_id }), + // })?; + let proposals = PROPOSALS.get(deps.storage, &proposal_id); + if proposals.clone().is_some() { + proposals.clone().unwrap().status = Status::ExecutionFailed; + } else { + return Err(ContractError::NoSuchProposal { id: proposal_id }); + } + PROPOSALS.insert(deps.storage, &proposal_id, &proposals.unwrap())?; - Ok(Response::new() - .add_attribute("proposal execution failed", proposal_id.to_string()) - .add_attribute("error", msg.result.into_result().err().unwrap_or_default())) - } - TaggedReplyId::FailedProposalHook(idx) => { - let addr = PROPOSAL_HOOKS.remove_hook_by_index(deps.storage, idx)?; - Ok(Response::new().add_attribute("removed_proposal_hook", format!("{addr}:{idx}"))) - } - TaggedReplyId::FailedVoteHook(idx) => { - let addr = VOTE_HOOKS.remove_hook_by_index(deps.storage, idx)?; - Ok(Response::new().add_attribute("removed vote hook", format!("{addr}:{idx}"))) - } - TaggedReplyId::PreProposeModuleInstantiation => { - let res = parse_reply_instantiate_data(msg)?; - let module = deps.api.addr_validate(&res.contract_address)?; - CREATION_POLICY.save( - deps.storage, - &ProposalCreationPolicy::Module { addr: module }, - )?; + Ok(Response::new() + .add_attribute("proposal_execution_failed", proposal_id.to_string()) + .add_attribute("error", msg.result.into_result().err().unwrap_or_default())) + } + }, + ReplyEvent::FailedProposalHook { idx } => match msg.result { + SubMsgResult::Err(err) => Err(ContractError::Std(StdError::GenericErr { msg: err })), + SubMsgResult::Ok(_) => { + let hook_item = PROPOSAL_HOOKS.remove_hook_by_index(deps.storage, idx)?; + Ok(Response::new().add_attribute( + "removed_proposal_hook", + format!("{0}:{idx}", hook_item.addr), + )) + } + }, + ReplyEvent::FailedVoteHook { idx } => match msg.result { + SubMsgResult::Err(err) => Err(ContractError::Std(StdError::GenericErr { msg: err })), + SubMsgResult::Ok(_) => { + let hook_item = VOTE_HOOKS.remove_hook_by_index(deps.storage, idx)?; + Ok(Response::new() + .add_attribute("removed_vote_hook", format!("{0}:{idx}", hook_item.addr))) + } + }, + ReplyEvent::PreProposalModuleInstantiate { code_hash } => match msg.result { + SubMsgResult::Err(err) => Err(ContractError::Std(StdError::GenericErr { msg: err })), + SubMsgResult::Ok(res) => { + let contract_address = parse_reply_event_for_contract_address(res.events)?; + + let module_addr = deps.api.addr_validate(&contract_address)?; + CREATION_POLICY.save( + deps.storage, + &ProposalCreationPolicy::Module { + addr: module_addr.clone(), + code_hash, + }, + )?; - match res.data { - Some(data) => Ok(Response::new() - .add_attribute("update_pre_propose_module", res.contract_address) - .set_data(data)), - None => Ok(Response::new() - .add_attribute("update_pre_propose_module", res.contract_address)), + // per the cosmwasm docs, we shouldn't have to forward + // data like this, yet here we are and it does not work if + // we do not. + // + // + match res.data { + Some(data) => Ok(Response::new() + .add_attribute("update_pre_propose_module", module_addr.clone().to_string()) + .set_data(data)), + None => Ok(Response::new() + .add_attribute("update_pre_propose_module", module_addr.to_string())), + } } - } - TaggedReplyId::FailedPreProposeModuleHook => { + }, + ReplyEvent::FailedPreProposeModuleHook {} => { let addr = match CREATION_POLICY.load(deps.storage)? { ProposalCreationPolicy::Anyone {} => { // Something is off if we're getting this // reply and we don't have a pre-propose // module installed. This should be // unreachable. - return Err(ContractError::InvalidReplyID { - id: failed_pre_propose_module_hook_id(), - }); + return Err(ContractError::InvalidReplyID { id: msg.id }); } - ProposalCreationPolicy::Module { addr } => { + ProposalCreationPolicy::Module { addr, code_hash: _ } => { // If we are here, our pre-propose module has // errored while receiving a proposal // hook. Rest in peace pre-propose module. @@ -1143,6 +1314,7 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Err(ContractError::UnknownReplyID {}), } } diff --git a/contracts/proposal/dao-proposal-multiple/src/error.rs b/contracts/proposal/dao-proposal-multiple/src/error.rs index 787a7b4..779a92a 100644 --- a/contracts/proposal/dao-proposal-multiple/src/error.rs +++ b/contracts/proposal/dao-proposal-multiple/src/error.rs @@ -2,8 +2,9 @@ use std::u64; use cosmwasm_std::StdError; use cw_hooks::HookError; -use secret_utils::ParseReplyError; use dao_voting::{reply::error::TagError, threshold::ThresholdError, veto::VetoError}; +use secret_cw_controllers::ReplyError; +use secret_utils::ParseReplyError; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -14,6 +15,9 @@ pub enum ContractError { #[error(transparent)] ParseReplyError(#[from] ParseReplyError), + #[error(transparent)] + ReplyUdError(#[from] ReplyError), + #[error("{0}")] HookError(#[from] HookError), @@ -26,6 +30,9 @@ pub enum ContractError { #[error("{0}")] ThresholdError(#[from] ThresholdError), + #[error("An unknown reply ID was received.")] + UnknownReplyID {}, + #[error("{0}")] VotingError(#[from] dao_voting::error::VotingError), diff --git a/contracts/proposal/dao-proposal-multiple/src/msg.rs b/contracts/proposal/dao-proposal-multiple/src/msg.rs index 061a9b7..959ef97 100644 --- a/contracts/proposal/dao-proposal-multiple/src/msg.rs +++ b/contracts/proposal/dao-proposal-multiple/src/msg.rs @@ -1,11 +1,13 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use secret_utils::Duration; +use cosmwasm_std::{Addr, Api, StdResult}; use dao_dao_macros::proposal_module_query; use dao_voting::{ multiple_choice::{MultipleChoiceOptions, MultipleChoiceVote, VotingStrategy}, pre_propose::PreProposeInfo, veto::VetoConfig, }; +use secret_toolkit::permit::Permit; +use secret_utils::Duration; #[cw_serde] pub struct InstantiateMsg { @@ -46,7 +48,7 @@ pub struct InstantiateMsg { pub veto: Option, // dao code hash - pub code_hash: String + pub dao_code_hash: String, } #[cw_serde] @@ -69,6 +71,8 @@ pub enum ExecuteMsg { /// Votes on a proposal. Voting power is determined by the DAO's /// voting power module. Vote { + /// Viewng key of the sender + key: String, /// The ID of the proposal to vote on. proposal_id: u64, /// The senders position on the proposal. @@ -81,6 +85,8 @@ pub enum ExecuteMsg { /// Causes the messages associated with a passed proposal to be /// executed by the DAO. Execute { + /// Viewng key of the sender + key: String, /// The ID of the proposal to execute. proposal_id: u64, }, @@ -151,15 +157,32 @@ pub enum ExecuteMsg { }, AddProposalHook { address: String, + code_hash: String, }, RemoveProposalHook { address: String, + code_hash: String, }, AddVoteHook { address: String, + code_hash: String, }, RemoveVoteHook { address: String, + code_hash: String, + }, + CreateViewingKey { + entropy: String, + padding: Option, + }, + SetViewingKey { + key: String, + padding: Option, + }, + // Permit + RevokePermit { + permit_name: String, + padding: Option, }, } @@ -188,7 +211,11 @@ pub enum QueryMsg { }, /// Returns a voters position on a proposal. #[returns(crate::query::VoteResponse)] - GetVote { proposal_id: u64, voter: String }, + GetVote { + proposal_id: u64, + voter: String, + key: String, + }, /// Lists all of the votes that have been cast on a proposal. #[returns(crate::query::VoteListResponse)] ListVotes { @@ -208,6 +235,40 @@ pub enum QueryMsg { /// Lists all of the consumers of vote hooks for this module. #[returns(::cw_hooks::HooksResponse)] VoteHooks {}, + #[returns(())] + WithPermit { + permit: Permit, + query: QueryWithPermit, + }, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryWithPermit { + #[returns(crate::query::VoteResponse)] + GetVote { proposal_id: u64, voter: String }, +} + +impl QueryMsg { + pub fn get_validation_params(&self, api: &dyn Api) -> StdResult<(Vec, String)> { + match self { + Self::GetVote { voter, key, .. } => { + let address = api.addr_validate(voter.as_str())?; + Ok((vec![address], key.clone())) + } + _ => panic!("This query type does not require authentication"), + } + } +} + +#[cw_serde] +pub struct CreateViewingKey { + pub key: String, +} + +#[cw_serde] +pub struct ViewingKeyError { + pub msg: String, } #[cw_serde] diff --git a/contracts/proposal/dao-proposal-multiple/src/proposal.rs b/contracts/proposal/dao-proposal-multiple/src/proposal.rs index 3b5850c..b19bdcc 100644 --- a/contracts/proposal/dao-proposal-multiple/src/proposal.rs +++ b/contracts/proposal/dao-proposal-multiple/src/proposal.rs @@ -1,7 +1,6 @@ use std::ops::Add; use cosmwasm_std::{Addr, BlockInfo, StdError, StdResult, Uint128}; -use secret_utils::Expiration; use dao_voting::{ multiple_choice::{ CheckedMultipleChoiceOption, MultipleChoiceOptionType, MultipleChoiceVotes, VotingStrategy, @@ -10,11 +9,11 @@ use dao_voting::{ veto::VetoConfig, voting::does_vote_count_pass, }; +use secret_utils::Expiration; use crate::query::ProposalResponse; -use serde::{Serialize,Deserialize}; use schemars::JsonSchema; - +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] #[serde(rename_all = "snake_case")] diff --git a/contracts/proposal/dao-proposal-multiple/src/state.rs b/contracts/proposal/dao-proposal-multiple/src/state.rs index 7376b63..aebeb05 100644 --- a/contracts/proposal/dao-proposal-multiple/src/state.rs +++ b/contracts/proposal/dao-proposal-multiple/src/state.rs @@ -1,16 +1,18 @@ use crate::proposal::MultipleChoiceProposal; use cosmwasm_std::{Addr, Uint128}; use cw_hooks::Hooks; -use secret_storage_plus::Item; -use secret_toolkit::{serialization::Json, storage::Keymap}; -use secret_utils::Duration; +use dao_interface::state::AnyContractInfo; use dao_voting::{ multiple_choice::{MultipleChoiceVote, VotingStrategy}, pre_propose::ProposalCreationPolicy, veto::VetoConfig, }; -use serde::{Serialize,Deserialize}; use schemars::JsonSchema; +use secret_cw_controllers::ReplyIds; +use secret_storage_plus::Item; +use secret_toolkit::{serialization::Json, storage::Keymap}; +use secret_utils::Duration; +use serde::{Deserialize, Serialize}; /// The proposal module's configuration. #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] @@ -36,12 +38,6 @@ pub struct Config { /// vote information is not known until the time of proposal /// expiration. pub allow_revoting: bool, - /// The address of the DAO that this governance module is - /// associated with. - pub dao: Addr, - /// The code hash of the DAO that this governance module is - /// associated with. - pub dao_code_hash: String, /// If set to true proposals will be closed if their execution /// fails. Otherwise, proposals will remain open after execution /// failure. For example, with this enabled a proposal to send 5 @@ -70,8 +66,8 @@ pub struct Ballot { /// The current top level config for the module. pub const CONFIG: Item = Item::new("config"); pub const PROPOSAL_COUNT: Item = Item::new("proposal_count"); -pub static PROPOSALS: Keymap = Keymap::new(b"proposals"); -pub static BALLOTS: Keymap<(u64, Addr), Ballot,Json> = Keymap::new(b"ballots"); +pub static PROPOSALS: Keymap = Keymap::new(b"proposals"); +pub static BALLOTS: Keymap<(u64, Addr), Ballot, Json> = Keymap::new(b"ballots"); /// Consumers of proposal state change hooks. pub const PROPOSAL_HOOKS: Hooks = Hooks::new("proposal_hooks"); /// Consumers of vote hooks. @@ -79,3 +75,5 @@ pub const VOTE_HOOKS: Hooks = Hooks::new("vote_hooks"); /// The address of the pre-propose module associated with this /// proposal module (if any). pub const CREATION_POLICY: Item = Item::new("creation_policy"); +pub const DAO: Item = Item::new("dao"); +pub const REPLY_IDS: ReplyIds = ReplyIds::new(b"reply_ids", b"reply_ids_count"); diff --git a/contracts/proposal/dao-proposal-single/src/contract.rs b/contracts/proposal/dao-proposal-single/src/contract.rs index 9b7e476..6ce6819 100644 --- a/contracts/proposal/dao-proposal-single/src/contract.rs +++ b/contracts/proposal/dao-proposal-single/src/contract.rs @@ -21,11 +21,12 @@ use dao_voting::veto::{VetoConfig, VetoError}; use dao_voting::voting::{get_total_power, get_voting_power, validate_voting_period, Vote, Votes}; use secret_cw2::set_contract_version; use secret_cw_controllers::ReplyEvent; +use secret_toolkit::permit::{Permit, RevokedPermits}; use secret_toolkit::utils::HandleCallback; -use secret_utils::{ - parse_reply_event_for_contract_address, Duration, -}; +use secret_toolkit::viewing_key::{ViewingKey, ViewingKeyStore}; +use secret_utils::{parse_reply_event_for_contract_address, Duration}; +use crate::msg::{CreateViewingKey, QueryWithPermit, ViewingKeyError}; // use crate::msg::MigrateMsg; use crate::proposal::{next_proposal_id, SingleChoiceProposal}; use crate::state::{Config, CREATION_POLICY, DAO, REPLY_IDS}; @@ -44,6 +45,8 @@ use crate::{ pub(crate) const CONTRACT_NAME: &str = "crates.io:dao-proposal-single"; pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, @@ -112,15 +115,18 @@ pub fn execute( proposer, }) => execute_propose(deps, env, info.sender, title, description, msgs, proposer), ExecuteMsg::Vote { + key, proposal_id, vote, rationale, - } => execute_vote(deps, env, info, proposal_id, vote, rationale), + } => execute_vote(deps, env, info, key, proposal_id, vote, rationale), ExecuteMsg::UpdateRationale { proposal_id, rationale, } => execute_update_rationale(deps, info, proposal_id, rationale), - ExecuteMsg::Execute { proposal_id } => execute_execute(deps, env, info, proposal_id), + ExecuteMsg::Execute { key, proposal_id } => { + execute_execute(deps, env, info, key, proposal_id) + } ExecuteMsg::Close { proposal_id } => execute_close(deps, env, info, proposal_id), ExecuteMsg::UpdateConfig { threshold, @@ -161,6 +167,9 @@ pub fn execute( execute_remove_vote_hook(deps, env, info, address, code_hash) } ExecuteMsg::Veto { proposal_id } => execute_veto(deps, env, info, proposal_id), + ExecuteMsg::CreateViewingKey { entropy, .. } => try_create_key(deps, env, info, entropy), + ExecuteMsg::SetViewingKey { key, .. } => try_set_key(deps, info, key), + ExecuteMsg::RevokePermit { permit_name, .. } => revoke_permit(deps, info, permit_name), } } @@ -360,6 +369,7 @@ pub fn execute_execute( deps: DepsMut, env: Env, info: MessageInfo, + key: String, proposal_id: u64, ) -> Result { let dao_info = DAO.load(deps.storage)?; @@ -376,6 +386,7 @@ pub fn execute_execute( deps.as_ref(), dao_info.code_hash.clone(), info.sender.clone(), + key, &dao_info.addr.clone(), Some(prop.start_height), )?; @@ -436,14 +447,14 @@ pub fn execute_execute( let reply_id = REPLY_IDS.add_event( deps.storage, ReplyEvent::FailedProposalExecution { proposal_id }, - ); + )?; Response::default().add_submessage(SubMsg::reply_on_error( execute_message.to_cosmos_msg( dao_info.code_hash.clone(), dao_info.addr.clone().into_string(), None, )?, - reply_id.unwrap(), + reply_id, )) } false => Response::default().add_message(execute_message.to_cosmos_msg( @@ -484,6 +495,7 @@ pub fn execute_vote( deps: DepsMut, env: Env, info: MessageInfo, + key: String, proposal_id: u64, vote: Vote, rationale: Option, @@ -509,6 +521,7 @@ pub fn execute_vote( deps.as_ref(), dao_info.code_hash.clone(), info.sender.clone(), + key, &dao_info.addr, Some(prop.start_height), )?; @@ -900,6 +913,47 @@ pub fn execute_remove_vote_hook( .add_attribute("address", address)) } +pub fn try_set_key( + deps: DepsMut, + info: MessageInfo, + key: String, +) -> Result { + ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); + Ok(Response::default()) +} + +pub fn try_create_key( + deps: DepsMut, + env: Env, + info: MessageInfo, + entropy: String, +) -> Result { + let key = ViewingKey::create( + deps.storage, + &info, + &env, + info.sender.as_str(), + entropy.as_ref(), + ); + + Ok(Response::new().set_data(to_binary(&CreateViewingKey { key })?)) +} + +fn revoke_permit( + deps: DepsMut, + info: MessageInfo, + permit_name: String, +) -> Result { + RevokedPermits::revoke_permit( + deps.storage, + PREFIX_REVOKED_PERMITS, + info.sender.as_str(), + &permit_name, + ); + + Ok(Response::default()) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { @@ -911,7 +965,6 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { } QueryMsg::NextProposalId {} => query_next_proposal_id(deps), QueryMsg::ProposalCount {} => query_proposal_count(deps), - QueryMsg::GetVote { proposal_id, voter } => query_vote(deps, proposal_id, voter), QueryMsg::ListVotes { proposal_id, start_after, @@ -925,7 +978,61 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::ProposalCreationPolicy {} => query_creation_policy(deps), QueryMsg::ProposalHooks {} => to_binary(&PROPOSAL_HOOKS.query_hooks(deps)?), QueryMsg::VoteHooks {} => to_binary(&VOTE_HOOKS.query_hooks(deps)?), + QueryMsg::WithPermit { permit, query } => permit_queries(deps, env, permit, query), + _ => viewing_keys_queries(deps, env, msg), + } +} + +fn permit_queries( + deps: Deps, + env: Env, + permit: Permit, + query: QueryWithPermit, +) -> Result { + // Validate permit content + + let _account = secret_toolkit::permit::validate( + deps, + PREFIX_REVOKED_PERMITS, + &permit, + env.contract.address.clone().into_string(), + None, + )?; + + // Permit validated! We can now execute the query. + match query { + QueryWithPermit::GetVote { proposal_id, voter } => { + if !permit.check_permission(&secret_toolkit::permit::TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query get vote, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_vote(deps, proposal_id, voter)?) + } + } +} + +pub fn viewing_keys_queries(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + let (addresses, key) = msg.get_validation_params(deps.api)?; + + for address in addresses { + let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); + if result.is_ok() { + return match msg { + // Base + QueryMsg::GetVote { + voter, proposal_id, .. + } => to_binary(&query_vote(deps, proposal_id, voter)?), + _ => panic!("This query type does not require authentication"), + }; + } } + + to_binary(&ViewingKeyError { + msg: "Wrong viewing key for this address or viewing key not set".to_string(), + }) } pub fn query_config(deps: Deps) -> StdResult { @@ -1203,8 +1310,8 @@ pub fn query_info(deps: Deps) -> StdResult { #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { - let repl = REPLY_IDS.get_event(deps.storage, msg.id); - match repl.unwrap() { + let repl = REPLY_IDS.get_event(deps.storage, msg.id)?; + match repl { ReplyEvent::FailedProposalExecution { proposal_id } => match msg.clone().result { SubMsgResult::Err(err) => Err(ContractError::Std(StdError::GenericErr { msg: err })), SubMsgResult::Ok(_) => { @@ -1282,11 +1389,9 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { + ProposalCreationPolicy::Module { addr, code_hash: _ } => { // If we are here, our pre-propose module has // errored while receiving a proposal // hook. Rest in peace pre-propose module. @@ -1296,6 +1401,6 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Err(ContractError::UnknownReplyID {}), + _ => Err(ContractError::UnknownReplyID {}), } } diff --git a/contracts/proposal/dao-proposal-single/src/error.rs b/contracts/proposal/dao-proposal-single/src/error.rs index 42e7cde..244e934 100644 --- a/contracts/proposal/dao-proposal-single/src/error.rs +++ b/contracts/proposal/dao-proposal-single/src/error.rs @@ -3,6 +3,7 @@ use std::u64; use cosmwasm_std::StdError; use cw_hooks::HookError; use dao_voting::{reply::error::TagError, veto::VetoError}; +use secret_cw_controllers::ReplyError; use secret_utils::ParseReplyError; use thiserror::Error; @@ -17,6 +18,9 @@ pub enum ContractError { #[error(transparent)] HookError(#[from] HookError), + #[error(transparent)] + ReplyUdError(#[from] ReplyError), + #[error(transparent)] VetoError(#[from] VetoError), diff --git a/contracts/proposal/dao-proposal-single/src/msg.rs b/contracts/proposal/dao-proposal-single/src/msg.rs index daffb17..9ca74a1 100644 --- a/contracts/proposal/dao-proposal-single/src/msg.rs +++ b/contracts/proposal/dao-proposal-single/src/msg.rs @@ -1,10 +1,12 @@ use cosmwasm_schema::QueryResponses; +use cosmwasm_std::{Addr, Api, StdResult}; use dao_dao_macros::proposal_module_query; use dao_voting::{ pre_propose::PreProposeInfo, proposal::SingleChoiceProposeMsg, threshold::Threshold, veto::VetoConfig, voting::Vote, }; use schemars::JsonSchema; +use secret_toolkit::permit::Permit; use secret_utils::Duration; use serde::{Deserialize, Serialize}; @@ -59,6 +61,8 @@ pub enum ExecuteMsg { /// Votes on a proposal. Voting power is determined by the DAO's /// voting power module. Vote { + /// The viewing key of the sender + key: String, /// The ID of the proposal to vote on. proposal_id: u64, /// The senders position on the proposal. @@ -77,6 +81,8 @@ pub enum ExecuteMsg { /// Causes the messages associated with a passed proposal to be /// executed by the DAO. Execute { + /// The viewing of the sender + key: String, /// The ID of the proposal to execute. proposal_id: u64, }, @@ -150,6 +156,19 @@ pub enum ExecuteMsg { AddVoteHook { address: String, code_hash: String }, /// Removed a consumer of vote hooks. RemoveVoteHook { address: String, code_hash: String }, + CreateViewingKey { + entropy: String, + padding: Option, + }, + SetViewingKey { + key: String, + padding: Option, + }, + // Permit + RevokePermit { + permit_name: String, + padding: Option, + }, } #[proposal_module_query] @@ -191,7 +210,11 @@ pub enum QueryMsg { }, /// Returns a voters position on a propsal. #[returns(crate::query::VoteResponse)] - GetVote { proposal_id: u64, voter: String }, + GetVote { + proposal_id: u64, + voter: String, + key: String, + }, /// Lists all of the votes that have been cast on a /// proposal. #[returns(crate::query::VoteListResponse)] @@ -217,6 +240,41 @@ pub enum QueryMsg { /// Lists all of the consumers of vote hooks for this module. #[returns(::cw_hooks::HooksResponse)] VoteHooks {}, + #[returns(())] + WithPermit { + permit: Permit, + query: QueryWithPermit, + }, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +#[serde(rename_all = "snake_case")] +#[derive(QueryResponses)] +pub enum QueryWithPermit { + #[returns(crate::query::VoteResponse)] + GetVote { proposal_id: u64, voter: String }, +} + +impl QueryMsg { + pub fn get_validation_params(&self, api: &dyn Api) -> StdResult<(Vec, String)> { + match self { + Self::GetVote { voter, key, .. } => { + let address = api.addr_validate(voter.as_str())?; + Ok((vec![address], key.clone())) + } + _ => panic!("This query type does not require authentication"), + } + } +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +pub struct CreateViewingKey { + pub key: String, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +pub struct ViewingKeyError { + pub msg: String, } #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] diff --git a/contracts/staking/snip20-stake-external-rewards/Cargo.toml b/contracts/staking/snip20-stake-external-rewards/Cargo.toml index 94a8520..2555767 100644 --- a/contracts/staking/snip20-stake-external-rewards/Cargo.toml +++ b/contracts/staking/snip20-stake-external-rewards/Cargo.toml @@ -23,7 +23,6 @@ secret-storage-plus = { workspace = true } secret-cw-controllers = { workspace = true } cw20 = { workspace = true } secret-utils = { workspace = true } -snip20-reference-impl = { workspace = true} secret-cw2 = { workspace = true } thiserror = { workspace = true } snip20-stake = { workspace = true, features = ["library"]} diff --git a/contracts/staking/snip20-stake-external-rewards/examples/schema.rs b/contracts/staking/snip20-stake-external-rewards/examples/schema.rs index ca1d1c8..07f7797 100644 --- a/contracts/staking/snip20-stake-external-rewards/examples/schema.rs +++ b/contracts/staking/snip20-stake-external-rewards/examples/schema.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::write_api; -use cw20_stake_external_rewards::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use snip20_stake_external_rewards::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; fn main() { write_api! { diff --git a/contracts/staking/snip20-stake-external-rewards/src/contract.rs b/contracts/staking/snip20-stake-external-rewards/src/contract.rs index 2258385..81e3e33 100644 --- a/contracts/staking/snip20-stake-external-rewards/src/contract.rs +++ b/contracts/staking/snip20-stake-external-rewards/src/contract.rs @@ -1,17 +1,15 @@ use crate::msg::{ - ExecuteMsg, InfoResponse, InstantiateMsg, PendingRewardsResponse, QueryMsg, - ReceiveMsg,MigrateMsg + ExecuteMsg, InfoResponse, InstantiateMsg, MigrateMsg, PendingRewardsResponse, QueryMsg, + ReceiveMsg, }; use crate::state::{ Config, Denom, RewardConfig, CONFIG, LAST_UPDATE_BLOCK, PENDING_REWARDS, REWARD_CONFIG, - REWARD_PER_TOKEN, SNIP20_STAKING_VIEWING_KEY, USER_REWARD_PER_TOKEN, + REWARD_PER_TOKEN, USER_REWARD_PER_TOKEN, VIEWING_KEY_INFO, }; +use crate::ContractError; use crate::ContractError::{ InvalidFunds, InvalidSnip20, NoRewardsClaimable, RewardPeriodNotFinished, }; -use crate::{snip20_stake_msg, ContractError}; -use secret_toolkit::utils::HandleCallback; -const EXECUTE_SNIP20_STAKE_VIEWING_KEY_ID: u64 = 0; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; @@ -19,21 +17,22 @@ use crate::msg::Snip20ReceiveMsg; use crate::state::Denom::Snip20; use cosmwasm_std::{ from_binary, to_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Deps, DepsMut, Empty, Env, - MessageInfo, Reply, Response, StdError, StdResult, SubMsg, SubMsgResult, Uint128, Uint256, - WasmMsg, + MessageInfo, Response, StdError, StdResult, Uint128, Uint256, WasmMsg, }; use dao_hooks::stake::StakeChangedHookMsg; -use secret_cw2::{set_contract_version,ContractVersion}; +use secret_cw2::{get_contract_version, set_contract_version, ContractVersion}; use std::cmp::min; use std::convert::TryInto; const CONTRACT_NAME: &str = "crates.io:snip20-stake-external-rewards"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, - env: Env, + _env: Env, _info: MessageInfo, msg: InstantiateMsg, ) -> Result, ContractError> { @@ -72,20 +71,6 @@ pub fn instantiate( }; REWARD_CONFIG.save(deps.storage, &reward_config)?; - // Create Snip20 Stake viewing key - let gen_viewing_key_msg = snip20_stake_msg::ExecuteMsg::CreateViewingKey { - entropy: "entropy".to_string(), - }; - - let submsg = SubMsg::reply_on_success( - gen_viewing_key_msg.to_cosmos_msg( - msg.staking_contract_code_hash.clone(), - msg.staking_contract.clone().to_string(), - None, - )?, - EXECUTE_SNIP20_STAKE_VIEWING_KEY_ID, - ); - Ok(Response::new() .add_attribute("owner", msg.owner.unwrap_or_else(|| "None".to_string())) .add_attribute("staking_contract", config.staking_contract) @@ -98,8 +83,7 @@ pub fn instantiate( ) .add_attribute("reward_rate", reward_config.reward_rate) .add_attribute("period_finish", reward_config.period_finish.to_string()) - .add_attribute("reward_duration", reward_config.reward_duration.to_string()) - .add_submessage(submsg).set_data(to_binary(&env.contract.code_hash)?)) + .add_attribute("reward_duration", reward_config.reward_duration.to_string())) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -124,13 +108,14 @@ pub fn execute( ) -> Result, ContractError> { match msg { ExecuteMsg::StakeChangeHook(msg) => execute_stake_changed(deps, env, info, msg), - ExecuteMsg::Claim {} => execute_claim(deps, env, info), + ExecuteMsg::Claim { key } => execute_claim(deps, env, info, key), ExecuteMsg::Fund {} => execute_fund_native(deps, env, info), ExecuteMsg::Receive(msg) => execute_receive(deps, env, info, msg), ExecuteMsg::UpdateRewardDuration { new_duration } => { execute_update_reward_duration(deps, env, info, new_duration) } ExecuteMsg::UpdateOwnership(action) => execute_update_owner(deps, info, env, action), + ExecuteMsg::SetViewingKey { key, .. } => try_set_key(deps, info, key), } } @@ -146,8 +131,9 @@ pub fn execute_receive( if config.reward_token != Denom::Snip20(info.sender) { return Err(InvalidSnip20 {}); }; + let key = VIEWING_KEY_INFO.load(deps.storage, sender.clone())?; match msg { - ReceiveMsg::Fund {} => execute_fund(deps, env, sender, wrapper.amount), + ReceiveMsg::Fund {} => execute_fund(deps, env, sender, wrapper.amount, Some(key)), } } @@ -161,7 +147,7 @@ pub fn execute_fund_native( match config.reward_token { Denom::Native(denom) => { let amount = secret_utils::must_pay(&info, &denom).map_err(|_| InvalidFunds {})?; - execute_fund(deps, env, info.sender, amount) + execute_fund(deps, env, info.sender, amount, None) } Snip20(_) => Err(InvalidFunds {}), } @@ -172,10 +158,11 @@ pub fn execute_fund( env: Env, sender: Addr, amount: Uint128, + key: Option, ) -> Result, ContractError> { cw_ownable::assert_owner(deps.storage, &sender)?; - update_rewards(&mut deps, &env, &sender)?; + update_rewards(&mut deps, &env, &sender, key.unwrap())?; let reward_config = REWARD_CONFIG.load(deps.storage)?; if reward_config.period_finish > env.block.height { return Err(RewardPeriodNotFinished {}); @@ -215,8 +202,14 @@ pub fn execute_stake_changed( return Err(ContractError::InvalidHookSender {}); }; match msg { - StakeChangedHookMsg::Stake { addr, .. } => execute_stake(deps, env, addr), - StakeChangedHookMsg::Unstake { addr, .. } => execute_unstake(deps, env, addr), + StakeChangedHookMsg::Stake { addr, .. } => { + let key = VIEWING_KEY_INFO.load(deps.storage, addr.clone())?; + execute_stake(deps, env, addr, key) + } + StakeChangedHookMsg::Unstake { addr, .. } => { + let key = VIEWING_KEY_INFO.load(deps.storage, addr.clone())?; + execute_unstake(deps, env, addr, key) + } } } @@ -224,8 +217,9 @@ pub fn execute_stake( mut deps: DepsMut, env: Env, addr: Addr, + key: String, ) -> Result, ContractError> { - update_rewards(&mut deps, &env, &addr)?; + update_rewards(&mut deps, &env, &addr, key)?; Ok(Response::new().add_attribute("action", "stake")) } @@ -233,8 +227,9 @@ pub fn execute_unstake( mut deps: DepsMut, env: Env, addr: Addr, + key: String, ) -> Result, ContractError> { - update_rewards(&mut deps, &env, &addr)?; + update_rewards(&mut deps, &env, &addr, key)?; Ok(Response::new().add_attribute("action", "unstake")) } @@ -242,8 +237,9 @@ pub fn execute_claim( mut deps: DepsMut, env: Env, info: MessageInfo, + key: String, ) -> Result, ContractError> { - update_rewards(&mut deps, &env, &info.sender)?; + update_rewards(&mut deps, &env, &info.sender, key)?; let rewards = PENDING_REWARDS .load(deps.storage, info.sender.clone()) .map_err(|_| NoRewardsClaimable {})?; @@ -274,6 +270,15 @@ pub fn execute_update_owner( Ok(Response::default().add_attributes(ownership.into_attributes())) } +pub fn try_set_key( + deps: DepsMut, + info: MessageInfo, + key: String, +) -> Result { + VIEWING_KEY_INFO.save(deps.storage, info.sender, &key)?; + Ok(Response::default()) +} + pub fn get_transfer_msg( recipient: Addr, amount: Uint128, @@ -287,12 +292,10 @@ pub fn get_transfer_msg( } .into()), Denom::Snip20(addr) => { - let snip20_msg = to_binary(&snip20_reference_impl::msg::ExecuteMsg::Transfer { + let snip20_msg = to_binary(&secret_toolkit::snip20::HandleMsg::Transfer { recipient: recipient.into_string(), amount, memo: None, - decoys: None, - entropy: None, padding: None, })?; Ok(WasmMsg::Execute { @@ -306,7 +309,7 @@ pub fn get_transfer_msg( } } -pub fn update_rewards(deps: &mut DepsMut, env: &Env, addr: &Addr) -> StdResult<()> { +pub fn update_rewards(deps: &mut DepsMut, env: &Env, addr: &Addr, key: String) -> StdResult<()> { let config = CONFIG.load(deps.storage)?; let reward_per_token = get_reward_per_token( deps.as_ref(), @@ -318,10 +321,10 @@ pub fn update_rewards(deps: &mut DepsMut, env: &Env, addr: &Addr) -> StdResult<( let earned_rewards = get_rewards_earned( deps.as_ref(), - env, addr, reward_per_token, &config.staking_contract, + key, )?; PENDING_REWARDS.update::<_, StdError>(deps.storage, addr.clone(), |r| { Ok(r.unwrap_or_default() + earned_rewards) @@ -364,18 +367,18 @@ pub fn get_reward_per_token( pub fn get_rewards_earned( deps: Deps, - env: &Env, addr: &Addr, reward_per_token: Uint256, staking_contract: &Addr, + key: String, ) -> StdResult { let config = CONFIG.load(deps.storage)?; let staked_balance = Uint256::from(get_staked_balance( deps, - env, staking_contract, config.staking_contract_code_hash, addr, + key, )?); let user_reward_per_token = USER_REWARD_PER_TOKEN .load(deps.storage, addr.clone()) @@ -404,24 +407,23 @@ fn get_total_staked( Ok(resp.total) } -// Need to add submsg functionality fn get_staked_balance( deps: Deps, - env: &Env, contract_address: &Addr, staking_contract_code_hash: String, addr: &Addr, + key: String, ) -> StdResult { - let key = SNIP20_STAKING_VIEWING_KEY.load(deps.storage)?; let msg = snip20_stake::msg::QueryMsg::StakedBalanceAtHeight { - contract_address:Some(env.contract.address.to_string()), address: addr.into(), height: None, key, }; - let resp: snip20_stake::msg::StakedBalanceAtHeightResponse = - deps.querier - .query_wasm_smart(staking_contract_code_hash, contract_address.to_string(), &msg)?; + let resp: snip20_stake::msg::StakedBalanceAtHeightResponse = deps.querier.query_wasm_smart( + staking_contract_code_hash, + contract_address.to_string(), + &msg, + )?; Ok(resp.balance) } @@ -459,10 +461,10 @@ fn scale_factor() -> Uint256 { pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::Info {} => Ok(to_binary(&query_info(deps, env)?)?), - QueryMsg::GetPendingRewards { address } => { - Ok(to_binary(&query_pending_rewards(deps, env, address)?)?) - } QueryMsg::Ownership {} => to_binary(&cw_ownable::get_ownership(deps.storage)?), + QueryMsg::GetPendingRewards { address, key } => { + Ok(to_binary(&query_pending_rewards(deps, env, address, key)?)?) + } } } @@ -476,6 +478,7 @@ pub fn query_pending_rewards( deps: Deps, env: Env, addr: String, + key: String, ) -> StdResult { let addr = deps.api.addr_validate(&addr)?; let config = CONFIG.load(deps.storage)?; @@ -485,13 +488,8 @@ pub fn query_pending_rewards( &config.staking_contract, config.staking_contract_code_hash, )?; - let earned_rewards = get_rewards_earned( - deps, - &env, - &addr, - reward_per_token, - &config.staking_contract, - )?; + let earned_rewards = + get_rewards_earned(deps, &addr, reward_per_token, &config.staking_contract, key)?; let existing_rewards = PENDING_REWARDS .load(deps.storage, addr.clone()) @@ -505,22 +503,6 @@ pub fn query_pending_rewards( }) } -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { - match msg.id { - EXECUTE_SNIP20_STAKE_VIEWING_KEY_ID => match msg.result { - SubMsgResult::Ok(res) => { - let data: snip20_stake::msg::CreateViewingKeyResponse = - from_binary(&res.data.unwrap())?; - SNIP20_STAKING_VIEWING_KEY.save(deps.storage, &data.key)?; - Ok(Response::new().add_attribute("action", "create_snip20_stake_viewing_key")) - } - SubMsgResult::Err(_) => Err(ContractError::Snip20StakeExecuteError {}), - }, - _ => Err(ContractError::UnknownReplyId { id: msg.id }), - } -} - // #[cfg(test)] // mod tests { // use std::borrow::BorrowMut; diff --git a/contracts/staking/snip20-stake-external-rewards/src/msg.rs b/contracts/staking/snip20-stake-external-rewards/src/msg.rs index 803b7b8..3f5d1ba 100644 --- a/contracts/staking/snip20-stake-external-rewards/src/msg.rs +++ b/contracts/staking/snip20-stake-external-rewards/src/msg.rs @@ -1,6 +1,6 @@ use crate::state::{Config, Denom, RewardConfig}; use cosmwasm_schema::QueryResponses; -use cosmwasm_std::{Addr, Binary, Uint128}; +use cosmwasm_std::{Addr, Api, Binary, StdResult, Uint128}; use dao_hooks::stake::StakeChangedHookMsg; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -38,10 +38,18 @@ pub struct Snip20ReceiveMsg { #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { StakeChangeHook(StakeChangedHookMsg), - Claim {}, + Claim { + key: String, + }, Receive(Snip20ReceiveMsg), Fund {}, - UpdateRewardDuration { new_duration: u64 }, + UpdateRewardDuration { + new_duration: u64, + }, + SetViewingKey { + key: String, + padding: Option, + }, } #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] @@ -59,17 +67,29 @@ pub enum ReceiveMsg { Fund {}, } -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug, QueryResponses)] +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug, QueryResponses)] #[serde(rename_all = "snake_case")] pub enum QueryMsg { #[returns(InfoResponse)] Info {}, #[returns(PendingRewardsResponse)] - GetPendingRewards { address: String }, + GetPendingRewards { address: String, key: String }, #[returns(::cw_ownable::Ownership<::cosmwasm_std::Addr>)] Ownership {}, } +impl QueryMsg { + pub fn get_validation_params(&self, api: &dyn Api) -> StdResult<(Vec, String)> { + match self { + Self::GetPendingRewards { key, address, .. } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + _ => panic!("This query type does not require authentication"), + } + } +} + #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] pub struct InfoResponse { pub config: Config, diff --git a/contracts/staking/snip20-stake-external-rewards/src/state.rs b/contracts/staking/snip20-stake-external-rewards/src/state.rs index 566c6de..80df6e2 100644 --- a/contracts/staking/snip20-stake-external-rewards/src/state.rs +++ b/contracts/staking/snip20-stake-external-rewards/src/state.rs @@ -36,4 +36,4 @@ pub const PENDING_REWARDS: Map = Map::new("pending_rewards"); pub const USER_REWARD_PER_TOKEN: Map = Map::new("user_reward_per_token"); -pub const SNIP20_STAKING_VIEWING_KEY: Item = Item::new("snip20_staking_viewing_key"); +pub const VIEWING_KEY_INFO: Map = Map::new("viewing_key_info"); diff --git a/contracts/staking/snip20-stake-reward-distributor/examples/schema.rs b/contracts/staking/snip20-stake-reward-distributor/examples/schema.rs index 9412131..9008f3c 100644 --- a/contracts/staking/snip20-stake-reward-distributor/examples/schema.rs +++ b/contracts/staking/snip20-stake-reward-distributor/examples/schema.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::write_api; -use cw20_stake_reward_distributor::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use snip20_stake_reward_distributor::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; fn main() { write_api! { diff --git a/contracts/staking/snip20-stake-reward-distributor/src/contract.rs b/contracts/staking/snip20-stake-reward-distributor/src/contract.rs index d6ee2f6..a5c6054 100644 --- a/contracts/staking/snip20-stake-reward-distributor/src/contract.rs +++ b/contracts/staking/snip20-stake-reward-distributor/src/contract.rs @@ -2,15 +2,19 @@ use std::cmp::min; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_binary,from_binary, Addr, CosmosMsg, Reply, StdError, SubMsg, Uint128, WasmMsg,SubMsgResult}; +use cosmwasm_std::{ + from_binary, to_binary, Addr, CosmosMsg, Reply, StdError, SubMsg, SubMsgResult, Uint128, + WasmMsg, +}; use secret_toolkit::utils::HandleCallback; +use snip20_reference_impl::msg::ExecuteAnswer; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InfoResponse, InstantiateMsg, QueryMsg,MigrateMsg}; +use crate::msg::{ExecuteMsg, InfoResponse, InstantiateMsg, MigrateMsg, QueryMsg}; use crate::snip20_msg; use crate::state::{Config, CONFIG, LAST_PAYMENT_BLOCK, TOKEN_VIEWING_KEY}; use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; -use secret_cw2::{set_contract_version,ContractVersion}; +use secret_cw2::{get_contract_version, set_contract_version, ContractVersion}; pub(crate) const CONTRACT_NAME: &str = "crates.io:snip20-stake-reward-distributor"; pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -35,7 +39,7 @@ pub fn instantiate( } let reward_token = deps.api.addr_validate(&msg.reward_token)?; - if !validate_cw20( + if !validate_snip20( deps.as_ref(), reward_token.clone(), msg.reward_token_code_hash.clone(), @@ -75,7 +79,8 @@ pub fn instantiate( .add_attribute("staking_addr", staking_addr.into_string()) .add_attribute("reward_token", reward_token.into_string()) .add_attribute("reward_rate", msg.reward_rate) - .add_submessage(submsg).set_data(to_binary(&env.contract.code_hash.clone()))) + .add_submessage(submsg) + .set_data(to_binary(&(env.contract.address, env.contract.code_hash))?)) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -132,7 +137,7 @@ pub fn execute_update_config( } let reward_token = deps.api.addr_validate(&reward_token)?; - if !validate_cw20( + if !validate_snip20( deps.as_ref(), reward_token.clone(), reward_token_code_hash.clone(), @@ -173,12 +178,12 @@ pub fn execute_update_owner( Ok(Response::default().add_attributes(ownership.into_attributes())) } -pub fn validate_cw20(deps: Deps, snip20_addr: Addr, snip20_code_hash: String) -> bool { - let response: Result = +pub fn validate_snip20(deps: Deps, snip20_addr: Addr, snip20_code_hash: String) -> bool { + let response: Result = deps.querier.query_wasm_smart( snip20_code_hash, snip20_addr, - &snip20_reference_impl::msg::QueryMsg::TokenInfo {}, + &secret_toolkit::snip20::QueryMsg::TokenInfo {}, ); response.is_ok() } @@ -204,29 +209,26 @@ fn get_distribution_msg(deps: Deps, env: &Env) -> Result StdResult { let config = CONFIG.load(deps.storage)?; let token_viewing_key = TOKEN_VIEWING_KEY.load(deps.storage)?; let last_payment_block = LAST_PAYMENT_BLOCK.load(deps.storage)?; - let balance_info: snip20_reference_impl::msg::Balance = deps.querier.query_wasm_smart( + let balance_info: secret_toolkit::snip20::query::Balance = deps.querier.query_wasm_smart( config.reward_token_code_hash.clone(), config.reward_token.clone(), - &snip20_reference_impl::msg::QueryMsg::Balance { + &secret_toolkit::snip20::QueryMsg::Balance { address: env.contract.address.to_string(), key: token_viewing_key, }, @@ -338,9 +338,16 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { // let mut token_viewing_key=TOKEN_VIEWING_KEY.load(deps.storage).unwrap_or_default(); - let data: snip20_reference_impl::msg::CreateViewingKeyResponse = + let data: snip20_reference_impl::msg::ExecuteAnswer = from_binary(&res.data.unwrap())?; - TOKEN_VIEWING_KEY.save(deps.storage, &data.key)?; + let mut viewing_key = String::new(); + match data { + ExecuteAnswer::CreateViewingKey { key } => { + viewing_key = key; + } + _ => {} + } + TOKEN_VIEWING_KEY.save(deps.storage, &viewing_key)?; Ok(Response::new().add_attribute("action", "create_token_viewing_key")) } SubMsgResult::Err(_) => Err(ContractError::TokenExecuteError {}), diff --git a/contracts/staking/snip20-stake/Cargo.toml b/contracts/staking/snip20-stake/Cargo.toml index 1434fcd..2baf049 100644 --- a/contracts/staking/snip20-stake/Cargo.toml +++ b/contracts/staking/snip20-stake/Cargo.toml @@ -26,7 +26,6 @@ cw-paginate-storage = { workspace = true } cw-ownable = { workspace = true } dao-hooks = { workspace = true } dao-voting = { workspace = true } -snip20-reference-impl={ workspace=true } cw20-stake-v1 = { workspace = true, features = ["library"] } cw-utils-v1 = { workspace = true } diff --git a/contracts/staking/snip20-stake/src/contract.rs b/contracts/staking/snip20-stake/src/contract.rs index 98e40e8..a77eb9b 100644 --- a/contracts/staking/snip20-stake/src/contract.rs +++ b/contracts/staking/snip20-stake/src/contract.rs @@ -1,4 +1,5 @@ use crate::math; +use crate::msg::{CreateViewingKeyResponse, QueryWithPermit, ViewingKeyError}; use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, ReceiveMsg}; use crate::msg::{ GetHooksResponse, ListStakersResponse, QueryMsg, Snip20ReceiveMsg, @@ -18,10 +19,8 @@ use cw_hooks::HookItem; use dao_hooks::stake::{stake_hook_msgs, unstake_hook_msgs}; use dao_voting::duration::validate_duration; use secret_cw2::{get_contract_version, set_contract_version, ContractVersion}; -use snip20_reference_impl::msg::CreateViewingKeyResponse; -use snip20_reference_impl::msg::ExecuteMsg::Transfer; - use secret_cw_controllers::ClaimsResponse; +use secret_toolkit::permit::{Permit, RevokedPermits, TokenPermissions}; pub use secret_toolkit::snip20::handle::{ burn_from_msg, burn_msg, decrease_allowance_msg, increase_allowance_msg, mint_msg, send_from_msg, send_msg, transfer_from_msg, transfer_msg, @@ -33,6 +32,8 @@ use secret_utils::Duration; pub(crate) const CONTRACT_NAME: &str = "crates.io:snip20-stake"; pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; + #[entry_point] pub fn instantiate( deps: DepsMut, @@ -47,11 +48,12 @@ pub fn instantiate( // though this provides some protection against mistakes where the // wrong address is provided. let token_address = deps.api.addr_validate(&msg.token_address)?; - let token_info: snip20_reference_impl::msg::TokenInfo = deps.querier.query_wasm_smart( + let token_info: secret_toolkit::snip20::query::TokenInfo = deps.querier.query_wasm_smart( msg.token_code_hash.clone().unwrap(), &token_address, - &snip20_reference_impl::msg::QueryMsg::TokenInfo {}, + &secret_toolkit::snip20::QueryMsg::TokenInfo {}, )?; + let _supply = token_info.total_supply.unwrap(); validate_duration(msg.unstaking_duration)?; @@ -60,6 +62,7 @@ pub fn instantiate( token_address, token_code_hash: msg.token_code_hash.clone().unwrap(), unstaking_duration: msg.unstaking_duration, + contract_address: env.contract.address, }; CONFIG.save(deps.storage, &config)?; @@ -71,7 +74,7 @@ pub fn instantiate( set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - Ok(Response::new().set_data(to_binary(&env.contract.code_hash)?)) + Ok(Response::new()) } #[entry_point] @@ -93,7 +96,9 @@ pub fn execute( execute_remove_hook(deps, env, info, addr, code_hash) } ExecuteMsg::UpdateOwnership(action) => execute_update_owner(deps, info, env, action), - ExecuteMsg::CreateViewingKey { entropy } => try_create_key(deps, env, info, entropy), + ExecuteMsg::CreateViewingKey { entropy, .. } => try_create_key(deps, env, info, entropy), + ExecuteMsg::SetViewingKey { key, .. } => try_set_key(deps, info, key), + ExecuteMsg::RevokePermit { permit_name, .. } => revoke_permit(deps, info, permit_name), } } @@ -226,13 +231,11 @@ pub fn execute_unstake( let hook_msgs = unstake_hook_msgs(HOOKS, deps.storage, info.sender.clone(), amount)?; match config.unstaking_duration { None => { - let snip_send_msg = Transfer { + let snip_send_msg = secret_toolkit::snip20::HandleMsg::Transfer { recipient: info.sender.to_string(), amount: amount_to_claim, memo: None, padding: None, - decoys: None, - entropy: None, }; let wasm_msg = cosmwasm_std::WasmMsg::Execute { contract_addr: config.token_address.to_string(), @@ -280,13 +283,11 @@ pub fn execute_claim( return Err(ContractError::NothingToClaim {}); } let config = CONFIG.load(deps.storage)?; - let cw_send_msg = Transfer { + let cw_send_msg = secret_toolkit::snip20::HandleMsg::Transfer { recipient: info.sender.to_string(), amount: release, memo: None, padding: None, - decoys: None, - entropy: None, }; let wasm_msg = cosmwasm_std::WasmMsg::Execute { contract_addr: config.token_address.to_string(), @@ -385,59 +386,129 @@ pub fn try_create_key( Ok(Response::new().set_data(to_binary(&CreateViewingKeyResponse { key })?)) } +pub fn try_set_key( + deps: DepsMut, + info: MessageInfo, + key: String, +) -> Result { + ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); + Ok(Response::default()) +} + +fn revoke_permit( + deps: DepsMut, + info: MessageInfo, + permit_name: String, +) -> Result { + RevokedPermits::revoke_permit( + deps.storage, + PREFIX_REVOKED_PERMITS, + info.sender.as_str(), + &permit_name, + ); + + Ok(Response::default()) +} + #[entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::GetConfig {} => to_binary(&query_config(deps)?), - QueryMsg::StakedBalanceAtHeight { - key, - contract_address, - address, - height, - } => to_binary(&query_staked_balance_at_height( - deps, - env, - key, - contract_address, - address, - height, - )?), QueryMsg::TotalStakedAtHeight { height } => { to_binary(&query_total_staked_at_height(deps, env, height)?) } - QueryMsg::StakedValue { key, address } => { - to_binary(&query_staked_value(deps, env, key, address)?) - } QueryMsg::TotalValue {} => to_binary(&query_total_value(deps, env)?), - QueryMsg::Claims { - key, - address, - contract_address, - } => to_binary(&query_claims(deps, key, address, contract_address)?), QueryMsg::GetHooks {} => to_binary(&query_hooks(deps)?), QueryMsg::ListStakers {} => query_list_stakers(deps), QueryMsg::Ownership {} => to_binary(&cw_ownable::get_ownership(deps.storage)?), + QueryMsg::WithPermit { permit, query } => permit_queries(deps, env, permit, query), + _ => viewing_keys_queries(deps, env, msg), } } +fn permit_queries( + deps: Deps, + env: Env, + permit: Permit, + query: QueryWithPermit, +) -> Result { + // Validate permit content + let token_address = CONFIG.load(deps.storage)?.contract_address; + + let _account = secret_toolkit::permit::validate( + deps, + PREFIX_REVOKED_PERMITS, + &permit, + token_address.into_string(), + None, + )?; + + // Permit validated! We can now execute the query. + match query { + QueryWithPermit::StakedBalanceAtHeight { address, height } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query balance, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_staked_balance_at_height(deps, env, address, height)?) + } + QueryWithPermit::StakedValue { address } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query history, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_staked_value(deps, address)?) + } + QueryWithPermit::Claims { address } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query history, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_claims(deps, address)?) + } + } +} + +pub fn viewing_keys_queries(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + let (addresses, key) = msg.get_validation_params(deps.api)?; + + for address in addresses { + let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); + if result.is_ok() { + return match msg { + // Base + QueryMsg::StakedBalanceAtHeight { + address, height, .. + } => to_binary(&query_staked_balance_at_height(deps, env, address, height)?), + QueryMsg::StakedValue { address, .. } => { + to_binary(&query_staked_value(deps, address)?) + } + QueryMsg::Claims { address, .. } => to_binary(&query_claims(deps, address)?), + _ => panic!("This query type does not require authentication"), + }; + } + } + + to_binary(&ViewingKeyError { + msg: "Wrong viewing key for this address or viewing key not set".to_string(), + }) +} + pub fn query_staked_balance_at_height( deps: Deps, env: Env, - key: String, - contract_address: Option, address: String, height: Option, ) -> StdResult { - if contract_address.is_some() { - authenticate( - deps, - deps.api.addr_validate(&contract_address.unwrap())?, - key, - )?; - } else { - authenticate(deps, deps.api.addr_validate(&address)?, key)?; - } - let address = deps.api.addr_validate(&address)?; let height = height.unwrap_or(env.block.height); let balance = StakedBalancesStore::may_load_at_height(deps.storage, address, height)?; @@ -461,14 +532,7 @@ pub fn query_total_staked_at_height( }) } -pub fn query_staked_value( - deps: Deps, - _env: Env, - key: String, - address: String, -) -> StdResult { - authenticate(deps, deps.api.addr_validate(&address)?, key)?; - +pub fn query_staked_value(deps: Deps, address: String) -> StdResult { let address = deps.api.addr_validate(&address)?; let balance = BALANCE.load(deps.storage).unwrap_or_default(); let staked = StakedBalancesStore::load(deps.storage, address); @@ -497,22 +561,7 @@ pub fn query_config(deps: Deps) -> StdResult { Ok(config) } -pub fn query_claims( - deps: Deps, - key: String, - address: String, - contract_address: Option, -) -> StdResult { - if contract_address.is_some() { - authenticate( - deps, - deps.api.addr_validate(&contract_address.unwrap())?, - key, - )?; - } else { - authenticate(deps, deps.api.addr_validate(&address)?, key)?; - } - +pub fn query_claims(deps: Deps, address: String) -> StdResult { CLAIMS.query_claims(deps, &deps.api.addr_validate(&address)?) } @@ -545,11 +594,6 @@ pub fn query_list_stakers(deps: Deps) -> StdResult { to_binary(&ListStakersResponse { stakers }) } -// Helper Functions -fn authenticate(deps: Deps, addr: Addr, key: String) -> StdResult<()> { - ViewingKey::check(deps.storage, addr.as_ref(), &key) -} - #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { let storage_version: ContractVersion = get_contract_version(deps.storage)?; diff --git a/contracts/staking/snip20-stake/src/msg.rs b/contracts/staking/snip20-stake/src/msg.rs index ef4aafb..7bc8597 100644 --- a/contracts/staking/snip20-stake/src/msg.rs +++ b/contracts/staking/snip20-stake/src/msg.rs @@ -1,9 +1,9 @@ use cosmwasm_schema::QueryResponses; -use cosmwasm_std::{Addr, Binary, Uint128}; +use cosmwasm_std::{Addr, Api, Binary, StdResult, Uint128}; use cw_hooks::HookItem; -// use snip20_reference_impl::receiver::Snip20ReceiveMsg; use cw_ownable::cw_ownable_execute; use schemars::JsonSchema; +use secret_toolkit::permit::Permit; use secret_utils::Duration; use serde::{Deserialize, Serialize}; @@ -37,12 +37,33 @@ pub struct Snip20ReceiveMsg { #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { Receive(Snip20ReceiveMsg), - Unstake { amount: Uint128 }, + Unstake { + amount: Uint128, + }, Claim {}, - UpdateConfig { duration: Option }, - AddHook { addr: String, code_hash: String }, - RemoveHook { addr: String, code_hash: String }, - CreateViewingKey { entropy: String }, + UpdateConfig { + duration: Option, + }, + AddHook { + addr: String, + code_hash: String, + }, + RemoveHook { + addr: String, + code_hash: String, + }, + CreateViewingKey { + entropy: String, + padding: Option, + }, + SetViewingKey { + key: String, + padding: Option, + }, // Permit + RevokePermit { + permit_name: String, + padding: Option, + }, } #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] @@ -75,7 +96,6 @@ pub enum QueryMsg { #[returns(StakedBalanceAtHeightResponse)] StakedBalanceAtHeight { key: String, - contract_address: Option, address: String, height: Option, }, @@ -88,21 +108,56 @@ pub enum QueryMsg { #[returns(crate::state::Config)] GetConfig {}, #[returns(ClaimsResponse)] - Claims { - key: String, - address: String, - contract_address: Option, - }, + Claims { key: String, address: String }, #[returns(GetHooksResponse)] GetHooks {}, #[returns(ListStakersResponse)] ListStakers {}, #[returns(::cw_ownable::Ownership::<::cosmwasm_std::Addr>)] Ownership {}, + #[returns(())] + WithPermit { + permit: Permit, + query: QueryWithPermit, + }, } -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +impl QueryMsg { + pub fn get_validation_params(&self, api: &dyn Api) -> StdResult<(Vec, String)> { + match self { + Self::StakedBalanceAtHeight { key, address, .. } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + Self::StakedValue { key, address } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + Self::Claims { key, address } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + _ => panic!("This query type does not require authentication"), + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, QueryResponses)] +#[cfg_attr(test, derive(Eq, PartialEq))] +#[serde(rename_all = "snake_case")] +pub enum QueryWithPermit { + #[returns(StakedBalanceAtHeightResponse)] + StakedBalanceAtHeight { + address: String, + height: Option, + }, + #[returns(StakedValueResponse)] + StakedValue { address: String }, + #[returns(ClaimsResponse)] + Claims { address: String }, +} +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] pub enum MigrateMsg { /// Migrates the contract from version one to version two. This /// will remove the contract's current manager, and require a @@ -153,3 +208,8 @@ pub enum ResponseStatus { Success, Failure, } + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +pub struct ViewingKeyError { + pub msg: String, +} diff --git a/contracts/staking/snip20-stake/src/state.rs b/contracts/staking/snip20-stake/src/state.rs index 9ff5cf9..5216836 100644 --- a/contracts/staking/snip20-stake/src/state.rs +++ b/contracts/staking/snip20-stake/src/state.rs @@ -12,6 +12,8 @@ pub struct Config { pub token_address: Addr, pub token_code_hash: String, pub unstaking_duration: Option, + // the address of this contract, used to validate query permits + pub contract_address: Addr, } pub const RESPONSE_BLOCK_SIZE: usize = 256; @@ -114,8 +116,8 @@ impl StakedBalancesStore { if id.unwrap() == (x.len() - 1) { return Ok(STAKED_BALANCES_PRIMARY.get(store, &key)); } else { - let snapshot_value = STAKED_BALANCES_SNAPSHOT - .get(store, &(x[id.unwrap() + 1 as usize], key.clone())); + let snapshot_value = + STAKED_BALANCES_SNAPSHOT.get(store, &(x[id.unwrap() + 1_usize], key.clone())); return Ok(snapshot_value); } } diff --git a/contracts/voting/dao-voting-cw4/Cargo.toml b/contracts/voting/dao-voting-cw4/Cargo.toml index db96c10..deea16e 100644 --- a/contracts/voting/dao-voting-cw4/Cargo.toml +++ b/contracts/voting/dao-voting-cw4/Cargo.toml @@ -19,16 +19,18 @@ library = [] [dependencies] cosmwasm-std = { workspace = true } cosmwasm-storage = { workspace = true } -cosmwasm-schema = { workspace = true } secret-storage-plus = { workspace = true } secret-cw2 = { workspace = true } secret-utils = { workspace = true } +cosmwasm-schema = { workspace = true } thiserror = { workspace = true } dao-dao-macros = { workspace = true } dao-interface = { workspace = true } cw4 = { workspace = true } -cw4-group = { workspace = true, features = ["library"] } -secret-toolkit = { workspace = true } +secret-toolkit ={ workspace = true } +serde = { workspace = true } +schemars ={ workspace = true } +secret-cw-controllers ={ workspace = true } [dev-dependencies] secret-multi-test = { workspace = true } diff --git a/contracts/voting/dao-voting-cw4/src/contract.rs b/contracts/voting/dao-voting-cw4/src/contract.rs index c4bcabf..4ec4a1c 100644 --- a/contracts/voting/dao-voting-cw4/src/contract.rs +++ b/contracts/voting/dao-voting-cw4/src/contract.rs @@ -1,4 +1,3 @@ -use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ @@ -6,12 +5,12 @@ use cosmwasm_std::{ SubMsg, SubMsgResult, Uint128, }; use cw4::{MemberListResponse, MemberResponse, TotalWeightResponse}; -use cw4_group::msg::InstantiateMsgResponse; // use cw4_group::msg::InstantiateMsg as Cw4GroupInstantiateMsg; use dao_interface::state::AnyContractInfo; use secret_cw2::{get_contract_version, set_contract_version, ContractVersion}; use secret_toolkit::utils::InitCallback; +use crate::cw4_group_msg; use crate::error::ContractError; use crate::msg::{ExecuteMsg, GroupContract, InstantiateMsg, MigrateMsg, QueryMsg}; use crate::state::{DAO, GROUP_CONTRACT}; @@ -21,21 +20,6 @@ pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const INSTANTIATE_GROUP_REPLY_ID: u64 = 0; -// #[cw_serde] -// pub struct Cw4GroupInstantiateMsg { -// /// The admin is the only account that can update the group state. -// /// Omit it to make the group immutable. -// pub admin: Option, -// pub members: Vec, -// } - -#[cw_serde] -struct Cw4GroupInstantiateMsg(cw4_group::msg::InstantiateMsg); - -impl InitCallback for Cw4GroupInstantiateMsg { - const BLOCK_SIZE: usize = 256; -} - #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, @@ -90,10 +74,10 @@ pub fn instantiate( // Instantiate group contract, set DAO as admin. // Voting module contracts are instantiated by the main dao-dao-core // contract, so the Admin is set to info.sender. - let msg = Cw4GroupInstantiateMsg(cw4_group::msg::InstantiateMsg { + let msg = cw4_group_msg::Cw4GroupInstantiateMsg { admin: Some(info.sender.to_string()), members: initial_members, - }); + }; let sub_msg = SubMsg::reply_always( msg.to_cosmos_msg( Some(info.sender.to_string()), @@ -107,11 +91,7 @@ pub fn instantiate( Ok(Response::new() .add_attribute("action", "instantiate") - .add_submessage(sub_msg) - .set_data(to_binary(&InstantiateMsgResponse { - code_hash: env.contract.code_hash, - address: env.contract.address.to_string(), - })?)) + .add_submessage(sub_msg)) } GroupContract::Existing { address, code_hash } => { let group_contract = deps.api.addr_validate(&address.clone())?; @@ -120,7 +100,7 @@ pub fn instantiate( let res: MemberListResponse = deps.querier.query_wasm_smart( code_hash.clone(), group_contract.clone(), - &cw4_group::msg::QueryMsg::ListMembers { + &cw4_group_msg::Cw4GroupQueryMsg::ListMembers { start_after: None, limit: Some(1), }, @@ -131,7 +111,7 @@ pub fn instantiate( } let data = AnyContractInfo { - addr: deps.api.addr_validate(&address)?, + addr: deps.api.addr_validate(&address.clone())?, code_hash: code_hash.clone(), }; @@ -139,11 +119,7 @@ pub fn instantiate( Ok(Response::new() .add_attribute("action", "instantiate") - .add_attribute("group_contract", group_contract.to_string()) - .set_data(to_binary(&InstantiateMsgResponse { - code_hash: env.contract.code_hash, - address: env.contract.address.to_string(), - })?)) + .add_attribute("group_contract", group_contract.to_string())) } } } @@ -161,13 +137,15 @@ pub fn execute( #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::VotingPowerAtHeight { address, height } => { - query_voting_power_at_height(deps, env, address, height) - } QueryMsg::TotalPowerAtHeight { height } => query_total_power_at_height(deps, env, height), QueryMsg::Info {} => query_info(deps), QueryMsg::GroupContract {} => to_binary(&GROUP_CONTRACT.load(deps.storage)?), QueryMsg::Dao {} => to_binary(&DAO.load(deps.storage)?), + QueryMsg::VotingPowerAtHeight { + address, + key, + height, + } => query_voting_power_at_height(deps, env, address, key, height), } } @@ -175,6 +153,7 @@ pub fn query_voting_power_at_height( deps: Deps, env: Env, address: String, + key: String, height: Option, ) -> StdResult { let addr = deps.api.addr_validate(&address)?.to_string(); @@ -182,9 +161,10 @@ pub fn query_voting_power_at_height( let res: MemberResponse = deps.querier.query_wasm_smart( group_contract.code_hash, group_contract.addr, - &cw4_group::msg::QueryMsg::Member { + &cw4_group_msg::Cw4GroupQueryMsg::Member { addr, at_height: height, + key, }, )?; @@ -199,7 +179,7 @@ pub fn query_total_power_at_height(deps: Deps, env: Env, height: Option) -> let res: TotalWeightResponse = deps.querier.query_wasm_smart( group_contract.code_hash, group_contract.addr, - &cw4_group::msg::QueryMsg::TotalWeight { at_height: height }, + &cw4_group_msg::Cw4GroupQueryMsg::TotalWeight { at_height: height }, )?; to_binary(&dao_interface::voting::TotalPowerAtHeightResponse { power: res.weight.into(), @@ -234,19 +214,21 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Err(ContractError::GroupContractInstantiateError {}), }, + _ => Err(ContractError::UnknownReplyId { id: msg.id }), } } diff --git a/contracts/voting/dao-voting-cw4/src/cw4_group_msg.rs b/contracts/voting/dao-voting-cw4/src/cw4_group_msg.rs new file mode 100644 index 0000000..0b0959f --- /dev/null +++ b/contracts/voting/dao-voting-cw4/src/cw4_group_msg.rs @@ -0,0 +1,84 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cw4::Member; +use schemars::JsonSchema; +use secret_toolkit::{ + permit::Permit, + utils::{HandleCallback, InitCallback}, +}; +use serde::{Deserialize, Serialize}; + +#[cw_serde] +pub struct Cw4GroupInstantiateMsg { + /// The admin is the only account that can update the group state. + /// Omit it to make the group immutable. + pub admin: Option, + pub members: Vec, +} + +#[cw_serde] +pub struct InstantiateMsgResponse { + /// The admin is the only account that can update the group state. + /// Omit it to make the group immutable. + pub address: String, + pub code_hash: String, +} + +#[cw_serde] +pub enum Cw4GroupExecuteMsg { + CreateViewingKey { + entropy: String, + padding: Option, + }, +} + +impl InitCallback for Cw4GroupInstantiateMsg { + const BLOCK_SIZE: usize = 256; +} + +impl HandleCallback for Cw4GroupExecuteMsg { + const BLOCK_SIZE: usize = 256; +} + +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] +pub struct CreateViewingKeyResponse { + pub key: String, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum Cw4GroupQueryMsg { + #[returns(secret_cw_controllers::AdminResponse)] + Admin {}, + #[returns(cw4::TotalWeightResponse)] + TotalWeight { at_height: Option }, + #[returns(cw4::MemberListResponse)] + ListMembers { + start_after: Option, + limit: Option, + }, + #[returns(cw4::MemberResponse)] + Member { + addr: String, + key: String, + at_height: Option, + }, + /// Shows all registered hooks. + #[returns(secret_cw_controllers::HooksResponse)] + Hooks {}, + #[returns(())] + WithPermit { + permit: Permit, + query: QueryWithPermit, + }, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, QueryResponses)] +#[cfg_attr(test, derive(Eq, PartialEq))] +#[serde(rename_all = "snake_case")] +pub enum QueryWithPermit { + #[returns(cw4::MemberResponse)] + Member { + address: String, + at_height: Option, + }, +} diff --git a/contracts/voting/dao-voting-cw4/src/lib.rs b/contracts/voting/dao-voting-cw4/src/lib.rs index f3befed..f88d6a9 100644 --- a/contracts/voting/dao-voting-cw4/src/lib.rs +++ b/contracts/voting/dao-voting-cw4/src/lib.rs @@ -1,9 +1,12 @@ #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] pub mod contract; +pub mod cw4_group_msg; mod error; pub mod msg; pub mod state; #[cfg(test)] -mod tests; \ No newline at end of file +mod tests; + +pub use crate::error::ContractError; diff --git a/contracts/voting/dao-voting-cw4/src/msg.rs b/contracts/voting/dao-voting-cw4/src/msg.rs index f7b49aa..0e89bf8 100644 --- a/contracts/voting/dao-voting-cw4/src/msg.rs +++ b/contracts/voting/dao-voting-cw4/src/msg.rs @@ -1,6 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use dao_dao_macros::voting_module_query; -use dao_interface::state::AnyContractInfo; #[cw_serde] pub enum GroupContract { @@ -28,7 +27,7 @@ pub enum ExecuteMsg {} #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - #[returns(AnyContractInfo)] + #[returns(dao_interface::state::AnyContractInfo)] GroupContract {}, } diff --git a/contracts/voting/dao-voting-snip20-staked/src/contract.rs b/contracts/voting/dao-voting-snip20-staked/src/contract.rs index 8ea845e..d1c25e4 100644 --- a/contracts/voting/dao-voting-snip20-staked/src/contract.rs +++ b/contracts/voting/dao-voting-snip20-staked/src/contract.rs @@ -1,24 +1,23 @@ use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, Snip20TokenInfo, StakingInfo}; -use crate::{snip20_msg, snip20_stake_msg}; use crate::state::{ - StakingContractInfo, TokenContractInfo, ACTIVE_THRESHOLD, DAO, SNIP20_STAKING_VIEWING_KEY, - STAKING_CONTRACT, STAKING_CONTRACT_CODE_HASH, STAKING_CONTRACT_CODE_ID, - STAKING_CONTRACT_UNSTAKING_DURATION, TOKEN_CONTRACT, TOKEN_VIEWING_KEY, + StakingContractInfo, TokenContractInfo, ACTIVE_THRESHOLD, DAO, STAKING_CONTRACT, + STAKING_CONTRACT_CODE_ID, STAKING_CONTRACT_UNSTAKING_DURATION, TOKEN_CONTRACT, }; +use crate::{snip20_msg, snip20_stake_msg}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_binary, to_binary, Addr, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, - Response, StdResult, SubMsg, SubMsgResult, Uint128, Uint256, + to_binary, Addr, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, + SubMsg, SubMsgResult, Uint128, Uint256, }; +use dao_interface::state::AnyContractInfo; use dao_interface::voting::IsActiveResponse; use dao_voting::threshold::ActiveThreshold; use dao_voting::threshold::ActiveThresholdResponse; use secret_cw2::{get_contract_version, set_contract_version, ContractVersion}; -use secret_toolkit::utils::{HandleCallback, InitCallback}; -use secret_utils::parse_reply_instantiate_data; - +use secret_toolkit::utils::InitCallback; +use secret_utils::parse_reply_event_for_contract_address; use std::convert::TryInto; pub(crate) const CONTRACT_NAME: &str = "crates.io:dao-voting-snip20-staked"; @@ -26,8 +25,8 @@ pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); const INSTANTIATE_TOKEN_REPLY_ID: u64 = 0; const INSTANTIATE_STAKING_REPLY_ID: u64 = 1; -const EXECUTE_TOKEN_VIEWING_KEY_ID: u64 = 2; -const EXECUTE_SNIP20_STAKE_VIEWING_KEY_ID: u64 = 3; + +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; // We multiply by this when calculating needed power for being active // when using active threshold with percent @@ -41,7 +40,13 @@ pub fn instantiate( msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - DAO.save(deps.storage, &info.sender)?; + DAO.save( + deps.storage, + &AnyContractInfo { + addr: info.sender.clone(), + code_hash: msg.dao_code_hash, + }, + )?; if let Some(active_threshold) = msg.active_threshold.as_ref() { if let ActiveThreshold::Percentage { percent } = active_threshold { @@ -64,19 +69,6 @@ pub fn instantiate( code_hash: code_hash.clone(), }; TOKEN_CONTRACT.save(deps.storage, &token_contract)?; - let gen_viewing_key_msg = snip20_msg::Snip20ExecuteMsg::CreateViewingKey { - entropy: "entropy".to_string(), - padding: None, - }; - - let submsg = SubMsg::reply_on_success( - gen_viewing_key_msg.to_cosmos_msg( - code_hash.clone(), - address.clone().to_string(), - None, - )?, - EXECUTE_TOKEN_VIEWING_KEY_ID, - ); if let Some(ActiveThreshold::AbsoluteCount { count }) = msg.active_threshold { assert_valid_absolute_count_threshold( @@ -100,7 +92,7 @@ pub fn instantiate( }; let resp: snip20_stake::state::Config = deps.querier.query_wasm_smart( staking_contract_code_hash.clone(), - &staking_contract_address.clone(), + staking_contract_address.clone(), &snip20_stake::msg::QueryMsg::GetConfig {}, )?; @@ -108,25 +100,12 @@ pub fn instantiate( return Err(ContractError::StakingContractMismatch {}); } - let gen_viewing_key_msg = snip20_stake_msg::ExecuteMsg::CreateViewingKey { - entropy: "entropy".to_string(), - }; - let msg = SubMsg::reply_always( - gen_viewing_key_msg.to_cosmos_msg( - staking_contract_code_hash.clone(), - staking_contract_address.clone().to_string(), - None, - )?, - EXECUTE_SNIP20_STAKE_VIEWING_KEY_ID, - ); STAKING_CONTRACT.save(deps.storage, &staking_contract)?; Ok(Response::default() .add_attribute("action", "instantiate") .add_attribute("token", "existing_token") .add_attribute("token_address", address) - .add_attribute("staking_contract", staking_contract_address) - .add_submessage(submsg) - .add_submessage(msg)) + .add_attribute("staking_contract", staking_contract_address)) } StakingInfo::New { staking_code_id, @@ -140,6 +119,10 @@ pub fn instantiate( token_address: address.to_string(), token_code_hash: Some(code_hash), }; + let staking_contract = StakingContractInfo { + addr: String::new(), + code_hash: staking_code_hash.clone(), + }; let msg = SubMsg::reply_always( init_msg.to_cosmos_msg( @@ -151,6 +134,7 @@ pub fn instantiate( )?, INSTANTIATE_STAKING_REPLY_ID, ); + STAKING_CONTRACT.save(deps.storage, &staking_contract)?; Ok(Response::default() .add_attribute("action", "instantiate") .add_attribute("token", "existing_token") @@ -191,10 +175,19 @@ pub fn instantiate( initial_balances.push(intitial_balance); } } + let staking_contract = StakingContractInfo { + addr: String::new(), + code_hash: staking_code_hash.clone(), + }; + let token_contract = TokenContractInfo { + addr: String::new(), + code_hash: code_hash.clone(), + }; STAKING_CONTRACT_CODE_ID.save(deps.storage, &staking_code_id)?; - STAKING_CONTRACT_CODE_HASH.save(deps.storage, &staking_code_hash)?; STAKING_CONTRACT_UNSTAKING_DURATION.save(deps.storage, &unstaking_duration)?; + STAKING_CONTRACT.save(deps.storage, &staking_contract)?; + TOKEN_CONTRACT.save(deps.storage, &token_contract)?; let init_msg = snip20_msg::InstantiateMsg { name, @@ -235,10 +228,10 @@ pub fn assert_valid_absolute_count_threshold( return Err(ContractError::ZeroActiveCount {}); } - let token_info: snip20_reference_impl::msg::TokenInfo = deps.querier.query_wasm_smart( + let token_info: secret_toolkit::snip20::query::TokenInfo = deps.querier.query_wasm_smart( token_code_hash.clone(), token_addr, - &snip20_reference_impl::msg::QueryMsg::TokenInfo {}, + &secret_toolkit::snip20::QueryMsg::TokenInfo {}, )?; if count > token_info.total_supply.unwrap() { @@ -268,7 +261,7 @@ pub fn execute_update_active_threshold( new_active_threshold: Option, ) -> Result { let dao = DAO.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -302,14 +295,16 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::TokenContract {} => query_token_contract(deps), QueryMsg::StakingContract {} => query_staking_contract(deps), - QueryMsg::VotingPowerAtHeight { address, height } => { - query_voting_power_at_height(deps, env, address, height) - } QueryMsg::TotalPowerAtHeight { height } => query_total_power_at_height(deps, env, height), QueryMsg::Info {} => query_info(deps), QueryMsg::Dao {} => query_dao(deps), QueryMsg::IsActive {} => query_is_active(deps), QueryMsg::ActiveThreshold {} => query_active_threshold(deps), + QueryMsg::VotingPowerAtHeight { + address, + key, + height, + } => query_voting_power_at_height(deps, address, key, height), } } @@ -323,24 +318,21 @@ pub fn query_staking_contract(deps: Deps) -> StdResult { to_binary(&staking_contract) } -/// Have to implement submsg functionaity pub fn query_voting_power_at_height( deps: Deps, - env: Env, address: String, + key: String, height: Option, ) -> StdResult { let staking_contract = STAKING_CONTRACT.load(deps.storage)?; let address = deps.api.addr_validate(&address)?; - let snip20_stake_viewing_key = SNIP20_STAKING_VIEWING_KEY.load(deps.storage)?; let res: snip20_stake::msg::StakedBalanceAtHeightResponse = deps.querier.query_wasm_smart( staking_contract.code_hash, staking_contract.addr, &snip20_stake::msg::QueryMsg::StakedBalanceAtHeight { address: address.to_string(), height, - key: snip20_stake_viewing_key, - contract_address: Some(env.contract.address.to_string()), + key, }, )?; to_binary(&dao_interface::voting::VotingPowerAtHeightResponse { @@ -414,11 +406,11 @@ pub fn query_is_active(deps: Deps) -> StdResult { // rounding is rounding down, so the whole thing can // be safely unwrapped at the end of the day thank you // for coming to my ted talk. - let total_potential_power: snip20_reference_impl::msg::TokenInfo = + let total_potential_power: secret_toolkit::snip20::query::TokenInfo = deps.querier.query_wasm_smart( token_contract.code_hash, token_contract.addr, - &snip20_reference_impl::msg::QueryMsg::TokenInfo {}, + &secret_toolkit::snip20::QueryMsg::TokenInfo {}, )?; let total_power = total_potential_power .total_supply @@ -470,93 +462,62 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result match msg.result { SubMsgResult::Ok(res) => { let mut token_contract = TOKEN_CONTRACT.load(deps.storage).unwrap_or_default(); - let token_init_response: snip20_reference_impl::msg::InitResponse = - from_binary(&res.data.unwrap())?; - token_contract.addr = token_init_response.contract_address.to_string(); - token_contract.code_hash = token_init_response.code_hash.clone(); - - TOKEN_CONTRACT.save(deps.storage, &token_contract)?; + // let token_init_response: snip20_reference_impl::msg::InitResponse = + // from_binary(&res.data.unwrap())?; + let token_address = parse_reply_event_for_contract_address(res.events)?; + token_contract.addr = token_address.clone(); let active_threshold = ACTIVE_THRESHOLD.may_load(deps.storage)?; if let Some(ActiveThreshold::AbsoluteCount { count }) = active_threshold { assert_valid_absolute_count_threshold( deps.as_ref(), - &deps.api.addr_validate( - &token_init_response.contract_address.clone().to_string(), - )?, - token_init_response.code_hash.clone(), + &deps.api.addr_validate(&token_address.clone())?, + token_contract.code_hash.clone(), count, )?; } let staking_contract_code_id = STAKING_CONTRACT_CODE_ID.load(deps.storage)?; - let staking_contract_code_hash = STAKING_CONTRACT_CODE_HASH.load(deps.storage)?; + let staking_contract = STAKING_CONTRACT.load(deps.storage)?; let unstaking_duration = STAKING_CONTRACT_UNSTAKING_DURATION.load(deps.storage)?; - let dao = DAO.load(deps.storage)?; + let dao_info = DAO.load(deps.storage)?; let init_msg = snip20_stake_msg::InstantiateMsg { - owner: Some(dao.clone().to_string()), + owner: Some(dao_info.addr.clone().to_string()), unstaking_duration, - token_address: token_init_response.contract_address.clone().to_string(), - token_code_hash: Some(token_init_response.code_hash.clone()), + token_address: token_address.clone(), + token_code_hash: Some(token_contract.code_hash.clone()), }; let msg = SubMsg::reply_on_success( init_msg.to_cosmos_msg( - Some(dao.clone().to_string()), + Some(dao_info.addr.clone().to_string()), env.contract.address.to_string(), staking_contract_code_id, - staking_contract_code_hash, + staking_contract.code_hash, None, )?, INSTANTIATE_STAKING_REPLY_ID, ); + TOKEN_CONTRACT.save(deps.storage, &token_contract)?; Ok(Response::default() - .add_attribute("token_address", token_init_response.contract_address) + .add_attribute("token_address", token_address) .add_submessage(msg)) } SubMsgResult::Err(_) => Err(ContractError::TokenInstantiateError {}), }, INSTANTIATE_STAKING_REPLY_ID => match msg.result { - SubMsgResult::Ok(res) => { - let staking_code_hash: String = - from_binary(&res.data.unwrap())?; - let staking_address = parse_reply_instantiate_data(msg); - + SubMsgResult::Ok(resp) => { let mut staking_contract = STAKING_CONTRACT.load(deps.storage).unwrap_or_default(); + let staking_address = parse_reply_event_for_contract_address(resp.events)?; - staking_contract.addr = staking_address.unwrap().contract_address.clone(); - staking_contract.code_hash = staking_code_hash; + staking_contract.addr = staking_address.clone(); STAKING_CONTRACT.save(deps.storage, &staking_contract)?; - Ok(Response::new().add_attribute( - "staking contract address ", - staking_address.unwrap().contract_address, - )) + Ok(Response::new().add_attribute("staking contract address ", staking_address)) } SubMsgResult::Err(_) => Err(ContractError::StakingInstantiateError {}), }, - EXECUTE_TOKEN_VIEWING_KEY_ID => { - match msg.result { - SubMsgResult::Ok(res) => { - // let mut token_viewing_key=TOKEN_VIEWING_KEY.load(deps.storage).unwrap_or_default(); - let data: snip20_reference_impl::msg::CreateViewingKeyResponse = - from_binary(&res.data.unwrap())?; - TOKEN_VIEWING_KEY.save(deps.storage, &data.key)?; - Ok(Response::new().add_attribute("action", "create_token_viewing_key")) - } - SubMsgResult::Err(_) => Err(ContractError::TokenExecuteError {}), - } - } - EXECUTE_SNIP20_STAKE_VIEWING_KEY_ID => match msg.result { - SubMsgResult::Ok(res) => { - let data: snip20_stake::msg::CreateViewingKeyResponse = - from_binary(&res.data.unwrap())?; - SNIP20_STAKING_VIEWING_KEY.save(deps.storage, &data.key)?; - Ok(Response::new().add_attribute("action", "create_snip20_stake_viewing_key")) - } - SubMsgResult::Err(_) => Err(ContractError::Snip20StakeExecuteError {}), - }, _ => Err(ContractError::UnknownReplyId { id: msg.id }), } } diff --git a/contracts/voting/dao-voting-snip20-staked/src/error.rs b/contracts/voting/dao-voting-snip20-staked/src/error.rs index 22d8814..34b64b2 100644 --- a/contracts/voting/dao-voting-snip20-staked/src/error.rs +++ b/contracts/voting/dao-voting-snip20-staked/src/error.rs @@ -1,4 +1,5 @@ use cosmwasm_std::StdError; +use secret_utils::ParseReplyError; use thiserror::Error; #[derive(Error, Debug)] @@ -9,6 +10,9 @@ pub enum ContractError { #[error("Unauthorized")] Unauthorized {}, + #[error(transparent)] + ParseReplyError(#[from] ParseReplyError), + #[error("Initial governance token balances must not be empty")] InitialBalancesError {}, diff --git a/contracts/voting/dao-voting-snip20-staked/src/lib.rs b/contracts/voting/dao-voting-snip20-staked/src/lib.rs index 6a80cfc..cddebeb 100644 --- a/contracts/voting/dao-voting-snip20-staked/src/lib.rs +++ b/contracts/voting/dao-voting-snip20-staked/src/lib.rs @@ -4,8 +4,8 @@ pub mod contract; mod error; pub mod msg; pub mod snip20_msg; -pub mod state; pub mod snip20_stake_msg; +pub mod state; #[cfg(test)] mod tests; diff --git a/contracts/voting/dao-voting-snip20-staked/src/msg.rs b/contracts/voting/dao-voting-snip20-staked/src/msg.rs index 1b74dd8..42a86e1 100644 --- a/contracts/voting/dao-voting-snip20-staked/src/msg.rs +++ b/contracts/voting/dao-voting-snip20-staked/src/msg.rs @@ -2,7 +2,7 @@ use crate::snip20_msg::InitialBalance; use cosmwasm_schema::QueryResponses; use cosmwasm_std::Uint128; use dao_dao_macros::{active_query, cw20_token_query, voting_module_query}; -use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; +use dao_voting::threshold::ActiveThreshold; use schemars::JsonSchema; use secret_utils::Duration; use serde::{Deserialize, Serialize}; @@ -68,6 +68,7 @@ pub struct InstantiateMsg { /// The number or percentage of tokens that must be staked /// for the DAO to be active pub active_threshold: Option, + pub dao_code_hash: String, } #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] @@ -89,7 +90,7 @@ pub enum QueryMsg { /// is wrapping. #[returns(cosmwasm_std::Addr)] StakingContract {}, - #[returns(ActiveThresholdResponse)] + #[returns(dao_voting::threshold::ActiveThresholdResponse)] ActiveThreshold {}, } diff --git a/contracts/voting/dao-voting-snip20-staked/src/snip20_msg.rs b/contracts/voting/dao-voting-snip20-staked/src/snip20_msg.rs index ffd8820..3b925ce 100644 --- a/contracts/voting/dao-voting-snip20-staked/src/snip20_msg.rs +++ b/contracts/voting/dao-voting-snip20-staked/src/snip20_msg.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{Binary, Uint128}; use schemars::JsonSchema; -use secret_toolkit::utils::{HandleCallback, InitCallback}; +use secret_toolkit::utils::InitCallback; use serde::{Deserialize, Serialize}; #[cfg_attr(test, derive(Eq, PartialEq))] @@ -52,16 +52,3 @@ pub struct InitConfig { /// default: False can_modify_denoms: Option, } - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum Snip20ExecuteMsg { - CreateViewingKey { - entropy: String, - padding: Option, - }, -} - -impl HandleCallback for Snip20ExecuteMsg { - const BLOCK_SIZE: usize = 256; -} diff --git a/contracts/voting/dao-voting-snip20-staked/src/snip20_stake_msg.rs b/contracts/voting/dao-voting-snip20-staked/src/snip20_stake_msg.rs index 1e6a908..fe379a2 100644 --- a/contracts/voting/dao-voting-snip20-staked/src/snip20_stake_msg.rs +++ b/contracts/voting/dao-voting-snip20-staked/src/snip20_stake_msg.rs @@ -1,8 +1,7 @@ use schemars::JsonSchema; +use secret_toolkit::utils::InitCallback; use secret_utils::Duration; use serde::{Deserialize, Serialize}; -use secret_toolkit::utils::{HandleCallback, InitCallback}; - #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] pub struct InstantiateMsg { @@ -16,13 +15,3 @@ pub struct InstantiateMsg { impl InitCallback for InstantiateMsg { const BLOCK_SIZE: usize = 256; } - -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] -pub enum ExecuteMsg { - CreateViewingKey { entropy: String }, -} - -impl HandleCallback for ExecuteMsg { - const BLOCK_SIZE: usize = 256; -} \ No newline at end of file diff --git a/contracts/voting/dao-voting-snip20-staked/src/state.rs b/contracts/voting/dao-voting-snip20-staked/src/state.rs index cd803c3..f93ae77 100644 --- a/contracts/voting/dao-voting-snip20-staked/src/state.rs +++ b/contracts/voting/dao-voting-snip20-staked/src/state.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::Addr; +use dao_interface::state::AnyContractInfo; use dao_voting::threshold::ActiveThreshold; use schemars::JsonSchema; use secret_storage_plus::Item; @@ -16,13 +16,12 @@ pub struct TokenContractInfo { pub addr: String, pub code_hash: String, } + pub const ACTIVE_THRESHOLD: Item = Item::new("active_threshold"); pub const TOKEN_CONTRACT: Item = Item::new("token"); -pub const DAO: Item = Item::new("dao"); +pub const DAO: Item = Item::new("dao"); pub const STAKING_CONTRACT: Item = Item::new("staking_contract"); pub const STAKING_CONTRACT_UNSTAKING_DURATION: Item> = Item::new("staking_contract_unstaking_duration"); pub const STAKING_CONTRACT_CODE_ID: Item = Item::new("staking_contract_code_id"); pub const STAKING_CONTRACT_CODE_HASH: Item = Item::new("staking_contract_code_hash"); -pub const TOKEN_VIEWING_KEY: Item = Item::new("token_viewing_key"); -pub const SNIP20_STAKING_VIEWING_KEY: Item = Item::new("snip20_staking_viewing_key"); diff --git a/contracts/voting/dao-voting-snip721-roles/src/contract.rs b/contracts/voting/dao-voting-snip721-roles/src/contract.rs index 4eb73d9..fc25a7c 100644 --- a/contracts/voting/dao-voting-snip721-roles/src/contract.rs +++ b/contracts/voting/dao-voting-snip721-roles/src/contract.rs @@ -1,13 +1,15 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_binary, to_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, Response, - StdResult, SubMsg, SubMsgResult, WasmMsg, + to_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, Response, StdResult, + SubMsg, SubMsgResult, WasmMsg, }; use cw4::{MemberResponse, TotalWeightResponse}; +use dao_interface::state::AnyContractInfo; use dao_snip721_extensions::roles::QueryExt; use secret_cw2::set_contract_version; +use secret_utils::parse_reply_event_for_contract_address; use crate::msg::{ExecuteMsg, InstantiateMsg, NftContract, QueryMsg}; use crate::state::{Config, CONFIG, DAO, INITIAL_NFTS}; @@ -28,7 +30,13 @@ pub fn instantiate( ) -> Result, ContractError> { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - DAO.save(deps.storage, &info.sender)?; + DAO.save( + deps.storage, + &AnyContractInfo { + addr: info.sender.clone(), + code_hash: msg.dao_code_hash, + }, + )?; match msg.nft_contract { NftContract::Existing { address, code_hash } => { @@ -63,7 +71,7 @@ pub fn instantiate( INITIAL_NFTS.save(deps.storage, &initial_nfts)?; let init_msg = snip721roles::Snip721RolesInstantiateMsg { - code_id:snip721_code_id, + code_id: snip721_code_id, code_hash: snip721_code_hash.clone(), label: label.clone(), name, @@ -82,6 +90,11 @@ pub fn instantiate( )?, INSTANTIATE_NFT_CONTRACT_REPLY_ID, ); + let config = Config { + nft_address: Addr::unchecked(""), + nft_code_hash: snip721_roles_code_hash.clone(), + }; + CONFIG.save(deps.storage, &config)?; Ok(Response::default().add_submessage(msg)) } @@ -103,9 +116,11 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::Config {} => query_config(deps), QueryMsg::Dao {} => query_dao(deps), - QueryMsg::VotingPowerAtHeight { address, height } => { - query_voting_power_at_height(deps, env, address, height) - } + QueryMsg::VotingPowerAtHeight { + address, + height, + key, + } => query_voting_power_at_height(deps, env, address, key, height), QueryMsg::TotalPowerAtHeight { height } => query_total_power_at_height(deps, env, height), QueryMsg::Info {} => query_info(deps), } @@ -115,6 +130,7 @@ pub fn query_voting_power_at_height( deps: Deps, env: Env, address: String, + key: String, at_height: Option, ) -> StdResult { let config = CONFIG.load(deps.storage)?; @@ -123,6 +139,7 @@ pub fn query_voting_power_at_height( config.nft_address, &snip721_roles::msg::QueryMsg::ExtensionQuery(QueryExt::Member { addr: address, + key, at_height, }), )?; @@ -173,15 +190,14 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { let dao = DAO.load(deps.storage)?; - let data: snip721_roles::msg::InstantiateResponse = - from_binary(&res.data.unwrap())?; + let mut config = CONFIG.load(deps.storage)?; + let nft_roles_contract_address = + parse_reply_event_for_contract_address(res.events)?; // Save config - let config = Config { - nft_address: data.contract_address.clone(), - nft_code_hash: data.code_hash.clone(), - }; - CONFIG.save(deps.storage, &config)?; + config.nft_address = deps + .api + .addr_validate(&nft_roles_contract_address.clone())?; let initial_nfts = INITIAL_NFTS.load(deps.storage)?; @@ -190,8 +206,8 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Result { Ok(SubMsg::new(WasmMsg::Execute { - contract_addr: data.contract_address.to_string().clone(), - code_hash: data.code_hash.to_string().clone(), + contract_addr: nft_roles_contract_address.clone(), + code_hash: config.nft_code_hash.clone(), funds: vec![], msg: to_binary(&snip721_roles::msg::ExecuteMsg::Snip721Execute( snip721_roles::snip721::Snip721ExecuteMsg::MintNft { @@ -233,20 +249,22 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result = Item::new("config"); -pub const DAO: Item = Item::new("dao"); +pub const DAO: Item = Item::new("dao"); // Holds initial NFTs messages during instantiation. pub const INITIAL_NFTS: Item> = Item::new("initial_nfts"); diff --git a/contracts/voting/dao-voting-snip721-staked/src/contract.rs b/contracts/voting/dao-voting-snip721-staked/src/contract.rs index 74ae2bd..02a37c3 100644 --- a/contracts/voting/dao-voting-snip721-staked/src/contract.rs +++ b/contracts/voting/dao-voting-snip721-staked/src/contract.rs @@ -1,10 +1,13 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; +use cosmwasm_std::StdError; use cosmwasm_std::{ from_binary, to_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Empty, Env, MessageInfo, Reply, Response, StdResult, SubMsg, SubMsgResult, Uint128, Uint256, WasmMsg, }; +use cw_hooks::HookItem; use dao_hooks::nft_stake::{stake_nft_hook_msgs, unstake_nft_hook_msgs}; +use dao_interface::state::AnyContractInfo; use dao_interface::state::ModuleInstantiateCallback; use dao_interface::{nft::NftFactoryCallback, voting::IsActiveResponse}; use dao_voting::duration::validate_duration; @@ -12,14 +15,20 @@ use dao_voting::threshold::{ assert_valid_absolute_count_threshold, assert_valid_percentage_threshold, ActiveThreshold, ActiveThresholdResponse, }; -use cw_hooks::HookItem; use schemars::JsonSchema; use secret_cw2::{get_contract_version, set_contract_version, ContractVersion}; +use secret_toolkit::permit::RevokedPermits; +use secret_toolkit::permit::{Permit, TokenPermissions}; use secret_toolkit::utils::{HandleCallback, InitCallback}; +use secret_toolkit::viewing_key::ViewingKey; +use secret_toolkit::viewing_key::ViewingKeyStore; +use secret_utils::parse_reply_event_for_contract_address; use secret_utils::Duration; use serde::{Deserialize, Serialize}; use crate::error::ContractError; +use crate::msg::QueryWithPermit; +use crate::msg::{CreateViewingKey, ViewingKeyError}; use crate::msg::{ ExecuteMsg, InstantiateMsg, MigrateMsg, NftContract, QueryMsg, Snip721ReceiveMsg, }; @@ -40,6 +49,8 @@ const FACTORY_EXECUTE_REPLY_ID: u64 = 2; // when using active threshold with percent const PRECISION_FACTOR: u128 = 10u128.pow(9); +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; + #[derive(Serialize, Deserialize, JsonSchema)] // Supported NFT instantiation messages pub enum NftInstantiateMsg { @@ -84,7 +95,13 @@ pub fn instantiate( ) -> Result, ContractError> { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; - DAO.save(deps.storage, &info.sender)?; + DAO.save( + deps.storage, + &AnyContractInfo { + addr: info.sender.clone(), + code_hash: msg.dao_code_hash, + }, + )?; // Validate unstaking duration validate_duration(msg.unstaking_duration)?; @@ -236,6 +253,9 @@ pub fn execute( ExecuteMsg::UpdateActiveThreshold { new_threshold } => { execute_update_active_threshold(deps, env, info, new_threshold) } + ExecuteMsg::CreateViewingKey { entropy, .. } => try_create_key(deps, env, info, entropy), + ExecuteMsg::SetViewingKey { key, .. } => try_set_key(deps, info, key), + ExecuteMsg::RevokePermit { permit_name, .. } => revoke_permit(deps, info, permit_name), } } @@ -336,7 +356,7 @@ pub fn execute_unstake( Ok(cosmwasm_std::WasmMsg::Execute { contract_addr: config.nft_address.to_string(), code_hash: config.nft_code_hash.clone(), - msg: to_binary(&snip721::Snip721ExecuteMsg::TransferNft { + msg: to_binary(&secret_toolkit::snip721::HandleMsg::TransferNft { recipient: info.sender.to_string(), token_id, memo: None, @@ -399,7 +419,7 @@ pub fn execute_claim_nfts( Ok(WasmMsg::Execute { contract_addr: config.nft_address.to_string(), code_hash: config.nft_code_hash.clone(), - msg: to_binary(&snip721::Snip721ExecuteMsg::TransferNft { + msg: to_binary(&secret_toolkit::snip721::HandleMsg::TransferNft { recipient: info.sender.to_string(), token_id: nft, memo: None, @@ -426,7 +446,7 @@ pub fn execute_update_config( let dao = DAO.load(deps.storage)?; // Only the DAO can update the config. - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -456,7 +476,7 @@ pub fn execute_add_hook( let dao = DAO.load(deps.storage)?; // Only the DAO can add a hook - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -483,7 +503,7 @@ pub fn execute_remove_hook( let dao = DAO.load(deps.storage)?; // Only the DAO can remove a hook - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -508,7 +528,7 @@ pub fn execute_update_active_threshold( new_active_threshold: Option, ) -> Result { let dao = DAO.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -519,11 +539,12 @@ pub fn execute_update_active_threshold( assert_valid_percentage_threshold(percent)?; } ActiveThreshold::AbsoluteCount { count } => { - let nft_supply: snip721::NumTokens = deps.querier.query_wasm_smart( - config.nft_code_hash.clone(), - config.nft_address, - &snip721::Snip721QueryMsg::NumTokens { viewer: None }, - )?; + let nft_supply: secret_toolkit::snip721::query::NumTokens = + deps.querier.query_wasm_smart( + config.nft_code_hash.clone(), + config.nft_address, + &secret_toolkit::snip721::QueryMsg::NumTokens { viewer: None }, + )?; assert_valid_absolute_count_threshold( count, Uint128::new(nft_supply.count.into()), @@ -538,6 +559,47 @@ pub fn execute_update_active_threshold( Ok(Response::new().add_attribute("action", "update_active_threshold")) } +pub fn try_set_key( + deps: DepsMut, + info: MessageInfo, + key: String, +) -> Result { + ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); + Ok(Response::default()) +} + +pub fn try_create_key( + deps: DepsMut, + env: Env, + info: MessageInfo, + entropy: String, +) -> Result { + let key = ViewingKey::create( + deps.storage, + &info, + &env, + info.sender.as_str(), + entropy.as_ref(), + ); + + Ok(Response::new().set_data(to_binary(&CreateViewingKey { key })?)) +} + +fn revoke_permit( + deps: DepsMut, + info: MessageInfo, + permit_name: String, +) -> Result { + RevokedPermits::revoke_permit( + deps.storage, + PREFIX_REVOKED_PERMITS, + info.sender.as_str(), + &permit_name, + ); + + Ok(Response::default()) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { @@ -546,16 +608,87 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::Dao {} => query_dao(deps), QueryMsg::Info {} => query_info(deps), QueryMsg::IsActive {} => query_is_active(deps, env), - QueryMsg::NftClaims { address } => query_nft_claims(deps, address), QueryMsg::Hooks {} => query_hooks(deps), - QueryMsg::StakedNfts { address } => query_staked_nfts(deps, address), QueryMsg::TotalPowerAtHeight { height } => query_total_power_at_height(deps, env, height), - QueryMsg::VotingPowerAtHeight { address, height } => { - query_voting_power_at_height(deps, env, address, height) + QueryMsg::WithPermit { permit, query } => permit_queries(deps, env, permit, query), + _ => viewing_keys_queries(deps, env, msg), + } +} + +fn permit_queries( + deps: Deps, + env: Env, + permit: Permit, + query: QueryWithPermit, +) -> Result { + // Validate permit content + + let _account = secret_toolkit::permit::validate( + deps, + PREFIX_REVOKED_PERMITS, + &permit, + env.contract.address.clone().into_string(), + None, + )?; + + // Permit validated! We can now execute the query. + match query { + QueryWithPermit::NftClaims { address, .. } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query nft Claims, got permissions {:?}", + permit.params.permissions + ))); + } + + query_nft_claims(deps, address) + } + QueryWithPermit::StakedNfts { address, .. } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query stake nfts, got permissions {:?}", + permit.params.permissions + ))); + } + + query_staked_nfts(deps, address) + } + QueryWithPermit::VotingPowerAtHeight { address, height } => { + if !permit.check_permission(&TokenPermissions::History) { + return Err(StdError::generic_err(format!( + "No permission to query voting power at height, got permissions {:?}", + permit.params.permissions + ))); + } + + query_voting_power_at_height(deps, env, address, Some(height)) } } } +pub fn viewing_keys_queries(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + let (addresses, key) = msg.get_validation_params(deps.api)?; + + for address in addresses { + let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); + if result.is_ok() { + return match msg { + // Base + QueryMsg::NftClaims { address, .. } => query_nft_claims(deps, address), + QueryMsg::StakedNfts { address, .. } => query_staked_nfts(deps, address), + QueryMsg::VotingPowerAtHeight { + address, height, .. + } => query_voting_power_at_height(deps, env, address, height), + _ => panic!("This query type does not require authentication"), + }; + } + } + + to_binary(&ViewingKeyError { + msg: "Wrong viewing key for this address or viewing key not set".to_string(), + }) +} + pub fn query_active_threshold(deps: Deps) -> StdResult { to_binary(&ActiveThresholdResponse { active_threshold: ACTIVE_THRESHOLD.may_load(deps.storage)?, @@ -567,10 +700,10 @@ pub fn query_is_active(deps: Deps, env: Env) -> StdResult { if let Some(threshold) = threshold { let config = CONFIG.load(deps.storage)?; let staked_nfts = StakedNftsTotalStore::may_load_at_height(deps.storage, env.block.height)?; - let total_nfts: snip721::NumTokens = deps.querier.query_wasm_smart( + let total_nfts: secret_toolkit::snip721::query::NumTokens = deps.querier.query_wasm_smart( config.nft_code_hash.clone(), config.nft_address, - &snip721::Snip721QueryMsg::NumTokens { viewer: None }, + &secret_toolkit::snip721::QueryMsg::NumTokens { viewer: None }, )?; match threshold { @@ -719,13 +852,11 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { let dao = DAO.load(deps.storage)?; - let nft_info: snip721_reference_impl::msg::InstantiateResponse = - from_binary(&res.data.unwrap())?; + let nft_contract_address = parse_reply_event_for_contract_address(res.events)?; // Save NFT contract to config let mut config = CONFIG.load(deps.storage)?; - config.nft_address = nft_info.contract_address.clone(); - config.nft_code_hash = nft_info.code_hash.clone(); + config.nft_address = deps.api.addr_validate(&nft_contract_address.clone())?; CONFIG.save(deps.storage, &config)?; let initial_nfts = INITIAL_NFTS.load(deps.storage)?; @@ -735,10 +866,10 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Result { Ok(SubMsg::new(WasmMsg::Execute { - contract_addr: nft_info.contract_address.to_string().clone(), + contract_addr: nft_contract_address.clone(), funds: vec![], msg: nft.clone(), - code_hash: nft_info.code_hash.clone(), + code_hash: config.nft_code_hash.clone(), })) }) .collect::>(); @@ -749,23 +880,20 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Err(ContractError::NftInstantiateError {}), @@ -775,18 +903,18 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Result Result { // Parse reply data @@ -835,11 +968,12 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result( - info.nft_code_hash.clone(), - nft_address.clone(), - &snip721::Snip721QueryMsg::NumTokens { viewer: None }, - )?; + deps.querier + .query_wasm_smart::( + info.nft_code_hash.clone(), + nft_address.clone(), + &secret_toolkit::snip721::QueryMsg::NumTokens { viewer: None }, + )?; // Update NFT contract config.nft_address = nft_address; diff --git a/contracts/voting/dao-voting-snip721-staked/src/msg.rs b/contracts/voting/dao-voting-snip721-staked/src/msg.rs index 809765c..a0af9a5 100644 --- a/contracts/voting/dao-voting-snip721-staked/src/msg.rs +++ b/contracts/voting/dao-voting-snip721-staked/src/msg.rs @@ -1,8 +1,9 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Binary}; +use cosmwasm_std::{Addr, Api, Binary, StdResult}; use dao_dao_macros::{active_query, voting_module_query}; -use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; +use dao_voting::threshold::ActiveThreshold; use schemars::JsonSchema; +use secret_toolkit::permit::Permit; use secret_utils::Duration; use serde::{Deserialize, Serialize}; @@ -77,6 +78,8 @@ pub struct InstantiateMsg { /// The number or percentage of tokens that must be staked /// for the DAO to be active pub active_threshold: Option, + + pub dao_code_hash: String, } #[cw_serde] @@ -105,6 +108,19 @@ pub enum ExecuteMsg { UpdateActiveThreshold { new_threshold: Option, }, + CreateViewingKey { + entropy: String, + padding: Option, + }, + SetViewingKey { + key: String, + padding: Option, + }, + // Permit + RevokePermit { + permit_name: String, + padding: Option, + }, } #[active_query] @@ -115,14 +131,39 @@ pub enum QueryMsg { #[returns(crate::state::Config)] Config {}, #[returns(::snip721_controllers::NftClaimsResponse)] - NftClaims { address: String }, + NftClaims { address: String, key: String }, #[returns(::secret_cw_controllers::HooksResponse)] Hooks {}, // List the staked NFTs for a given address. #[returns(Vec)] - StakedNfts { address: String }, - #[returns(ActiveThresholdResponse)] + StakedNfts { address: String, key: String }, + #[returns(dao_voting::threshold::ActiveThresholdResponse)] ActiveThreshold {}, + #[returns(())] + WithPermit { + permit: Permit, + query: QueryWithPermit, + }, +} + +impl QueryMsg { + pub fn get_validation_params(&self, api: &dyn Api) -> StdResult<(Vec, String)> { + match self { + Self::NftClaims { address, key } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + Self::StakedNfts { address, key, .. } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + Self::VotingPowerAtHeight { address, key, .. } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + _ => panic!("This query type does not require authentication"), + } + } } #[cw_serde] @@ -132,3 +173,24 @@ pub struct MigrateMsg {} pub struct IsActiveResponse { pub active: bool, } + +#[cw_serde] +pub struct CreateViewingKey { + pub key: String, +} + +#[cw_serde] +pub struct ViewingKeyError { + pub msg: String, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryWithPermit { + #[returns(::snip721_controllers::NftClaimsResponse)] + NftClaims { address: String }, + #[returns(Vec)] + StakedNfts { address: String }, + #[returns(dao_interface::voting::VotingPowerAtHeightResponse)] + VotingPowerAtHeight { address: String, height: u64 }, +} diff --git a/contracts/voting/dao-voting-snip721-staked/src/state.rs b/contracts/voting/dao-voting-snip721-staked/src/state.rs index 0303e27..dc0e8f2 100644 --- a/contracts/voting/dao-voting-snip721-staked/src/state.rs +++ b/contracts/voting/dao-voting-snip721-staked/src/state.rs @@ -1,6 +1,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Binary, Empty, StdError, StdResult, Storage, Uint128}; use cw_hooks::Hooks; +use dao_interface::state::AnyContractInfo; use dao_voting::threshold::ActiveThreshold; use secret_storage_plus::Item; use secret_toolkit::storage::Keymap; @@ -18,7 +19,7 @@ pub struct Config { pub const ACTIVE_THRESHOLD: Item = Item::new("active_threshold"); pub const CONFIG: Item = Item::new("config"); -pub const DAO: Item = Item::new("dao"); +pub const DAO: Item = Item::new("dao"); // Holds initial NFTs messages during instantiation. pub const INITIAL_NFTS: Item> = Item::new("initial_nfts"); diff --git a/contracts/voting/dao-voting-token-staked/src/contract.rs b/contracts/voting/dao-voting-token-staked/src/contract.rs index 91f2009..0e43dcd 100644 --- a/contracts/voting/dao-voting-token-staked/src/contract.rs +++ b/contracts/voting/dao-voting-token-staked/src/contract.rs @@ -14,6 +14,7 @@ use cw_hooks::HookItem; use dao_hooks::stake::{stake_hook_msgs, unstake_hook_msgs}; use dao_interface::{ // state::ModuleInstantiateCallback, + state::AnyContractInfo, token::TokenFactoryCallback, voting::{ DenomResponse, IsActiveResponse, TotalPowerAtHeightResponse, VotingPowerAtHeightResponse, @@ -29,12 +30,16 @@ use dao_voting::{ use prost::Message; use secret_cw2::{get_contract_version, set_contract_version, ContractVersion}; use secret_cw_controllers::ClaimsResponse; +use secret_toolkit::{ + permit::{Permit, RevokedPermits, TokenPermissions}, + viewing_key::{ViewingKey, ViewingKeyStore}, +}; // use secret_toolkit::utils::InitCallback; use secret_utils::{must_pay, parse_reply_execute_data, Duration}; use crate::msg::{ - ExecuteMsg, GetHooksResponse, InstantiateMsg, ListStakersResponse, MigrateMsg, QueryMsg, - StakerBalanceResponse, TokenInfo, + CreateViewingKey, ExecuteMsg, GetHooksResponse, InstantiateMsg, ListStakersResponse, + MigrateMsg, QueryMsg, QueryWithPermit, StakerBalanceResponse, TokenInfo, ViewingKeyError, }; use crate::state::{ Config, StakedBalancesStore, TotalStakedStore, ACTIVE_THRESHOLD, CLAIMS, CONFIG, DAO, DENOM, @@ -56,10 +61,12 @@ const FACTORY_EXECUTE_REPLY_ID: u64 = 2; // when using active threshold with percent const PRECISION_FACTOR: u128 = 10u128.pow(9); +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; + #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, - _env: Env, + env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result { @@ -72,7 +79,13 @@ pub fn instantiate( }; CONFIG.save(deps.storage, &config)?; - DAO.save(deps.storage, &info.sender)?; + DAO.save( + deps.storage, + &AnyContractInfo { + addr: info.sender, + code_hash: msg.dao_code_hash, + }, + )?; // Validate Active Threshold if let Some(active_threshold) = msg.active_threshold.as_ref() { @@ -100,8 +113,10 @@ pub fn instantiate( Ok(Response::new() .add_attribute("action", "instantiate") .add_attribute("token", "existing_token") - .add_attribute("denom", denom)) + .add_attribute("denom", denom) + .set_data(to_binary(&(env.contract.address, env.contract.code_hash))?)) } + // TokenInfo::New(ref token) => { // let NewTokenInfo { // subdenom, @@ -183,6 +198,9 @@ pub fn execute( ExecuteMsg::RemoveHook { addr, code_hash } => { execute_remove_hook(deps, env, info, addr, code_hash) } + ExecuteMsg::CreateViewingKey { entropy, .. } => try_create_key(deps, env, info, entropy), + ExecuteMsg::SetViewingKey { key, .. } => try_set_key(deps, info, key), + ExecuteMsg::RevokePermit { permit_name, .. } => revoke_permit(deps, info, permit_name), } } @@ -314,7 +332,7 @@ pub fn execute_update_config( // Only the DAO can update the config let dao = DAO.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -356,7 +374,7 @@ pub fn execute_update_active_threshold( new_active_threshold: Option, ) -> Result { let dao = DAO.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -390,7 +408,7 @@ pub fn execute_add_hook( code_hash: String, ) -> Result { let dao = DAO.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -415,7 +433,7 @@ pub fn execute_remove_hook( code_hash: String, ) -> Result { let dao = DAO.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr { return Err(ContractError::Unauthorized {}); } @@ -432,18 +450,55 @@ pub fn execute_remove_hook( .add_attribute("hook", addr)) } +pub fn try_set_key( + deps: DepsMut, + info: MessageInfo, + key: String, +) -> Result { + ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); + Ok(Response::default()) +} + +pub fn try_create_key( + deps: DepsMut, + env: Env, + info: MessageInfo, + entropy: String, +) -> Result { + let key = ViewingKey::create( + deps.storage, + &info, + &env, + info.sender.as_str(), + entropy.as_ref(), + ); + + Ok(Response::new().set_data(to_binary(&CreateViewingKey { key })?)) +} + +fn revoke_permit( + deps: DepsMut, + info: MessageInfo, + permit_name: String, +) -> Result { + RevokedPermits::revoke_permit( + deps.storage, + PREFIX_REVOKED_PERMITS, + info.sender.as_str(), + &permit_name, + ); + + Ok(Response::default()) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::VotingPowerAtHeight { address, height } => { - to_binary(&query_voting_power_at_height(deps, env, address, height)?) - } QueryMsg::TotalPowerAtHeight { height } => { to_binary(&query_total_power_at_height(deps, env, height)?) } QueryMsg::Info {} => query_info(deps), QueryMsg::Dao {} => query_dao(deps), - QueryMsg::Claims { address } => to_binary(&query_claims(deps, address)?), QueryMsg::GetConfig {} => to_binary(&CONFIG.load(deps.storage)?), QueryMsg::Denom {} => to_binary(&DenomResponse { denom: DENOM.load(deps.storage)?, @@ -455,9 +510,79 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::ActiveThreshold {} => query_active_threshold(deps), QueryMsg::GetHooks {} => to_binary(&query_hooks(deps)?), QueryMsg::TokenContract {} => to_binary(&TOKEN_ISSUER_CONTRACT.may_load(deps.storage)?), + QueryMsg::WithPermit { permit, query } => permit_queries(deps, env, permit, query), + _ => viewing_keys_queries(deps, env, msg), + } +} + +fn permit_queries( + deps: Deps, + env: Env, + permit: Permit, + query: QueryWithPermit, +) -> Result { + // Validate permit content + + let _account = secret_toolkit::permit::validate( + deps, + PREFIX_REVOKED_PERMITS, + &permit, + env.contract.address.clone().into_string(), + None, + )?; + + // Permit validated! We can now execute the query. + match query { + QueryWithPermit::Claims { address } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query nft Claims, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_claims(deps, address)?) + } + QueryWithPermit::VotingPowerAtHeight { address, height } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query voting power at height, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_voting_power_at_height( + deps, + env, + address, + Some(height), + )?) + } } } +pub fn viewing_keys_queries(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + let (addresses, key) = msg.get_validation_params(deps.api)?; + + for address in addresses { + let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); + if result.is_ok() { + return match msg { + // Base + QueryMsg::Claims { address, .. } => to_binary(&query_claims(deps, address)?), + QueryMsg::VotingPowerAtHeight { + address, height, .. + } => to_binary(&query_voting_power_at_height(deps, env, address, height)?), + _ => panic!("This query type does not require authentication"), + }; + } + } + + to_binary(&ViewingKeyError { + msg: "Wrong viewing key for this address or viewing key not set".to_string(), + }) +} + pub fn query_voting_power_at_height( deps: Deps, env: Env, diff --git a/contracts/voting/dao-voting-token-staked/src/msg.rs b/contracts/voting/dao-voting-token-staked/src/msg.rs index 2a6738b..8297680 100644 --- a/contracts/voting/dao-voting-token-staked/src/msg.rs +++ b/contracts/voting/dao-voting-token-staked/src/msg.rs @@ -1,9 +1,10 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Binary, Uint128}; +use cosmwasm_std::{Addr, Api, Binary, StdResult, Uint128}; use cw_hooks::HookItem; use dao_dao_macros::{active_query, native_token_query, voting_module_query}; // use dao_interface::token::NewTokenInfo; -use dao_voting::threshold::{ActiveThreshold, ActiveThresholdResponse}; +use dao_voting::threshold::ActiveThreshold; +use secret_toolkit::permit::Permit; use secret_utils::Duration; #[cw_serde] @@ -15,10 +16,14 @@ pub enum TokenInfo { /// Token factory denom denom: String, }, + /// NOTE* There is right now no way to create new token so will be using existing token + /// Creates a new Token Factory token via the issue contract with the DAO automatically /// setup as admin and owner. + // New(NewTokenInfo), + /// Uses a factory contract that must return the denom, optionally a Token Contract address. /// The binary must serialize to a `WasmMsg::Execute` message. /// Validation happens in the factory contract itself, so be sure to use a @@ -35,6 +40,7 @@ pub struct InstantiateMsg { /// The number or percentage of tokens that must be staked /// for the DAO to be active pub active_threshold: Option, + pub dao_code_hash: String, } #[cw_serde] @@ -57,6 +63,19 @@ pub enum ExecuteMsg { AddHook { addr: String, code_hash: String }, /// Removes a hook that fires on staking / unstaking RemoveHook { addr: String, code_hash: String }, + CreateViewingKey { + entropy: String, + padding: Option, + }, + SetViewingKey { + key: String, + padding: Option, + }, + // Permit + RevokePermit { + permit_name: String, + padding: Option, + }, } #[native_token_query] @@ -68,18 +87,39 @@ pub enum QueryMsg { #[returns(crate::state::Config)] GetConfig {}, #[returns(secret_cw_controllers::ClaimsResponse)] - Claims { address: String }, + Claims { address: String, key: String }, #[returns(ListStakersResponse)] ListStakers { start_after: Option, limit: Option, }, - #[returns(ActiveThresholdResponse)] + #[returns(dao_voting::threshold::ActiveThresholdResponse)] ActiveThreshold {}, #[returns(GetHooksResponse)] GetHooks {}, #[returns(Option)] TokenContract {}, + #[returns(())] + WithPermit { + permit: Permit, + query: QueryWithPermit, + }, +} + +impl QueryMsg { + pub fn get_validation_params(&self, api: &dyn Api) -> StdResult<(Vec, String)> { + match self { + Self::Claims { address, key } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + Self::VotingPowerAtHeight { address, key, .. } => { + let address = api.addr_validate(address.as_str())?; + Ok((vec![address], key.clone())) + } + _ => panic!("This query type does not require authentication"), + } + } } #[cw_serde] @@ -100,3 +140,22 @@ pub struct StakerBalanceResponse { pub struct GetHooksResponse { pub hooks: Vec, } + +#[cw_serde] +pub struct CreateViewingKey { + pub key: String, +} + +#[cw_serde] +pub struct ViewingKeyError { + pub msg: String, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryWithPermit { + #[returns(secret_cw_controllers::ClaimsResponse)] + Claims { address: String }, + #[returns(dao_interface::voting::VotingPowerAtHeightResponse)] + VotingPowerAtHeight { address: String, height: u64 }, +} diff --git a/contracts/voting/dao-voting-token-staked/src/state.rs b/contracts/voting/dao-voting-token-staked/src/state.rs index 31e0447..9001646 100644 --- a/contracts/voting/dao-voting-token-staked/src/state.rs +++ b/contracts/voting/dao-voting-token-staked/src/state.rs @@ -1,6 +1,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, StdResult, Storage, Uint128}; use cw_hooks::Hooks; +use dao_interface::state::AnyContractInfo; use dao_voting::threshold::ActiveThreshold; use secret_cw_controllers::Claims; use secret_storage_plus::Item; @@ -18,7 +19,7 @@ pub struct Config { pub const CONFIG: Item = Item::new("config"); /// The address of the DAO this voting contract is connected to -pub const DAO: Item = Item::new("dao"); +pub const DAO: Item = Item::new("dao"); /// The native denom associated with this contract pub const DENOM: Item = Item::new("denom"); diff --git a/packages/cw-denom/src/lib.rs b/packages/cw-denom/src/lib.rs index 740e38a..1b56915 100644 --- a/packages/cw-denom/src/lib.rs +++ b/packages/cw-denom/src/lib.rs @@ -39,7 +39,7 @@ pub enum CheckedDenom { /// A native (bank module) asset. Native(String), /// A cw20 asset. - Cw20(Addr), + Cw20(Addr,String), } /// A denom that has not been checked to confirm it points to a valid @@ -49,7 +49,7 @@ pub enum UncheckedDenom { /// A native (bank module) asset. Native(String), /// A cw20 asset. - Cw20(String), + Cw20(String,String), } impl UncheckedDenom { @@ -62,20 +62,20 @@ impl UncheckedDenom { /// `cw20::TokenInfoResponse`. /// /// [default SDK rules]: https://github.com/cosmos/cosmos-sdk/blob/7728516abfab950dc7a9120caad4870f1f962df5/types/coin.go#L865-L867 - pub fn into_checked(self, deps: Deps, code_hash: String) -> Result { + pub fn into_checked(self, deps: Deps) -> Result { match self { Self::Native(denom) => validate_native_denom(denom), - Self::Cw20(addr) => { + Self::Cw20(addr,code_hash) => { let addr = deps.api.addr_validate(&addr)?; let _info: secret_toolkit::snip20::TokenInfoResponse = deps .querier .query_wasm_smart( - code_hash, + code_hash.clone(), addr.clone(), &secret_toolkit::snip20::QueryMsg::TokenInfo {}, ) .map_err(|err| DenomError::InvalidCw20 { err })?; - Ok(CheckedDenom::Cw20(addr)) + Ok(CheckedDenom::Cw20(addr,code_hash)) } } } @@ -97,7 +97,7 @@ impl CheckedDenom { pub fn is_cw20(&self, cw20: &Addr) -> bool { match self { CheckedDenom::Native(_) => false, - CheckedDenom::Cw20(a) => a == cw20, + CheckedDenom::Cw20(a,_) => a == cw20, } } @@ -116,7 +116,7 @@ impl CheckedDenom { pub fn is_native(&self, denom: &str) -> bool { match self { CheckedDenom::Native(n) => n == denom, - CheckedDenom::Cw20(_) => false, + CheckedDenom::Cw20(..) => false, } } @@ -124,13 +124,12 @@ impl CheckedDenom { pub fn query_balance( &self, querier: &QuerierWrapper, - code_hash: String, who: &Addr, key: String, ) -> StdResult { match self { CheckedDenom::Native(denom) => Ok(querier.query_balance(who, denom)?.amount), - CheckedDenom::Cw20(address) => { + CheckedDenom::Cw20(address,code_hash) => { let balance: secret_toolkit::snip20::Balance = querier.query_wasm_smart( code_hash, address, @@ -149,7 +148,6 @@ impl CheckedDenom { /// execution to fail. pub fn get_transfer_to_message( &self, - code_hash: String, who: &Addr, amount: Uint128, ) -> StdResult { @@ -162,9 +160,9 @@ impl CheckedDenom { }], } .into(), - CheckedDenom::Cw20(address) => WasmMsg::Execute { + CheckedDenom::Cw20(address,code_hash) => WasmMsg::Execute { contract_addr: address.to_string(), - code_hash, + code_hash:code_hash.to_string(), msg: to_binary(&secret_toolkit::snip20::HandleMsg::Transfer { recipient: who.to_string(), amount, @@ -210,7 +208,7 @@ impl fmt::Display for CheckedDenom { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Native(inner) => write!(f, "{inner}"), - Self::Cw20(inner) => write!(f, "{inner}"), + Self::Cw20(inner,_) => write!(f, "{inner}"), } } } @@ -225,6 +223,8 @@ mod tests { use super::*; const CW20_ADDR: &str = "cw20"; + const CW20_CODE_HASH: &str = "CODE_HASH"; + fn token_info_mock_querier(works: bool) -> impl Fn(&WasmQuery) -> QuerierResult { move |query: &WasmQuery| -> QuerierResult { @@ -263,12 +263,12 @@ mod tests { let mut deps = mock_dependencies(); deps.querier = querier; - let unchecked = UncheckedDenom::Cw20(CW20_ADDR.to_string()); + let unchecked = UncheckedDenom::Cw20(CW20_ADDR.to_string(),CW20_CODE_HASH.to_string()); let checked = unchecked - .into_checked(deps.as_ref(), mock_env().contract.code_hash) + .into_checked(deps.as_ref()) .unwrap(); - assert_eq!(checked, CheckedDenom::Cw20(Addr::unchecked(CW20_ADDR))) + assert_eq!(checked, CheckedDenom::Cw20(Addr::unchecked(CW20_ADDR),CW20_CODE_HASH.to_string())) } #[test] @@ -279,9 +279,9 @@ mod tests { let mut deps = mock_dependencies(); deps.querier = querier; - let unchecked = UncheckedDenom::Cw20(CW20_ADDR.to_string()); + let unchecked = UncheckedDenom::Cw20(CW20_ADDR.to_string(),CW20_CODE_HASH.to_string()); let err = unchecked - .into_checked(deps.as_ref(), mock_env().contract.code_hash) + .into_checked(deps.as_ref()) .unwrap_err(); assert_eq!( err, @@ -301,9 +301,9 @@ mod tests { let mut deps = mock_dependencies(); deps.querier = querier; - let unchecked = UncheckedDenom::Cw20("HasCapitalsSoShouldNotValidate".to_string()); + let unchecked = UncheckedDenom::Cw20("HasCapitalsSoShouldNotValidate".to_string(),"HasCapitalsSoShouldNotValidate".to_string()); let err = unchecked - .into_checked(deps.as_ref(), mock_env().contract.code_hash) + .into_checked(deps.as_ref()) .unwrap_err(); assert_eq!( err, @@ -384,7 +384,7 @@ mod tests { fn test_display() { let denom = CheckedDenom::Native("hello".to_string()); assert_eq!(denom.to_string(), "hello".to_string()); - let denom = CheckedDenom::Cw20(Addr::unchecked("hello")); + let denom = CheckedDenom::Cw20(Addr::unchecked("hello"),"CODE_HASH".to_string()); assert_eq!(denom.to_string(), "hello".to_string()); } } diff --git a/packages/cw4-group-1.1.2/src/contract.rs b/packages/cw4-group-1.1.2/src/contract.rs index 0a383c6..6bac4cd 100644 --- a/packages/cw4-group-1.1.2/src/contract.rs +++ b/packages/cw4-group-1.1.2/src/contract.rs @@ -1,26 +1,33 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - attr, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, SubMsg, - Uint64, + attr, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, + SubMsg, Uint64, }; use cw4::{ Member, MemberChangedHookMsg, MemberDiff, MemberListResponse, MemberResponse, TotalWeightResponse, }; use secret_cw2::set_contract_version; +use secret_toolkit::permit::{Permit, RevokedPermits, TokenPermissions}; +use secret_toolkit::viewing_key::{ViewingKey, ViewingKeyStore}; // use secret_storage_plus::Bound; // use secret_utils::maybe_addr; use crate::error::ContractError; use crate::helpers::validate_unique_members; -use crate::msg::{ExecuteMsg, InstantiateMsg, InstantiateMsgResponse, QueryMsg}; +use crate::msg::{ + CreateViewingKeyResponse, ExecuteMsg, InstantiateMsg, InstantiateMsgResponse, QueryMsg, + QueryWithPermit, ViewingKeyError, +}; use crate::state::{MembersStore, TotalStore, ADMIN, HOOKS, MEMBERS_PRIMARY}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw4-group"; const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const PREFIX_REVOKED_PERMITS: &str = "revoked_permits"; + // Note, you can use StdResult in some functions where you do not // make use of the custom errors #[cfg_attr(not(feature = "library"), entry_point)] @@ -100,6 +107,9 @@ pub fn execute( api.addr_validate(hook.addr.as_str())?, hook.code_hash, )?), + ExecuteMsg::CreateViewingKey { entropy, .. } => try_create_key(deps, env, info, entropy), + ExecuteMsg::SetViewingKey { key, .. } => try_set_key(deps, info, key), + ExecuteMsg::RevokePermit { permit_name, .. } => revoke_permit(deps, info, permit_name), } } @@ -176,13 +186,50 @@ pub fn update_members( Ok(MemberChangedHookMsg { diffs }) } +pub fn try_create_key( + deps: DepsMut, + env: Env, + info: MessageInfo, + entropy: String, +) -> Result { + let key = ViewingKey::create( + deps.storage, + &info, + &env, + info.sender.as_str(), + entropy.as_ref(), + ); + + Ok(Response::new().set_data(to_binary(&CreateViewingKeyResponse { key })?)) +} + +pub fn try_set_key( + deps: DepsMut, + info: MessageInfo, + key: String, +) -> Result { + ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); + Ok(Response::default()) +} + +fn revoke_permit( + deps: DepsMut, + info: MessageInfo, + permit_name: String, +) -> Result { + RevokedPermits::revoke_permit( + deps.storage, + PREFIX_REVOKED_PERMITS, + info.sender.as_str(), + &permit_name, + ); + + Ok(Response::default()) +} + #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { - QueryMsg::Member { - addr, - at_height: height, - } => to_binary(&query_member(deps, addr, height)?), QueryMsg::ListMembers { start_after, limit } => { to_binary(&query_list_members(deps, start_after, limit)?) } @@ -191,9 +238,62 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } QueryMsg::Admin {} => to_binary(&ADMIN.query_admin(deps)?), QueryMsg::Hooks {} => to_binary(&HOOKS.query_hooks(deps)?), + QueryMsg::WithPermit { permit, query } => permit_queries(deps, env, permit, query), + _ => viewing_keys_queries(deps, env, msg), } } +fn permit_queries( + deps: Deps, + env: Env, + permit: Permit, + query: QueryWithPermit, +) -> Result { + // Validate permit content + let _account = secret_toolkit::permit::validate( + deps, + PREFIX_REVOKED_PERMITS, + &permit, + env.contract.address.clone().into_string(), + None, + )?; + + // Permit validated! We can now execute the query. + match query { + QueryWithPermit::Member { address, at_height } => { + if !permit.check_permission(&TokenPermissions::Balance) { + return Err(StdError::generic_err(format!( + "No permission to query balance, got permissions {:?}", + permit.params.permissions + ))); + } + + to_binary(&query_member(deps, address, at_height)?) + } + } +} + +pub fn viewing_keys_queries(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + let (addresses, key) = msg.get_validation_params(deps.api)?; + + for address in addresses { + let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); + if result.is_ok() { + return match msg { + // Base + QueryMsg::Member { + addr, at_height, .. + } => to_binary(&query_member(deps, addr, at_height)?), + _ => panic!("This query type does not require authentication"), + }; + } + } + + to_binary(&ViewingKeyError { + msg: "Wrong viewing key for this address or viewing key not set".to_string(), + }) +} + pub fn query_total_weight(deps: Deps, height: Option) -> StdResult { if height.is_some() { let weight = TotalStore::may_load_at_height(deps.storage, height.unwrap())?; diff --git a/packages/cw4-group-1.1.2/src/helpers.rs b/packages/cw4-group-1.1.2/src/helpers.rs index fc11f69..6dd724e 100644 --- a/packages/cw4-group-1.1.2/src/helpers.rs +++ b/packages/cw4-group-1.1.2/src/helpers.rs @@ -4,7 +4,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; use cw4::{Cw4Contract, Member}; -use crate::{msg::ExecuteMsg, error::ContractError}; +use crate::{error::ContractError, msg::ExecuteMsg}; /// Cw4GroupContract is a wrapper around Cw4Contract that provides a lot of helpers /// for working with cw4-group contracts. @@ -26,7 +26,7 @@ impl Cw4GroupContract { Cw4GroupContract(Cw4Contract(addr)) } - fn encode_msg(&self, msg: ExecuteMsg,code_hash: String) -> StdResult { + fn encode_msg(&self, msg: ExecuteMsg, code_hash: String) -> StdResult { Ok(WasmMsg::Execute { contract_addr: self.addr().into(), code_hash, @@ -36,9 +36,14 @@ impl Cw4GroupContract { .into()) } - pub fn update_members(&self, remove: Vec, add: Vec,code_hash: String) -> StdResult { + pub fn update_members( + &self, + remove: Vec, + add: Vec, + code_hash: String, + ) -> StdResult { let msg = ExecuteMsg::UpdateMembers { remove, add }; - self.encode_msg(msg,code_hash) + self.encode_msg(msg, code_hash) } } diff --git a/packages/cw4-group-1.1.2/src/msg.rs b/packages/cw4-group-1.1.2/src/msg.rs index 5984872..c98861b 100644 --- a/packages/cw4-group-1.1.2/src/msg.rs +++ b/packages/cw4-group-1.1.2/src/msg.rs @@ -1,6 +1,10 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Addr, Api, StdResult}; use cw4::Member; +use schemars::JsonSchema; use secret_cw_controllers::HookItem; +use secret_toolkit::permit::Permit; +use serde::{Deserialize, Serialize}; #[cw_serde] pub struct InstantiateMsg { @@ -32,6 +36,18 @@ pub enum ExecuteMsg { AddHook { hook: HookItem }, /// Remove a hook. Must be called by Admin RemoveHook { hook: HookItem }, + CreateViewingKey { + entropy: String, + padding: Option, + }, + SetViewingKey { + key: String, + padding: Option, + }, // Permit + RevokePermit { + permit_name: String, + padding: Option, + }, } #[cw_serde] @@ -49,9 +65,48 @@ pub enum QueryMsg { #[returns(cw4::MemberResponse)] Member { addr: String, + key: String, at_height: Option, }, /// Shows all registered hooks. #[returns(secret_cw_controllers::HooksResponse)] Hooks {}, + #[returns(())] + WithPermit { + permit: Permit, + query: QueryWithPermit, + }, +} + +impl QueryMsg { + pub fn get_validation_params(&self, api: &dyn Api) -> StdResult<(Vec, String)> { + match self { + Self::Member { addr, key, .. } => { + let address = api.addr_validate(addr.as_str())?; + Ok((vec![address], key.clone())) + } + _ => panic!("This query type does not require authentication"), + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, QueryResponses)] +#[cfg_attr(test, derive(Eq, PartialEq))] +#[serde(rename_all = "snake_case")] +pub enum QueryWithPermit { + #[returns(cw4::MemberResponse)] + Member { + address: String, + at_height: Option, + }, +} + +#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +pub struct ViewingKeyError { + pub msg: String, +} + +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)] +pub struct CreateViewingKeyResponse { + pub key: String, } diff --git a/packages/cw4-group-1.1.2/src/tests.rs b/packages/cw4-group-1.1.2/src/tests.rs index 137b252..cba3d13 100644 --- a/packages/cw4-group-1.1.2/src/tests.rs +++ b/packages/cw4-group-1.1.2/src/tests.rs @@ -391,8 +391,17 @@ fn hooks_fire() { MemberDiff::new(USER2, Some(6), None), ]; let hook_msg = MemberChangedHookMsg { diffs }; - let msg1 = SubMsg::new(hook_msg.clone().into_cosmos_msg(contract1,mock_env().contract.code_hash.clone()).unwrap()); - let msg2 = SubMsg::new(hook_msg.into_cosmos_msg(contract2,mock_env().contract.code_hash.clone()).unwrap()); + let msg1 = SubMsg::new( + hook_msg + .clone() + .into_cosmos_msg(contract1, mock_env().contract.code_hash.clone()) + .unwrap(), + ); + let msg2 = SubMsg::new( + hook_msg + .into_cosmos_msg(contract2, mock_env().contract.code_hash.clone()) + .unwrap(), + ); dbg!(&res.messages); dbg!(&msg1); dbg!(&msg2); diff --git a/packages/dao-dao-macros/src/lib.rs b/packages/dao-dao-macros/src/lib.rs index ae0bfe6..ede2f53 100644 --- a/packages/dao-dao-macros/src/lib.rs +++ b/packages/dao-dao-macros/src/lib.rs @@ -133,6 +133,7 @@ pub fn voting_module_query(metadata: TokenStream, input: TokenStream) -> TokenSt #[returns(#vp)] VotingPowerAtHeight { address: ::std::string::String, + key: ::std::string::String, height: ::std::option::Option<::std::primitive::u64> }, /// Returns the total voting power at a given block heigh. diff --git a/packages/dao-interface/src/msg.rs b/packages/dao-interface/src/msg.rs index 3cf3731..8698535 100644 --- a/packages/dao-interface/src/msg.rs +++ b/packages/dao-interface/src/msg.rs @@ -261,7 +261,7 @@ pub enum QueryMsg { #[returns(crate::query::PauseInfoResponse)] PauseInfo {}, /// Gets the contract's voting module. - #[returns(cosmwasm_std::Addr)] + #[returns(crate::state::AnyContractInfo)] VotingModule {}, /// Returns all SubDAOs with their charters in a vec. /// start_after is bound exclusive and asks for a string address. @@ -277,11 +277,13 @@ pub enum QueryMsg { #[returns(crate::voting::VotingPowerAtHeightResponse)] VotingPowerAtHeight { address: String, + key: String, height: Option, }, /// Returns the total voting power at a given block height. #[returns(crate::voting::TotalPowerAtHeightResponse)] TotalPowerAtHeight { height: Option }, + } #[allow(clippy::large_enum_variant)] diff --git a/packages/dao-interface/src/voting.rs b/packages/dao-interface/src/voting.rs index 150985e..b9d6bcb 100644 --- a/packages/dao-interface/src/voting.rs +++ b/packages/dao-interface/src/voting.rs @@ -9,7 +9,7 @@ use schemars::JsonSchema; #[derive(QueryResponses)] pub enum Query { /// Returns the token contract address, if set. - #[returns(::cosmwasm_std::Addr)] + #[returns(crate::state::AnyContractInfo)] TokenContract {}, /// Returns the native token denom, if used. #[returns(DenomResponse)] @@ -18,6 +18,7 @@ pub enum Query { #[returns(VotingPowerAtHeightResponse)] VotingPowerAtHeight { address: ::std::string::String, + key : ::std::string::String, height: ::std::option::Option<::std::primitive::u64>, }, /// Returns the total voting power at a given block heigh. @@ -26,7 +27,7 @@ pub enum Query { height: ::std::option::Option<::std::primitive::u64>, }, /// Returns the address of the DAO this module belongs to. - #[returns(cosmwasm_std::Addr)] + #[returns(crate::state::AnyContractInfo)] Dao {}, /// Returns contract version info. #[returns(InfoResponse)] diff --git a/packages/dao-pre-propose-base/src/execute.rs b/packages/dao-pre-propose-base/src/execute.rs index c0720e8..bc124ec 100644 --- a/packages/dao-pre-propose-base/src/execute.rs +++ b/packages/dao-pre-propose-base/src/execute.rs @@ -8,6 +8,7 @@ use secret_cw2::set_contract_version; use cw_denom::UncheckedDenom; use dao_interface::voting::{Query as CwCoreQuery, VotingPowerAtHeightResponse}; +use dao_interface::state::AnyContractInfo; use dao_voting::{ deposit::{DepositRefundPolicy, UncheckedDepositInfo}, status::Status, @@ -32,7 +33,7 @@ where pub fn instantiate( &self, deps: DepsMut, - env: Env, + _env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result { @@ -42,21 +43,21 @@ where // making limited assumptions here. The only way to associate // a deposit module with a proposal module is for the proposal // module to instantiate it. - self.proposal_module.save(deps.storage, &info.sender)?; + self.proposal_module.save(deps.storage, &AnyContractInfo { addr: info.sender.clone(), code_hash: msg.proposal_module_code_hash.clone() })?; // Query the proposal module for its DAO. - let dao: Addr = deps.querier.query_wasm_smart( - env.contract.code_hash.clone(), + let dao_info: AnyContractInfo = deps.querier.query_wasm_smart( + msg.proposal_module_code_hash.clone(), info.sender.clone(), &CwCoreQuery::Dao {}, )?; - self.dao.save(deps.storage, &dao)?; + self.dao.save(deps.storage, &dao_info)?; let deposit_info = msg .deposit_info .map(|info| { - info.into_checked(deps.as_ref(), dao.clone(), env.contract.code_hash.clone()) + info.into_checked(deps.as_ref(), dao_info.addr.clone(), dao_info.code_hash.clone()) }) .transpose()?; @@ -75,7 +76,7 @@ where "open_proposal_submission", config.open_proposal_submission.to_string(), ) - .add_attribute("dao", dao)) + .add_attribute("dao", dao_info.addr.to_string())) } pub fn execute( @@ -86,7 +87,7 @@ where msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::Propose { msg } => self.execute_propose(deps, env, info, msg), + ExecuteMsg::Propose {key, msg } => self.execute_propose(deps, env, info,key, msg), ExecuteMsg::UpdateConfig { deposit_info, open_proposal_submission, @@ -122,12 +123,13 @@ where deps: DepsMut, env: Env, info: MessageInfo, + key: String, msg: ProposalMessage, ) -> Result { self.check_can_submit( deps.as_ref(), info.sender.clone(), - env.contract.code_hash.clone(), + key, )?; let config = self.config.load(deps.storage)?; @@ -135,7 +137,6 @@ where let deposit_messages = if let Some(ref deposit_info) = config.deposit_info { deposit_info.check_native_deposit_paid(&info)?; deposit_info.get_take_deposit_messages( - env.contract.code_hash.clone(), &info.sender, &env.contract.address.clone(), )? @@ -143,13 +144,13 @@ where vec![] }; - let proposal_module = self.proposal_module.load(deps.storage)?; + let proposal_module_info = self.proposal_module.load(deps.storage)?; // Snapshot the deposit using the ID of the proposal that we // will create. let next_id = deps.querier.query_wasm_smart( - env.contract.code_hash.clone(), - &proposal_module, + proposal_module_info.code_hash.clone(), + &proposal_module_info.addr.clone().to_string(), &dao_interface::proposal::Query::NextProposalId {}, )?; self.deposits.insert( @@ -159,8 +160,8 @@ where )?; let propose_messsage = WasmMsg::Execute { - contract_addr: proposal_module.into_string(), - code_hash: env.contract.code_hash.clone(), + contract_addr: proposal_module_info.addr.into_string(), + code_hash: proposal_module_info.code_hash.clone(), msg: to_binary(&msg)?, funds: vec![], }; @@ -193,16 +194,16 @@ where &self, deps: DepsMut, info: MessageInfo, - env: Env, + _env: Env, deposit_info: Option, open_proposal_submission: bool, ) -> Result { let dao = self.dao.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr.clone() { Err(PreProposeError::NotDao {}) } else { let deposit_info = deposit_info - .map(|d| d.into_checked(deps.as_ref(), dao, env.contract.code_hash.clone())) + .map(|d| d.into_checked(deps.as_ref(), dao.addr.clone(), dao.code_hash.clone())) .transpose()?; self.config.save( deps.storage, @@ -227,11 +228,11 @@ where key: String, ) -> Result { let dao = self.dao.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr.clone() { Err(PreProposeError::NotDao {}) } else { let denom = match denom { - Some(denom) => Some(denom.into_checked(deps, env.contract.code_hash.clone())?), + Some(denom) => Some(denom.into_checked(deps)?), None => { let config = self.config.load(deps.storage)?; config.deposit_info.map(|d| d.denom) @@ -242,7 +243,6 @@ where Some(denom) => { let balance = denom.query_balance( &deps.querier, - env.contract.code_hash.clone(), &env.contract.address, key, )?; @@ -250,14 +250,13 @@ where Err(PreProposeError::NothingToWithdraw {}) } else { let withdraw_message = denom.get_transfer_to_message( - env.contract.code_hash.clone(), - &dao, + &dao.addr.clone(), balance, )?; Ok(Response::default() .add_message(withdraw_message) .add_attribute("method", "withdraw") - .add_attribute("receiver", &dao) + .add_attribute("receiver", &dao.addr.to_string()) .add_attribute("denom", denom.to_string())) } } @@ -273,7 +272,7 @@ where code_hash: String ) -> Result { let dao = self.dao.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr.clone() { return Err(PreProposeError::NotDao {}); } @@ -294,7 +293,7 @@ where code_hash: String ) -> Result { let dao = self.dao.load(deps.storage)?; - if info.sender != dao { + if info.sender != dao.addr.clone() { return Err(PreProposeError::NotDao {}); } @@ -315,12 +314,12 @@ where &self, deps: Deps, info: MessageInfo, - env: Env, + _env: Env, id: u64, new_status: Status, ) -> Result { let proposal_module = self.proposal_module.load(deps.storage)?; - if info.sender != proposal_module { + if info.sender != proposal_module.addr.clone() { return Err(PreProposeError::NotModule {}); } @@ -354,12 +353,12 @@ where if should_refund_to_proposer { deposit_info - .get_return_deposit_message(&proposer, env.contract.code_hash.clone())? + .get_return_deposit_message(&proposer)? } else { // If the proposer doesn't get the deposit, the DAO does. let dao = self.dao.load(deps.storage)?; deposit_info - .get_return_deposit_message(&dao, env.contract.code_hash.clone())? + .get_return_deposit_message(&dao.addr.clone())? } } else { // No deposit info for this proposal. Nothing to do. @@ -387,17 +386,18 @@ where &self, deps: Deps, who: Addr, - code_hash: String, + key: String ) -> Result<(), PreProposeError> { let config = self.config.load(deps.storage)?; if !config.open_proposal_submission { let dao = self.dao.load(deps.storage)?; let voting_power: VotingPowerAtHeightResponse = deps.querier.query_wasm_smart( - code_hash, - dao.into_string(), + dao.code_hash.clone(), + dao.addr.clone().into_string(), &CwCoreQuery::VotingPowerAtHeight { address: who.into_string(), + key, height: None, }, )?; diff --git a/packages/dao-pre-propose-base/src/lib.rs b/packages/dao-pre-propose-base/src/lib.rs index be00400..6ca223e 100644 --- a/packages/dao-pre-propose-base/src/lib.rs +++ b/packages/dao-pre-propose-base/src/lib.rs @@ -5,5 +5,5 @@ pub mod execute; pub mod msg; pub mod state; -#[cfg(test)] -mod tests; +// #[cfg(test)] +// mod tests; diff --git a/packages/dao-pre-propose-base/src/msg.rs b/packages/dao-pre-propose-base/src/msg.rs index 4ea4d09..6224fab 100644 --- a/packages/dao-pre-propose-base/src/msg.rs +++ b/packages/dao-pre-propose-base/src/msg.rs @@ -5,7 +5,6 @@ use dao_voting::{ status::Status, }; use serde::{Deserialize, Serialize}; - #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] #[serde(rename_all = "snake_case")] pub struct InstantiateMsg { @@ -16,6 +15,7 @@ pub struct InstantiateMsg { /// proposals in the DAO. Otherwise, any address may create a /// proposal so long as they pay the deposit. pub open_proposal_submission: bool, + pub proposal_module_code_hash: String, /// Extension for instantiation. The default implementation will /// do nothing with this data. pub extension: InstantiateExt, @@ -26,7 +26,9 @@ pub struct InstantiateMsg { pub enum ExecuteMsg { /// Creates a new proposal in the pre-propose module. MSG will be /// serialized and used as the proposal creation message. - Propose { msg: ProposalMessage }, + Propose { key: String, + msg: ProposalMessage + }, /// Updates the configuration of this module. This will completely /// override the existing configuration. This new configuration @@ -101,11 +103,11 @@ where { /// Gets the proposal module that this pre propose module is /// associated with. Returns `Addr`. - #[returns(cosmwasm_std::Addr)] + #[returns(dao_interface::state::AnyContractInfo)] ProposalModule {}, /// Gets the DAO (dao-dao-core) module this contract is associated /// with. Returns `Addr`. - #[returns(cosmwasm_std::Addr)] + #[returns(dao_interface::state::AnyContractInfo)] Dao {}, /// Gets the module's configuration. #[returns(crate::state::Config)] diff --git a/packages/dao-pre-propose-base/src/state.rs b/packages/dao-pre-propose-base/src/state.rs index d62980b..34e652a 100644 --- a/packages/dao-pre-propose-base/src/state.rs +++ b/packages/dao-pre-propose-base/src/state.rs @@ -2,6 +2,7 @@ use std::marker::PhantomData; use cosmwasm_std::Addr; use cw_hooks::Hooks; +use dao_interface::state::AnyContractInfo; use schemars::JsonSchema; use secret_storage_plus::Item; use secret_toolkit::{serialization::Json, storage::Keymap}; @@ -23,10 +24,10 @@ pub struct Config { pub struct PreProposeContract { /// The proposal module that this module is associated with. - pub proposal_module: Item<'static, Addr>, + pub proposal_module: Item<'static, AnyContractInfo>, /// The DAO (dao-dao-core module) that this module is associated /// with. - pub dao: Item<'static, Addr>, + pub dao: Item<'static, AnyContractInfo>, /// The configuration for this module. pub config: Item<'static, Config>, /// Map between proposal IDs and (deposit, proposer) pairs. diff --git a/packages/dao-snip721-extensions/Cargo.toml b/packages/dao-snip721-extensions/Cargo.toml index fe3ca57..6d06ec6 100644 --- a/packages/dao-snip721-extensions/Cargo.toml +++ b/packages/dao-snip721-extensions/Cargo.toml @@ -12,3 +12,5 @@ cosmwasm-std = { workspace = true } cosmwasm-schema = { workspace = true } secret-cw-controllers = { workspace = true } cw4 = { workspace = true } +dao-interface = { workspace = true } +secret-toolkit ={ workspace = true } \ No newline at end of file diff --git a/packages/dao-snip721-extensions/src/roles.rs b/packages/dao-snip721-extensions/src/roles.rs index 61f68e0..5d799ed 100644 --- a/packages/dao-snip721-extensions/src/roles.rs +++ b/packages/dao-snip721-extensions/src/roles.rs @@ -1,5 +1,6 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::CustomMsg; +use cosmwasm_std::{Addr, Api, CustomMsg, StdResult}; +use secret_toolkit::permit::Permit; #[cw_serde] pub struct MetadataExt { @@ -28,6 +29,19 @@ pub enum ExecuteExt { token_id: String, role: Option, }, + CreateViewingKey { + entropy: String, + padding: Option, + }, + SetViewingKey { + key: String, + padding: Option, + }, + // Permit + RevokePermit { + permit_name: String, + padding: Option, + }, } impl CustomMsg for ExecuteExt {} @@ -47,10 +61,49 @@ pub enum QueryExt { #[returns(cw4::MemberResponse)] Member { addr: String, + key: String, at_height: Option, }, /// Shows all registered hooks. #[returns(secret_cw_controllers::HooksResponse)] Hooks {}, + #[returns(())] + WithPermit { + permit: Permit, + query: QueryWithPermit, + }, + +} + +impl QueryExt { + pub fn get_validation_params(&self, api: &dyn Api) -> StdResult<(Vec, String)> { + match self { + Self::Member { addr, key, .. } => { + let address = api.addr_validate(addr.as_str())?; + Ok((vec![address], key.clone())) + } + _ => panic!("This query type does not require authentication"), + } + } +} + +#[cw_serde] +pub struct CreateViewingKey { + pub key: String, } + +#[cw_serde] +pub struct ViewingKeyError { + pub msg: String, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryWithPermit { + #[returns(dao_interface::voting::VotingPowerAtHeightResponse)] + Member { addr: String, + at_height: Option,}, +} + + impl CustomMsg for QueryExt {} diff --git a/packages/dao-voting/src/deposit.rs b/packages/dao-voting/src/deposit.rs index d235953..f883f23 100644 --- a/packages/dao-voting/src/deposit.rs +++ b/packages/dao-voting/src/deposit.rs @@ -4,7 +4,7 @@ use cosmwasm_std::{ use schemars::JsonSchema; use secret_utils::{must_pay, PaymentError}; -use dao_interface::voting::DenomResponse; +use dao_interface::{state::AnyContractInfo, voting::DenomResponse}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -123,9 +123,9 @@ impl UncheckedDepositInfo { } let denom = match denom { - DepositToken::Token { denom } => denom.into_checked(deps, code_hash.clone()), + DepositToken::Token { denom } => denom.into_checked(deps), DepositToken::VotingModuleToken { token_type } => { - let voting_module: Addr = deps.querier.query_wasm_smart( + let voting_module: AnyContractInfo = deps.querier.query_wasm_smart( code_hash.clone(), dao, &dao_interface::msg::QueryMsg::VotingModule {}, @@ -136,20 +136,20 @@ impl UncheckedDepositInfo { // If the voting module has no native token denom this // will error. This is desirable. let denom: DenomResponse = deps.querier.query_wasm_smart( - code_hash.clone(), - voting_module, + voting_module.code_hash.clone(), + voting_module.addr.to_string().clone(), &dao_interface::voting::Query::Denom {}, )?; // Validate that native denom is formatted correctly. - UncheckedDenom::Native(denom.denom).into_checked(deps, code_hash.clone()) + UncheckedDenom::Native(denom.denom).into_checked(deps) } VotingModuleTokenType::Cw20 => { // If the voting module has no cw20 token this will // error. This is desirable. - let token_addr: Addr = deps.querier.query_wasm_smart( - code_hash.clone(), - voting_module, + let token: AnyContractInfo = deps.querier.query_wasm_smart( + voting_module.code_hash.clone(), + voting_module.addr.to_string().clone(), &dao_interface::voting::Query::TokenContract {}, )?; @@ -157,7 +157,7 @@ impl UncheckedDepositInfo { // returned a valid token. Conversion of the unchecked // denom into a checked one will do a `TokenInfo {}` // query. - UncheckedDenom::Cw20(token_addr.into_string()).into_checked(deps, code_hash) + UncheckedDenom::Cw20(token.addr.into_string(),token.code_hash).into_checked(deps) } } } @@ -200,13 +200,12 @@ impl CheckedDepositInfo { pub fn get_take_deposit_messages( &self, - code_hash: String, depositor: &Addr, contract: &Addr, ) -> StdResult> { let take_deposit_msg: Vec = if let Self { amount, - denom: CheckedDenom::Cw20(address), + denom: CheckedDenom::Cw20(address,token_code_hash), .. } = self { @@ -225,7 +224,7 @@ impl CheckedDepositInfo { memo: None, padding: None, })?, - code_hash, + code_hash:token_code_hash.clone(), } .into()] } @@ -240,7 +239,6 @@ impl CheckedDepositInfo { pub fn get_return_deposit_message( &self, depositor: &Addr, - code_hash: String, ) -> StdResult> { // Should get caught in `into_checked()`, but to be pedantic. if self.amount.is_zero() { @@ -248,7 +246,7 @@ impl CheckedDepositInfo { } let message = self .denom - .get_transfer_to_message(code_hash, depositor, self.amount)?; + .get_transfer_to_message(depositor, self.amount)?; Ok(vec![message]) } } @@ -281,7 +279,7 @@ pub mod tests { // Doesn't matter what we submit if it's a cw20 token. info.funds = vec![]; - deposit_info.denom = CheckedDenom::Cw20(Addr::unchecked(CW20)); + deposit_info.denom = CheckedDenom::Cw20(Addr::unchecked(CW20),"code_hash".to_string()); deposit_info.check_native_deposit_paid(&info).unwrap(); info.funds = coins(100, NATIVE_DENOM); @@ -361,7 +359,6 @@ pub mod tests { }; let messages = deposit_info .get_take_deposit_messages( - mock_env().contract.code_hash, &Addr::unchecked("ekez"), &Addr::unchecked(CW20), ) @@ -369,10 +366,9 @@ pub mod tests { assert_eq!(messages, vec![]); // Does something for cw20s. - deposit_info.denom = CheckedDenom::Cw20(Addr::unchecked(CW20)); + deposit_info.denom = CheckedDenom::Cw20(Addr::unchecked(CW20),"code_hash".to_string()); let messages = deposit_info .get_take_deposit_messages( - mock_env().contract.code_hash, &Addr::unchecked("ekez"), &Addr::unchecked("contract"), ) @@ -397,7 +393,6 @@ pub mod tests { deposit_info.amount = Uint128::zero(); let messages = deposit_info .get_take_deposit_messages( - mock_env().contract.code_hash, &Addr::unchecked("ekez"), &Addr::unchecked(CW20), ) @@ -413,7 +408,7 @@ pub mod tests { refund_policy: DepositRefundPolicy::Always, }; let messages = deposit_info - .get_return_deposit_message(&Addr::unchecked("ekez"), mock_env().contract.code_hash) + .get_return_deposit_message(&Addr::unchecked("ekez")) .unwrap(); assert_eq!( messages, @@ -426,7 +421,7 @@ pub mod tests { // Don't fire a message if there is nothing to send! deposit_info.amount = Uint128::zero(); let messages = deposit_info - .get_return_deposit_message(&Addr::unchecked("ekez"), mock_env().contract.code_hash) + .get_return_deposit_message(&Addr::unchecked("ekez")) .unwrap(); assert_eq!(messages, vec![]); } @@ -434,12 +429,12 @@ pub mod tests { #[test] fn test_get_return_deposit_message_cw20() { let mut deposit_info = CheckedDepositInfo { - denom: CheckedDenom::Cw20(Addr::unchecked(CW20)), + denom: CheckedDenom::Cw20(Addr::unchecked(CW20),"code_hash".to_string()), amount: Uint128::new(10), refund_policy: DepositRefundPolicy::Always, }; let messages = deposit_info - .get_return_deposit_message(&Addr::unchecked("ekez"), mock_env().contract.code_hash) + .get_return_deposit_message(&Addr::unchecked("ekez")) .unwrap(); assert_eq!( messages, @@ -458,7 +453,7 @@ pub mod tests { // Don't fire a message if there is nothing to send! deposit_info.amount = Uint128::zero(); let messages = deposit_info - .get_return_deposit_message(&Addr::unchecked("ekez"), mock_env().contract.code_hash) + .get_return_deposit_message(&Addr::unchecked("ekez")) .unwrap(); assert_eq!(messages, vec![]); } diff --git a/packages/dao-voting/src/voting.rs b/packages/dao-voting/src/voting.rs index 1fb8cd6..e81b10e 100644 --- a/packages/dao-voting/src/voting.rs +++ b/packages/dao-voting/src/voting.rs @@ -197,6 +197,7 @@ pub fn get_voting_power( deps: Deps, code_hash: String, address: Addr, + key: String, dao: &Addr, height: Option, ) -> StdResult { @@ -206,6 +207,7 @@ pub fn get_voting_power( &voting::Query::VotingPowerAtHeight { address: address.into_string(), height, + key, }, )?; Ok(response.power) diff --git a/snip20-reference-impl/Cargo.lock b/snip20-reference-impl/Cargo.lock deleted file mode 100644 index 8e3ff6b..0000000 --- a/snip20-reference-impl/Cargo.lock +++ /dev/null @@ -1,872 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bincode2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49f6183038e081170ebbbadee6678966c7d54728938a3e7de7f4e780770318f" -dependencies = [ - "byteorder", - "serde", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "const-oid" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" - -[[package]] -name = "cosmwasm-crypto" -version = "1.1.9" -source = "git+https://github.com/scrtlabs/cosmwasm/?tag=v1.1.9-secret#e40a15f04dae80680dbe22aef760e5eaab6b0a19" -dependencies = [ - "digest 0.10.6", - "ed25519-zebra", - "k256", - "rand_core 0.6.4", - "thiserror", -] - -[[package]] -name = "cosmwasm-derive" -version = "1.1.9" -source = "git+https://github.com/scrtlabs/cosmwasm/?tag=v1.1.9-secret#e40a15f04dae80680dbe22aef760e5eaab6b0a19" -dependencies = [ - "syn 1.0.109", -] - -[[package]] -name = "cosmwasm-schema" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1b99f612ccf162940ae2eef9f370ee37cf2ddcf4a9a8f5ee15ec6b46a5ecd2e" -dependencies = [ - "cosmwasm-schema-derive", - "schemars", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cosmwasm-schema-derive" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92ceea61033cb69c336abf673da017ddf251fc4e26e0cdd387eaf8bedb14e49" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cosmwasm-std" -version = "1.1.9" -source = "git+https://github.com/scrtlabs/cosmwasm/?tag=v1.1.9-secret#e40a15f04dae80680dbe22aef760e5eaab6b0a19" -dependencies = [ - "base64 0.13.1", - "cosmwasm-crypto", - "cosmwasm-derive", - "derivative", - "forward_ref", - "hex", - "schemars", - "serde", - "serde-json-wasm", - "thiserror", - "uint", -] - -[[package]] -name = "cosmwasm-storage" -version = "1.1.9" -source = "git+https://github.com/scrtlabs/cosmwasm/?tag=v1.1.9-secret#e40a15f04dae80680dbe22aef760e5eaab6b0a19" -dependencies = [ - "cosmwasm-std", - "serde", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle", -] - -[[package]] -name = "dyn-clone" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek", - "hashbrown", - "hex", - "rand_core 0.6.4", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.6", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2 0.10.6", -] - -[[package]] -name = "libc" -version = "0.2.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "remain" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4b7d9b4676922ecbbad6d317e0f847762c4b28b935a2db3b44bd4f36c1aa7f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "schemars" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.109", -] - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "secret-toolkit" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "secret-toolkit-crypto", - "secret-toolkit-permit", - "secret-toolkit-serialization", - "secret-toolkit-snip20", - "secret-toolkit-snip721", - "secret-toolkit-storage", - "secret-toolkit-utils", - "secret-toolkit-viewing-key", -] - -[[package]] -name = "secret-toolkit-crypto" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "cosmwasm-std", - "rand_chacha", - "rand_core 0.6.4", - "sha2 0.10.6", -] - -[[package]] -name = "secret-toolkit-permit" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "bech32", - "cosmwasm-std", - "remain", - "ripemd", - "schemars", - "secret-toolkit-crypto", - "serde", -] - -[[package]] -name = "secret-toolkit-serialization" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "bincode2", - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "secret-toolkit-snip20" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "cosmwasm-std", - "schemars", - "secret-toolkit-utils", - "serde", -] - -[[package]] -name = "secret-toolkit-snip721" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "cosmwasm-std", - "schemars", - "secret-toolkit-utils", - "serde", -] - -[[package]] -name = "secret-toolkit-storage" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "cosmwasm-std", - "cosmwasm-storage", - "secret-toolkit-serialization", - "serde", -] - -[[package]] -name = "secret-toolkit-utils" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "cosmwasm-std", - "cosmwasm-storage", - "schemars", - "serde", -] - -[[package]] -name = "secret-toolkit-viewing-key" -version = "0.8.0" -source = "git+https://github.com/scrtlabs/secret-toolkit?rev=9b74bdac71c2fedcc12246f18cdfdd94b8991282#9b74bdac71c2fedcc12246f18cdfdd94b8991282" -dependencies = [ - "base64 0.21.0", - "cosmwasm-std", - "cosmwasm-storage", - "schemars", - "secret-toolkit-crypto", - "secret-toolkit-utils", - "serde", - "subtle", -] - -[[package]] -name = "serde" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.4", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "serde_json" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.6", - "rand_core 0.6.4", -] - -[[package]] -name = "snip20-reference-impl" -version = "1.0.0" -dependencies = [ - "base64 0.21.0", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "rand", - "schemars", - "secret-toolkit", - "secret-toolkit-crypto", - "serde", -] - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "zeroize" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/snip20-reference-impl/tests/example-receiver/Cargo.lock b/snip20-reference-impl/tests/example-receiver/Cargo.lock deleted file mode 100644 index 1f783aa..0000000 --- a/snip20-reference-impl/tests/example-receiver/Cargo.lock +++ /dev/null @@ -1,649 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64ct" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - -[[package]] -name = "cosmwasm-derive" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8568b289cc366981319ab39edd85d666456456f7c126433ef065ebe5257f27b2" -dependencies = [ - "syn", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" -dependencies = [ - "byteorder", - "digest", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "dyn-clone" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" - -[[package]] -name = "ecdsa" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek", - "hashbrown", - "hex", - "rand_core 0.6.4", - "serde", - "sha2", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff", - "generic-array", - "group", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "ff" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "group" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest", -] - -[[package]] -name = "itoa" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" - -[[package]] -name = "k256" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2", -] - -[[package]] -name = "libc" -version = "0.2.137" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" - -[[package]] -name = "once_cell" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der", - "spki", - "zeroize", -] - -[[package]] -name = "proc-macro2" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "receiver-contract" -version = "0.1.0" -dependencies = [ - "schemars", - "secret-cosmwasm-std", - "secret-cosmwasm-storage", - "serde", - "snafu", -] - -[[package]] -name = "rfc6979" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" -dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", -] - -[[package]] -name = "ryu" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" - -[[package]] -name = "schemars" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn", -] - -[[package]] -name = "sec1" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" -dependencies = [ - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "secret-cosmwasm-crypto" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19980a28b539adc47dc8b6af4bf6ec264d0441daf3852140cce5b27a81e95a0d" -dependencies = [ - "digest", - "ed25519-zebra", - "k256", - "rand_core 0.6.4", - "thiserror", -] - -[[package]] -name = "secret-cosmwasm-std" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0cf71b9340ef6a67d0e2078a1ca5cda31816aa5028f90ee79864ac231a1fcb6" -dependencies = [ - "base64", - "cosmwasm-derive", - "forward_ref", - "schemars", - "secret-cosmwasm-crypto", - "serde", - "serde-json-wasm", - "thiserror", - "uint", -] - -[[package]] -name = "secret-cosmwasm-storage" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf5f2977eabf4217c7ad618c36c5d0dc853c606b6ea26ab07760588d4fbeb0b" -dependencies = [ - "secret-cosmwasm-std", - "serde", -] - -[[package]] -name = "serde" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer", - "cfg-if", - "cpufeatures", - "digest", - "opaque-debug", -] - -[[package]] -name = "signature" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "snafu" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ba99b054b22972ee794cf04e5ef572da1229e33b65f3c57abbff0525a454" -dependencies = [ - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5e79cdebbabaebb06a9bdbaedc7f159b410461f63611d4d0e3fb0fab8fed850" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "uint" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-ident" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "zeroize" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" diff --git a/snip721-reference-impl/Cargo.lock b/snip721-reference-impl/Cargo.lock deleted file mode 100644 index 4e19f64..0000000 --- a/snip721-reference-impl/Cargo.lock +++ /dev/null @@ -1,897 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bincode2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49f6183038e081170ebbbadee6678966c7d54728938a3e7de7f4e780770318f" -dependencies = [ - "byteorder", - "serde", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "cosmwasm-derive" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e199424486ea97d6b211db6387fd72e26b4a439d40cc23140b2d8305728055b" -dependencies = [ - "syn 1.0.109", -] - -[[package]] -name = "cosmwasm-schema" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef683a9c1c4eabd6d31515719d0d2cc66952c4c87f7eb192bfc90384517dc34" -dependencies = [ - "cosmwasm-schema-derive", - "schemars", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cosmwasm-schema-derive" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9567025acbb4c0c008178393eb53b3ac3c2e492c25949d3bf415b9cbe80772d8" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cpufeatures" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle", -] - -[[package]] -name = "dyn-clone" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" - -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek", - "hashbrown", - "hex", - "rand_core 0.6.4", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "digest 0.10.7", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "static_assertions", -] - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2 0.10.8", -] - -[[package]] -name = "libc" -version = "0.2.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "primitive-types" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" -dependencies = [ - "fixed-hash", - "uint", -] - -[[package]] -name = "proc-macro2" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "remain" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce3a7139d2ee67d07538ee5dba997364fbc243e7e7143e96eb830c74bfaa082" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint", - "hmac", - "zeroize", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "schemars" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.109", -] - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "secp256k1" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" -dependencies = [ - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" -dependencies = [ - "cc", -] - -[[package]] -name = "secret-cosmwasm-crypto" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8535d61c88d0a6c222df2cebb69859d8e9ba419a299a1bc84c904b0d9c00c7b2" -dependencies = [ - "digest 0.10.7", - "ed25519-zebra", - "k256", - "rand_core 0.6.4", - "thiserror", -] - -[[package]] -name = "secret-cosmwasm-std" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4393b01aa6587007161a6bb193859deaa8165ab06c8a35f253d329ff99e4d" -dependencies = [ - "base64 0.13.1", - "cosmwasm-derive", - "derivative", - "forward_ref", - "hex", - "schemars", - "secret-cosmwasm-crypto", - "serde", - "serde-json-wasm", - "thiserror", - "uint", -] - -[[package]] -name = "secret-cosmwasm-storage" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb43da2cb72a53b16ea1555bca794fb828b48ab24ebeb45f8e26f1881c45a783" -dependencies = [ - "secret-cosmwasm-std", - "serde", -] - -[[package]] -name = "secret-toolkit" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338c972c0a98de51ccbb859312eb7672bc64b9050b086f058748ba26a509edbb" -dependencies = [ - "secret-toolkit-crypto", - "secret-toolkit-permit", - "secret-toolkit-serialization", - "secret-toolkit-storage", - "secret-toolkit-utils", - "secret-toolkit-viewing-key", -] - -[[package]] -name = "secret-toolkit-crypto" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003d7d5772c67f2240b7f298f96eb73a8a501916fe18c1d730ebfd591bf7e519" -dependencies = [ - "rand_chacha", - "rand_core 0.6.4", - "secp256k1", - "secret-cosmwasm-std", - "sha2 0.10.8", -] - -[[package]] -name = "secret-toolkit-permit" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4330571400b5959450fa37040609e6804a147d83f606783506bc2275f1527712" -dependencies = [ - "bech32", - "remain", - "ripemd", - "schemars", - "secret-cosmwasm-std", - "secret-toolkit-crypto", - "serde", -] - -[[package]] -name = "secret-toolkit-serialization" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "890adaeaa710f9f7068a807eb1553edc8c30ce9907290895c9097dd642fc613b" -dependencies = [ - "bincode2", - "schemars", - "secret-cosmwasm-std", - "serde", -] - -[[package]] -name = "secret-toolkit-storage" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e8c5418af3e7ae1d1331c383b32d56c74a340dbc3b972d53555a768698f2a3" -dependencies = [ - "secret-cosmwasm-std", - "secret-cosmwasm-storage", - "secret-toolkit-serialization", - "serde", -] - -[[package]] -name = "secret-toolkit-utils" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f1cba2e70fd701e3dfc6072807c02eeeb9776bee49e346a9c7745d84ff40c8" -dependencies = [ - "schemars", - "secret-cosmwasm-std", - "secret-cosmwasm-storage", - "serde", -] - -[[package]] -name = "secret-toolkit-viewing-key" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d89a0b69fa9b12735a612fa30e6e7e48130943982f1783b7ddd5c46ed09e921" -dependencies = [ - "base64 0.21.5", - "schemars", - "secret-cosmwasm-std", - "secret-cosmwasm-storage", - "secret-toolkit-crypto", - "secret-toolkit-utils", - "serde", - "subtle", -] - -[[package]] -name = "serde" -version = "1.0.190" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.190" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "serde_json" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "snip721-reference-impl" -version = "1.0.0" -dependencies = [ - "base64 0.21.5", - "bincode2", - "cosmwasm-schema", - "primitive-types", - "schemars", - "secret-cosmwasm-std", - "secret-cosmwasm-storage", - "secret-toolkit", - "serde", -] - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.38", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"