From e11d97be9f54df40881dec97efc2bcf0afe377fd Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Wed, 4 Dec 2024 16:43:53 +0000 Subject: [PATCH 01/10] feat: added Pow Cw721 ADO --- Cargo.lock | 20 ++ .../andromeda-pow-cw721/.cargo/config | 4 + .../andromeda-pow-cw721/Cargo.toml | 48 +++++ .../andromeda-pow-cw721/examples/schema.rs | 10 + .../andromeda-pow-cw721/src/contract.rs | 107 +++++++++++ .../andromeda-pow-cw721/src/execute.rs | 146 +++++++++++++++ .../andromeda-pow-cw721/src/interface.rs | 6 + .../andromeda-pow-cw721/src/lib.rs | 12 ++ .../andromeda-pow-cw721/src/query.rs | 25 +++ .../andromeda-pow-cw721/src/state.rs | 6 + .../andromeda-pow-cw721/src/testing/mock.rs | 84 +++++++++ .../src/testing/mock_querier.rs | 99 ++++++++++ .../andromeda-pow-cw721/src/testing/mod.rs | 3 + .../andromeda-pow-cw721/src/testing/tests.rs | 175 ++++++++++++++++++ .../andromeda-non-fungible-tokens/src/lib.rs | 1 + .../src/pow_cw721.rs | 56 ++++++ 16 files changed, 802 insertions(+) create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/.cargo/config create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/examples/schema.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/contract.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/interface.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/lib.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/query.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/state.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock_querier.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mod.rs create mode 100644 contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs create mode 100644 packages/andromeda-non-fungible-tokens/src/pow_cw721.rs diff --git a/Cargo.lock b/Cargo.lock index 76b075014..9e1be7924 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -728,6 +728,26 @@ dependencies = [ "cw-utils 1.0.3", ] +[[package]] +name = "andromeda-pow-cw721" +version = "0.1.0-beta" +dependencies = [ + "andromeda-app", + "andromeda-non-fungible-tokens", + "andromeda-std", + "andromeda-testing", + "cosmwasm-schema 1.5.8", + "cosmwasm-std 1.5.8", + "cw-multi-test", + "cw-orch", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "cw721 0.18.0", + "cw721-base 0.18.0", + "sha2 0.10.8", + "test-case", +] + [[package]] name = "andromeda-primitive" version = "2.0.4" diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/.cargo/config b/contracts/non-fungible-tokens/andromeda-pow-cw721/.cargo/config new file mode 100644 index 000000000..336b618a1 --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --example schema" diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml b/contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml new file mode 100644 index 000000000..ce9247980 --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "andromeda-pow-cw721" +version = "0.1.0-beta" +authors = ["Mitar Djakovic "] +edition = "2021" +rust-version = "1.75.0" + +exclude = [ + # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. + "contract.wasm", + "hash.txt", +] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] +testing = ["cw-multi-test", "andromeda-testing"] + + +[dependencies] +cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true } +cw-storage-plus = { workspace = true } +cw-utils = { workspace = true } +cw721 = { workspace = true } +cw721-base = { workspace = true } +sha2 = { version = "0.10.8" } +test-case = { workspace = true } +# hex = { version = "0.4.3" } + +andromeda-std = { workspace = true } +andromeda-non-fungible-tokens = { workspace = true } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +cw-multi-test = { workspace = true, optional = true } +andromeda-testing = { workspace = true, optional = true } +cw-orch = { workspace = true } +# cw-orch-daemon = "0.24.2" + +[dev-dependencies] +andromeda-app = { workspace = true } diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/examples/schema.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/examples/schema.rs new file mode 100644 index 000000000..2c2e470fc --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/examples/schema.rs @@ -0,0 +1,10 @@ +use andromeda_non_fungible_tokens::pow_cw721::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use cosmwasm_schema::write_api; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg + } +} diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/contract.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/contract.rs new file mode 100644 index 000000000..e57c2d07b --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/contract.rs @@ -0,0 +1,107 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError}; + +use andromeda_non_fungible_tokens::pow_cw721::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use andromeda_std::{ + ado_base::{ + permissioning::{LocalPermission, Permission}, + InstantiateMsg as BaseInstantiateMsg, MigrateMsg, + }, + ado_contract::ADOContract, + common::{context::ExecuteContext, encode_binary}, + error::ContractError, +}; + +use crate::execute::handle_execute; +use crate::query::{query_linked_cw721_address, query_pow_nft}; +use crate::state::LINKED_CW721_ADDRESS; + +const CONTRACT_NAME: &str = "crates.io:andromeda-pow-cw721"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +pub const MINT_POW_NFT_ACTION: &str = "MINT_POW_NFT"; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + let inst_resp = ADOContract::default().instantiate( + deps.storage, + env.clone(), + deps.api, + &deps.querier, + info, + BaseInstantiateMsg { + ado_type: CONTRACT_NAME.to_string(), + ado_version: CONTRACT_VERSION.to_string(), + kernel_address: msg.kernel_address, + owner: msg.owner, + }, + )?; + + LINKED_CW721_ADDRESS.save(deps.storage, &msg.linked_cw721_address)?; + + // Set mint PoW NFT action permission + if let Some(authorized_origin_minter_addresses) = msg.authorized_origin_minter_addresses { + if !authorized_origin_minter_addresses.is_empty() { + ADOContract::default().permission_action(MINT_POW_NFT_ACTION, deps.storage)?; + } + + for origin_minter_address in authorized_origin_minter_addresses { + let addr = origin_minter_address.get_raw_address(&deps.as_ref())?; + ADOContract::set_permission( + deps.storage, + MINT_POW_NFT_ACTION, + addr, + Permission::Local(LocalPermission::Whitelisted(None)), + )?; + } + } + + Ok(inst_resp) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + let ctx = ExecuteContext::new(deps, info, env); + match msg { + ExecuteMsg::AMPReceive(pkt) => { + ADOContract::default().execute_amp_receive(ctx, pkt, handle_execute) + } + _ => handle_execute(ctx, msg), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { + match msg { + QueryMsg::GetPowNFT { token_id } => encode_binary(&query_pow_nft(deps, token_id)?), + QueryMsg::GetLinkedCw721Address {} => encode_binary(&query_linked_cw721_address(deps)?), + _ => ADOContract::default().query(deps, env, msg), + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> Result { + if msg.result.is_err() { + return Err(ContractError::Std(StdError::generic_err( + msg.result.unwrap_err(), + ))); + } + + Ok(Response::default()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { + ADOContract::default().migrate(deps, CONTRACT_NAME, CONTRACT_VERSION) +} diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs new file mode 100644 index 000000000..4df675280 --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs @@ -0,0 +1,146 @@ +use andromeda_non_fungible_tokens::{ + cw721::{ExecuteMsg as AndrCw721ExecuteMsg, TokenExtension}, + pow_cw721::{ExecuteMsg, PowNFTInfo}, +}; +use andromeda_std::{ + ado_contract::ADOContract, + amp::AndrAddr, + common::{actions::call_action, context::ExecuteContext, encode_binary}, + error::ContractError, +}; +use cosmwasm_std::{Binary, CosmosMsg, Response, WasmMsg}; +use sha2::{Digest, Sha256}; + +use crate::contract::MINT_POW_NFT_ACTION; +use crate::state::{LINKED_CW721_ADDRESS, POW_NFT}; + +pub fn handle_execute(mut ctx: ExecuteContext, msg: ExecuteMsg) -> Result { + let action_response = call_action( + &mut ctx.deps, + &ctx.info, + &ctx.env, + &ctx.amp_ctx, + msg.as_ref(), + )?; + + let res = match msg { + ExecuteMsg::MintPowNFT { + owner, + token_id, + token_uri, + extension, + base_difficulty, + } => execute_mint_pow_nft(ctx, owner, token_id, token_uri, extension, base_difficulty), + ExecuteMsg::SubmitProof { token_id, nonce } => execute_submit_proof(ctx, token_id, nonce), + _ => ADOContract::default().execute(ctx, msg), + }?; + + Ok(res + .add_submessages(action_response.messages) + .add_attributes(action_response.attributes) + .add_events(action_response.events)) +} + +fn execute_mint_pow_nft( + mut ctx: ExecuteContext, + owner: AndrAddr, + token_id: String, + token_uri: Option, + extension: TokenExtension, + base_difficulty: u64, +) -> Result { + let sender = ctx.info.sender; + + ADOContract::default().is_permissioned( + ctx.deps.branch(), + ctx.env.clone(), + MINT_POW_NFT_ACTION, + sender.clone(), + )?; + + let owner_addr = owner.get_raw_address(&ctx.deps.as_ref())?; + let cw721_address = LINKED_CW721_ADDRESS.load(ctx.deps.storage)?; + + let addr = cw721_address + .get_raw_address(&ctx.deps.as_ref())? + .to_string(); + let mint_msg = CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: addr, + msg: encode_binary(&AndrCw721ExecuteMsg::Mint { + token_id: token_id.clone(), + owner: owner_addr.to_string(), + token_uri, + extension, + })?, + funds: vec![], + }); + + let block_height = ctx.env.block.height; + + let mut hasher = Sha256::new(); + hasher.update(block_height.to_be_bytes()); + let last_hash = hasher.finalize().to_vec(); + + let pow_nft_info = PowNFTInfo { + owner: owner_addr, + level: 1_u64, + last_hash: Binary(last_hash), + difficulty: base_difficulty, + }; + + POW_NFT.save(ctx.deps.storage, token_id, &pow_nft_info)?; + + Ok(Response::new() + .add_message(mint_msg) + .add_attribute("method", "mint_pow_nft") + .add_attribute("sender", sender)) +} + +fn execute_submit_proof( + ctx: ExecuteContext, + token_id: String, + nonce: u128, +) -> Result { + let sender = ctx.info.sender; + let mut pow_nft = POW_NFT + .load(ctx.deps.storage, token_id.clone()) + .map_err(|_| ContractError::NFTNotFound {})?; + + let mut hasher = Sha256::new(); + hasher.update(&pow_nft.last_hash); + hasher.update(&nonce.to_be_bytes()); + let hash = hasher.finalize(); + println!("Hash: {:?}", Binary(hash.to_vec())); + println!("Last Hash: {:?}", pow_nft.last_hash); + + let hash_value = u128::from_be_bytes(hash[0..16].try_into().unwrap()); + let threshold = u128::MAX >> (pow_nft.difficulty as u32); + println!("Difficulty: {:?}", pow_nft.difficulty); + println!("Threshold: {:?}", threshold); + println!("Hash Value: {:?}", hash_value); + + if hash_value > threshold { + return Err(ContractError::CustomError { + msg: "Proof does not meet difficulty".to_string(), + }); + } + + pow_nft.level += 1; + pow_nft.difficulty = (pow_nft.difficulty as f64 * 1.5) as u64; + + let block_height = ctx.env.block.height; + + let mut hasher = Sha256::new(); + hasher.update(&pow_nft.last_hash); + hasher.update(&nonce.to_be_bytes()); + hasher.update(&block_height.to_be_bytes()); + let hash = hasher.finalize(); + pow_nft.last_hash = Binary(hash.to_vec()); + println!("Last Hash After Level Up: {:?}", pow_nft.last_hash); + + POW_NFT.save(ctx.deps.storage, token_id, &pow_nft)?; + + Ok(Response::new() + .add_attribute("method", "submit_proof") + .add_attribute("sender", sender)) +} diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/interface.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/interface.rs new file mode 100644 index 000000000..0f24711ca --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/interface.rs @@ -0,0 +1,6 @@ +use andromeda_non_fungible_tokens::pow_cw721::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use andromeda_std::{ado_base::MigrateMsg, contract_interface, deploy::ADOMetadata}; + +pub const CONTRACT_ID: &str = "pow-cw721"; + +contract_interface!(PowCw721Contract, CONTRACT_ID, "andromeda_pow_cw721.wasm"); diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/lib.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/lib.rs new file mode 100644 index 000000000..b123a2029 --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/lib.rs @@ -0,0 +1,12 @@ +pub mod contract; +pub mod execute; +pub mod query; +pub mod state; + +#[cfg(test)] +pub mod testing; + +#[cfg(not(target_arch = "wasm32"))] +mod interface; +#[cfg(not(target_arch = "wasm32"))] +pub use crate::interface::PowCw721Contract; diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/query.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/query.rs new file mode 100644 index 000000000..f1f26702b --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/query.rs @@ -0,0 +1,25 @@ +use andromeda_non_fungible_tokens::pow_cw721::{GetLinkedCw721AddressResponse, GetPowNFTResponse}; +use andromeda_std::error::ContractError; +use cosmwasm_std::Deps; + +use crate::state::{LINKED_CW721_ADDRESS, POW_NFT}; + +pub fn query_pow_nft(deps: Deps, token_id: String) -> Result { + let pow_nft = POW_NFT + .load(deps.storage, token_id) + .map_err(|_| ContractError::NFTNotFound {})?; + + Ok(GetPowNFTResponse { + nft_response: pow_nft, + }) +} + +pub fn query_linked_cw721_address( + deps: Deps, +) -> Result { + let linked_cw721_address = LINKED_CW721_ADDRESS.load(deps.storage)?; + + Ok(GetLinkedCw721AddressResponse { + linked_cw721_address, + }) +} diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/state.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/state.rs new file mode 100644 index 000000000..a991b7985 --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/state.rs @@ -0,0 +1,6 @@ +use andromeda_non_fungible_tokens::pow_cw721::PowNFTInfo; +use andromeda_std::amp::AndrAddr; +use cw_storage_plus::{Item, Map}; + +pub const LINKED_CW721_ADDRESS: Item = Item::new("linked_cw721_address"); +pub const POW_NFT: Map = Map::new("pow_nft"); diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock.rs new file mode 100644 index 000000000..9f53fc7fe --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock.rs @@ -0,0 +1,84 @@ +// use andromeda_data_storage::graph::{Coordinate, GetMapInfoResponse, MapInfo}; +use andromeda_non_fungible_tokens::cw721::TokenExtension; +use andromeda_non_fungible_tokens::pow_cw721::{ + ExecuteMsg, GetLinkedCw721AddressResponse, GetPowNFTResponse, InstantiateMsg, QueryMsg, +}; +use andromeda_std::{ + amp::AndrAddr, error::ContractError, testing::mock_querier::MOCK_KERNEL_CONTRACT, +}; +use cosmwasm_std::{ + from_json, + testing::{mock_env, mock_info, MockApi, MockStorage}, + Deps, DepsMut, OwnedDeps, Response, +}; + +use crate::contract::{execute, instantiate, query}; +use crate::testing::mock_querier::{mock_dependencies_custom, WasmMockQuerier}; + +pub type MockDeps = OwnedDeps; + +pub fn proper_initialization( + linked_cw721_address: AndrAddr, + authorized_origin_minter_addresses: Option>, +) -> MockDeps { + let mut deps = mock_dependencies_custom(&[]); + let info = mock_info("creator", &[]); + let msg = InstantiateMsg { + kernel_address: MOCK_KERNEL_CONTRACT.to_string(), + owner: None, + linked_cw721_address, + authorized_origin_minter_addresses, + }; + let env = mock_env(); + instantiate(deps.as_mut(), env, info, msg).unwrap(); + deps +} + +pub fn mint_pow_nft( + deps: DepsMut<'_>, + sender: &str, + owner: AndrAddr, + token_id: String, + token_uri: Option, + extension: TokenExtension, + base_difficulty: u64, +) -> Result { + let msg = ExecuteMsg::MintPowNFT { + owner, + token_id, + token_uri, + extension, + base_difficulty, + }; + let info = mock_info(sender, &[]); + execute(deps, mock_env(), info, msg) +} + +pub fn submit_proof( + deps: DepsMut<'_>, + sender: &str, + token_id: String, + nonce: u128, +) -> Result { + let msg = ExecuteMsg::SubmitProof { token_id, nonce }; + let info = mock_info(sender, &[]); + execute(deps, mock_env(), info, msg) +} + +pub fn query_linked_cw721_address( + deps: Deps, +) -> Result { + let res = query(deps, mock_env(), QueryMsg::GetLinkedCw721Address {}); + match res { + Ok(res) => Ok(from_json(res).unwrap()), + Err(err) => Err(err), + } +} + +pub fn query_pow_nft(deps: Deps, token_id: String) -> Result { + let res = query(deps, mock_env(), QueryMsg::GetPowNFT { token_id }); + match res { + Ok(res) => Ok(from_json(res).unwrap()), + Err(err) => Err(err), + } +} diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock_querier.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock_querier.rs new file mode 100644 index 000000000..d84dfba91 --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock_querier.rs @@ -0,0 +1,99 @@ +use andromeda_non_fungible_tokens::cw721::QueryMsg as AndrCw721QueryMsg; +use andromeda_std::testing::mock_querier::MockAndromedaQuerier; +use andromeda_std::{ + ado_base::InstantiateMsg, ado_contract::ADOContract, + testing::mock_querier::MOCK_KERNEL_CONTRACT, +}; +use cosmwasm_std::QuerierWrapper; +use cosmwasm_std::{ + from_json, + testing::{mock_env, mock_info, MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}, + Coin, OwnedDeps, Querier, QuerierResult, QueryRequest, SystemError, SystemResult, WasmQuery, +}; +use cosmwasm_std::{to_json_binary, Binary, ContractResult}; + +pub const MOCK_CW721_CONTRACT: &str = "cw721_contract"; +pub const ORIGIN_MINTER: &str = "origin_minter"; + +/// Alternative to `cosmwasm_std::testing::mock_dependencies` that allows us to respond to custom queries. +/// +/// Automatically assigns a kernel address as MOCK_KERNEL_CONTRACT. +pub fn mock_dependencies_custom( + contract_balance: &[Coin], +) -> OwnedDeps { + let custom_querier: WasmMockQuerier = + WasmMockQuerier::new(MockQuerier::new(&[(MOCK_CONTRACT_ADDR, contract_balance)])); + let storage = MockStorage::default(); + let mut deps = OwnedDeps { + storage, + api: MockApi::default(), + querier: custom_querier, + custom_query_type: std::marker::PhantomData, + }; + ADOContract::default() + .instantiate( + &mut deps.storage, + mock_env(), + &deps.api, + &QuerierWrapper::new(&deps.querier), + mock_info("sender", &[]), + InstantiateMsg { + ado_type: "pow-cw721".to_string(), + ado_version: "test".to_string(), + kernel_address: MOCK_KERNEL_CONTRACT.to_string(), + owner: None, + }, + ) + .unwrap(); + deps +} +pub struct WasmMockQuerier { + pub base: MockQuerier, +} + +impl Querier for WasmMockQuerier { + fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { + // MockQuerier doesn't support Custom, so we ignore it completely here + let request: QueryRequest = match from_json(bin_request) { + Ok(v) => v, + Err(e) => { + return SystemResult::Err(SystemError::InvalidRequest { + error: format!("Parsing query request: {e}"), + request: bin_request.into(), + }) + } + }; + self.handle_query(&request) + } +} + +impl WasmMockQuerier { + pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { + match &request { + QueryRequest::Wasm(WasmQuery::Smart { contract_addr, msg }) => { + match contract_addr.as_str() { + MOCK_CW721_CONTRACT => self.handle_cw721_smart_query(msg), + _ => MockAndromedaQuerier::default().handle_query(&self.base, request), + } + } + _ => MockAndromedaQuerier::default().handle_query(&self.base, request), + } + } + + fn handle_cw721_smart_query(&self, msg: &Binary) -> QuerierResult { + match from_json(msg).unwrap() { + AndrCw721QueryMsg::OwnerOf { .. } => { + let msg_response = cw721::OwnerOfResponse { + owner: ORIGIN_MINTER.to_string(), + approvals: vec![], + }; + SystemResult::Ok(ContractResult::Ok(to_json_binary(&msg_response).unwrap())) + } + _ => panic!("Unsupported Query"), + } + } + + pub fn new(base: MockQuerier) -> Self { + WasmMockQuerier { base } + } +} diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mod.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mod.rs new file mode 100644 index 000000000..217ceb8c1 --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mod.rs @@ -0,0 +1,3 @@ +mod mock; +mod mock_querier; +mod tests; diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs new file mode 100644 index 000000000..f0509d0d9 --- /dev/null +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs @@ -0,0 +1,175 @@ +use andromeda_non_fungible_tokens::cw721::{QueryMsg as AndrCw721QueryMsg, TokenExtension}; +use andromeda_std::{amp::AndrAddr, common::encode_binary, error::ContractError}; +use cosmwasm_std::Querier; +use cosmwasm_std::{from_json, to_json_binary, QueryRequest, WasmQuery}; +use test_case::test_case; + +use crate::testing::mock::{ + mint_pow_nft, proper_initialization, query_linked_cw721_address, query_pow_nft, submit_proof, +}; +use crate::testing::mock_querier::{MOCK_CW721_CONTRACT, ORIGIN_MINTER}; + +pub const AUTHORIZED_ORIGIN_MINTER1: &str = "authorized_origin_minter1"; +pub const AUTHORIZED_ORIGIN_MINTER2: &str = "authorized_origin_minter2"; +pub const UNAUTHORIZED_ORIGIN_MINTER: &str = "unauthorized_origin_minter"; + +#[test] +fn test_instantiation() { + let deps = proper_initialization(AndrAddr::from_string(MOCK_CW721_CONTRACT), None); + let linked_cw721_address = query_linked_cw721_address(deps.as_ref()) + .unwrap() + .linked_cw721_address; + assert_eq!( + linked_cw721_address, + AndrAddr::from_string(MOCK_CW721_CONTRACT) + ); +} + +#[test] +fn test_mint_pow_nft_invalid_user() { + let mut deps = proper_initialization( + AndrAddr::from_string(MOCK_CW721_CONTRACT), + Some(vec![ + AndrAddr::from_string(AUTHORIZED_ORIGIN_MINTER1), + AndrAddr::from_string(AUTHORIZED_ORIGIN_MINTER2), + ]), + ); + let err_response = mint_pow_nft( + deps.as_mut(), + UNAUTHORIZED_ORIGIN_MINTER, + AndrAddr::from_string("owner"), + "test_pow1".to_string(), + None, + TokenExtension { + publisher: "Andromeda".to_string(), + }, + 10_u64, + ) + .unwrap_err(); + + assert_eq!(err_response, ContractError::Unauthorized {}); +} + +#[test] +fn test_mint_pow_nft() { + let mut deps = proper_initialization(AndrAddr::from_string(MOCK_CW721_CONTRACT), None); + + mint_pow_nft( + deps.as_mut(), + ORIGIN_MINTER, + AndrAddr::from_string(ORIGIN_MINTER), + "test_pow1".to_string(), + None, + TokenExtension { + publisher: "Andromeda".to_string(), + }, + 10_u64, + ) + .unwrap(); + + let owner_query_msg = to_json_binary(&QueryRequest::::Wasm( + WasmQuery::Smart { + contract_addr: MOCK_CW721_CONTRACT.to_string(), + msg: encode_binary(&AndrCw721QueryMsg::OwnerOf { + token_id: "test_pow1".to_string(), + include_expired: None, + }) + .unwrap(), + }, + )) + .unwrap(); + + let raw_query_res = deps.querier.raw_query(&owner_query_msg); + let owner_response: cw721::OwnerOfResponse = + from_json(&(raw_query_res.unwrap()).unwrap()).unwrap(); + assert_eq!(owner_response.owner, ORIGIN_MINTER); + + let pow_nft = query_pow_nft(deps.as_ref(), "test_pow1".to_string()).unwrap(); + println!("PoW NFT: {:?}", pow_nft); + assert_eq!(pow_nft.nft_response.level, 1); +} + +#[test_case("test_pow1", 20_u64, 582586_u128 ; "Difficulty: 20")] +#[test_case("test_pow1", 10_u64, 944_u128 ; "Difficulty: 10")] +#[test_case("test_pow1", 2_u64, 19_u128 ; "Difficulty: 2")] +fn test_submit_valid_proofs(token_id: &str, difficulty: u64, nonce: u128) { + let mut deps = proper_initialization(AndrAddr::from_string(MOCK_CW721_CONTRACT), None); + + mint_pow_nft( + deps.as_mut(), + ORIGIN_MINTER, + AndrAddr::from_string(ORIGIN_MINTER), + token_id.to_string(), + None, + TokenExtension { + publisher: "Andromeda".to_string(), + }, + difficulty, + ) + .unwrap(); + + submit_proof(deps.as_mut(), "viewer", "test_pow1".to_string(), nonce).unwrap(); + + let pow_nft = query_pow_nft(deps.as_ref(), token_id.to_string()).unwrap(); + + println!("PoW NFT: {:?}", pow_nft); +} + +#[test_case("test_pow1", 20_u64, 58256_u128 ; "Difficulty: 20")] +#[test_case("test_pow1", 10_u64, 94_u128 ; "Difficulty: 10")] +#[test_case("test_pow1", 2_u64, 10_u128 ; "Difficulty: 2")] +fn test_submit_invalid_proofs(token_id: &str, difficulty: u64, nonce: u128) { + let mut deps = proper_initialization(AndrAddr::from_string(MOCK_CW721_CONTRACT), None); + + mint_pow_nft( + deps.as_mut(), + ORIGIN_MINTER, + AndrAddr::from_string(ORIGIN_MINTER), + token_id.to_string(), + None, + TokenExtension { + publisher: "Andromeda".to_string(), + }, + difficulty, + ) + .unwrap(); + + let err = submit_proof(deps.as_mut(), "viewer", "test_pow1".to_string(), nonce).unwrap_err(); + + assert_eq!( + err, + ContractError::CustomError { + msg: "Proof does not meet difficulty".to_string() + } + ); +} + +#[test] +fn test_increase_level() { + let mut deps = proper_initialization(AndrAddr::from_string(MOCK_CW721_CONTRACT), None); + + mint_pow_nft( + deps.as_mut(), + ORIGIN_MINTER, + AndrAddr::from_string(ORIGIN_MINTER), + "test_pow1".to_string(), + None, + TokenExtension { + publisher: "Andromeda".to_string(), + }, + 2_u64, + ) + .unwrap(); + + let nonces_to_submit = vec![19_u128, 18_u128, 28_u128, 44_u128, 217_u128, 5530_u128]; + + for nonce in nonces_to_submit.iter() { + submit_proof(deps.as_mut(), "viewer", "test_pow1".to_string(), *nonce).unwrap(); + let pow_nft = query_pow_nft(deps.as_ref(), "test_pow1".to_string()).unwrap(); + println!("Level: {:?}", pow_nft.nft_response.level); + println!("<==============================< CONTINUE >==============================>"); + } + + let pow_nft = query_pow_nft(deps.as_ref(), "test_pow1".to_string()).unwrap(); + assert_eq!(7, pow_nft.nft_response.level); +} diff --git a/packages/andromeda-non-fungible-tokens/src/lib.rs b/packages/andromeda-non-fungible-tokens/src/lib.rs index 74ec1fbae..a86275a9e 100644 --- a/packages/andromeda-non-fungible-tokens/src/lib.rs +++ b/packages/andromeda-non-fungible-tokens/src/lib.rs @@ -1,6 +1,7 @@ pub mod auction; pub mod crowdfund; pub mod cw721; +pub mod pow_cw721; // pub mod cw721_bid; // pub mod cw721_staking; // pub mod cw721_timelock; diff --git a/packages/andromeda-non-fungible-tokens/src/pow_cw721.rs b/packages/andromeda-non-fungible-tokens/src/pow_cw721.rs new file mode 100644 index 000000000..a17e901ac --- /dev/null +++ b/packages/andromeda-non-fungible-tokens/src/pow_cw721.rs @@ -0,0 +1,56 @@ +use andromeda_std::{amp::AndrAddr, andr_exec, andr_instantiate, andr_query}; +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Addr, Binary}; + +use crate::cw721::TokenExtension; + +#[andr_instantiate] +#[cw_serde] +pub struct InstantiateMsg { + pub linked_cw721_address: AndrAddr, + pub authorized_origin_minter_addresses: Option>, +} + +#[andr_exec] +#[cw_serde] +pub enum ExecuteMsg { + MintPowNFT { + owner: AndrAddr, + token_id: String, + token_uri: Option, + extension: TokenExtension, + base_difficulty: u64, + }, + SubmitProof { + token_id: String, + nonce: u128, + }, +} + +#[cw_serde] +pub struct PowNFTInfo { + pub owner: Addr, + pub level: u64, + pub last_hash: Binary, + pub difficulty: u64, +} + +#[andr_query] +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(GetPowNFTResponse)] + GetPowNFT { token_id: String }, + #[returns(GetLinkedCw721AddressResponse)] + GetLinkedCw721Address {}, +} + +#[cw_serde] +pub struct GetPowNFTResponse { + pub nft_response: PowNFTInfo, +} + +#[cw_serde] +pub struct GetLinkedCw721AddressResponse { + pub linked_cw721_address: AndrAddr, +} From c8953e68b65696ee0595788f23484ae47ee56098 Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Wed, 4 Dec 2024 18:13:53 +0000 Subject: [PATCH 02/10] feat: added margin to difficulty --- .../andromeda-pow-cw721/src/execute.rs | 22 +++++++++++++------ .../andromeda-pow-cw721/src/testing/tests.rs | 3 +-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs index 4df675280..14b0ff0f8 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs @@ -110,14 +110,9 @@ fn execute_submit_proof( hasher.update(&pow_nft.last_hash); hasher.update(&nonce.to_be_bytes()); let hash = hasher.finalize(); - println!("Hash: {:?}", Binary(hash.to_vec())); - println!("Last Hash: {:?}", pow_nft.last_hash); let hash_value = u128::from_be_bytes(hash[0..16].try_into().unwrap()); let threshold = u128::MAX >> (pow_nft.difficulty as u32); - println!("Difficulty: {:?}", pow_nft.difficulty); - println!("Threshold: {:?}", threshold); - println!("Hash Value: {:?}", hash_value); if hash_value > threshold { return Err(ContractError::CustomError { @@ -125,8 +120,22 @@ fn execute_submit_proof( }); } + pow_nft.difficulty = if pow_nft.difficulty >= 2 { + let next_difficulty = (pow_nft.difficulty as f64 * 1.5) as u64; + if next_difficulty > 128 { + return Err(ContractError::CustomError { + msg: format!( + "Max difficulty is 128. Next difficulty will be over 128. Current level: {:?}", + pow_nft.level + ), + }); + } + next_difficulty + } else { + 2 + }; + pow_nft.level += 1; - pow_nft.difficulty = (pow_nft.difficulty as f64 * 1.5) as u64; let block_height = ctx.env.block.height; @@ -136,7 +145,6 @@ fn execute_submit_proof( hasher.update(&block_height.to_be_bytes()); let hash = hasher.finalize(); pow_nft.last_hash = Binary(hash.to_vec()); - println!("Last Hash After Level Up: {:?}", pow_nft.last_hash); POW_NFT.save(ctx.deps.storage, token_id, &pow_nft)?; diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs index f0509d0d9..51dd563a1 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs @@ -85,7 +85,6 @@ fn test_mint_pow_nft() { assert_eq!(owner_response.owner, ORIGIN_MINTER); let pow_nft = query_pow_nft(deps.as_ref(), "test_pow1".to_string()).unwrap(); - println!("PoW NFT: {:?}", pow_nft); assert_eq!(pow_nft.nft_response.level, 1); } @@ -112,7 +111,7 @@ fn test_submit_valid_proofs(token_id: &str, difficulty: u64, nonce: u128) { let pow_nft = query_pow_nft(deps.as_ref(), token_id.to_string()).unwrap(); - println!("PoW NFT: {:?}", pow_nft); + println!("PoW NFT Level: {:?}", pow_nft.nft_response.level); } #[test_case("test_pow1", 20_u64, 58256_u128 ; "Difficulty: 20")] From 6e9ea185aae10cb17db0ee0ee18d59761013bf9c Mon Sep 17 00:00:00 2001 From: Mitar Djakovic Date: Wed, 4 Dec 2024 20:40:05 +0200 Subject: [PATCH 03/10] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba2a70e9e..8a1496e36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Validator Staking ADO [(#330)](https://github.com/andromedaprotocol/andromeda-core/pull/330) - Added Restake and Redelegate to Validator Staking [(#622)](https://github.com/andromedaprotocol/andromeda-core/pull/622) - Added andromeda-math and andromeda-account packages[(#672)](https://github.com/andromedaprotocol/andromeda-core/pull/672) +- Added PoW Cw721 ADO [(#697)](https://github.com/andromedaprotocol/andromeda-core/pull/697) ### Changed From feb0aa66e47f5e16f5f53b6898c877635c4540aa Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Mon, 9 Dec 2024 14:41:17 +0000 Subject: [PATCH 04/10] fix: renamed nonce to solution --- .../andromeda-pow-cw721/src/execute.rs | 10 ++++++---- .../andromeda-pow-cw721/src/testing/mock.rs | 4 ++-- .../andromeda-non-fungible-tokens/src/pow_cw721.rs | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs index 14b0ff0f8..fbadba26b 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs @@ -31,7 +31,9 @@ pub fn handle_execute(mut ctx: ExecuteContext, msg: ExecuteMsg) -> Result execute_mint_pow_nft(ctx, owner, token_id, token_uri, extension, base_difficulty), - ExecuteMsg::SubmitProof { token_id, nonce } => execute_submit_proof(ctx, token_id, nonce), + ExecuteMsg::SubmitProof { token_id, solution } => { + execute_submit_proof(ctx, token_id, solution) + } _ => ADOContract::default().execute(ctx, msg), }?; @@ -99,7 +101,7 @@ fn execute_mint_pow_nft( fn execute_submit_proof( ctx: ExecuteContext, token_id: String, - nonce: u128, + solution: u128, ) -> Result { let sender = ctx.info.sender; let mut pow_nft = POW_NFT @@ -108,7 +110,7 @@ fn execute_submit_proof( let mut hasher = Sha256::new(); hasher.update(&pow_nft.last_hash); - hasher.update(&nonce.to_be_bytes()); + hasher.update(&solution.to_be_bytes()); let hash = hasher.finalize(); let hash_value = u128::from_be_bytes(hash[0..16].try_into().unwrap()); @@ -141,7 +143,7 @@ fn execute_submit_proof( let mut hasher = Sha256::new(); hasher.update(&pow_nft.last_hash); - hasher.update(&nonce.to_be_bytes()); + hasher.update(&solution.to_be_bytes()); hasher.update(&block_height.to_be_bytes()); let hash = hasher.finalize(); pow_nft.last_hash = Binary(hash.to_vec()); diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock.rs index 9f53fc7fe..c0d431b92 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/mock.rs @@ -58,9 +58,9 @@ pub fn submit_proof( deps: DepsMut<'_>, sender: &str, token_id: String, - nonce: u128, + solution: u128, ) -> Result { - let msg = ExecuteMsg::SubmitProof { token_id, nonce }; + let msg = ExecuteMsg::SubmitProof { token_id, solution }; let info = mock_info(sender, &[]); execute(deps, mock_env(), info, msg) } diff --git a/packages/andromeda-non-fungible-tokens/src/pow_cw721.rs b/packages/andromeda-non-fungible-tokens/src/pow_cw721.rs index a17e901ac..59f4719dd 100644 --- a/packages/andromeda-non-fungible-tokens/src/pow_cw721.rs +++ b/packages/andromeda-non-fungible-tokens/src/pow_cw721.rs @@ -23,7 +23,7 @@ pub enum ExecuteMsg { }, SubmitProof { token_id: String, - nonce: u128, + solution: u128, }, } From 59f3110fe4c219a3936ab8a1d8861783e295cc3d Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Mon, 9 Dec 2024 14:52:46 +0000 Subject: [PATCH 05/10] feat: Added validation for base_difficulty parameter --- .../non-fungible-tokens/andromeda-pow-cw721/src/execute.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs index fbadba26b..8eb058a5a 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs @@ -51,6 +51,12 @@ fn execute_mint_pow_nft( extension: TokenExtension, base_difficulty: u64, ) -> Result { + if base_difficulty == 0 || base_difficulty > 128 { + return Err(ContractError::CustomError { + msg: "Base difficulty must be between 1 and 128".to_string(), + }); + } + let sender = ctx.info.sender; ADOContract::default().is_permissioned( From b3682d1c6e3cbd33fc502d83abce60a031a4cd51 Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Mon, 9 Dec 2024 15:33:53 +0000 Subject: [PATCH 06/10] fix: Added replay protection for proof submissions --- .../non-fungible-tokens/andromeda-pow-cw721/src/execute.rs | 2 ++ .../andromeda-pow-cw721/src/testing/tests.rs | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs index 8eb058a5a..77e7d4a0c 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs @@ -146,11 +146,13 @@ fn execute_submit_proof( pow_nft.level += 1; let block_height = ctx.env.block.height; + let nonce = ctx.env.transaction.unwrap(); let mut hasher = Sha256::new(); hasher.update(&pow_nft.last_hash); hasher.update(&solution.to_be_bytes()); hasher.update(&block_height.to_be_bytes()); + hasher.update(&nonce.index.to_be_bytes()); let hash = hasher.finalize(); pow_nft.last_hash = Binary(hash.to_vec()); diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs index 51dd563a1..6405650ee 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/testing/tests.rs @@ -111,7 +111,7 @@ fn test_submit_valid_proofs(token_id: &str, difficulty: u64, nonce: u128) { let pow_nft = query_pow_nft(deps.as_ref(), token_id.to_string()).unwrap(); - println!("PoW NFT Level: {:?}", pow_nft.nft_response.level); + assert_eq!(2, pow_nft.nft_response.level); } #[test_case("test_pow1", 20_u64, 58256_u128 ; "Difficulty: 20")] @@ -160,13 +160,10 @@ fn test_increase_level() { ) .unwrap(); - let nonces_to_submit = vec![19_u128, 18_u128, 28_u128, 44_u128, 217_u128, 5530_u128]; + let nonces_to_submit = vec![19_u128, 5_u128, 0_u128, 50_u128, 1474_u128, 16440_u128]; for nonce in nonces_to_submit.iter() { submit_proof(deps.as_mut(), "viewer", "test_pow1".to_string(), *nonce).unwrap(); - let pow_nft = query_pow_nft(deps.as_ref(), "test_pow1".to_string()).unwrap(); - println!("Level: {:?}", pow_nft.nft_response.level); - println!("<==============================< CONTINUE >==============================>"); } let pow_nft = query_pow_nft(deps.as_ref(), "test_pow1".to_string()).unwrap(); From c053ef639b7ac28092abbf146b480b2adce9d885 Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Mon, 9 Dec 2024 15:50:42 +0000 Subject: [PATCH 07/10] fix: Handled missing transaction info gracefully --- .../non-fungible-tokens/andromeda-pow-cw721/src/execute.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs index 77e7d4a0c..de8b5b351 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs @@ -146,7 +146,12 @@ fn execute_submit_proof( pow_nft.level += 1; let block_height = ctx.env.block.height; - let nonce = ctx.env.transaction.unwrap(); + let nonce = ctx + .env + .transaction + .ok_or_else(|| ContractError::CustomError { + msg: "Transaction info not available".to_string(), + })?; let mut hasher = Sha256::new(); hasher.update(&pow_nft.last_hash); From bee09211ce8c898859e0c79ab63ab205e479e409 Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Mon, 9 Dec 2024 16:37:15 +0000 Subject: [PATCH 08/10] feat: added token_id uniqueness validation --- .../andromeda-pow-cw721/src/execute.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs index de8b5b351..b11ae28b5 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs @@ -66,6 +66,15 @@ fn execute_mint_pow_nft( sender.clone(), )?; + if POW_NFT + .may_load(ctx.deps.storage, token_id.clone())? + .is_some() + { + return Err(ContractError::CustomError { + msg: format!("Token ID {} already exists", token_id), + }); + } + let owner_addr = owner.get_raw_address(&ctx.deps.as_ref())?; let cw721_address = LINKED_CW721_ADDRESS.load(ctx.deps.storage)?; From 97718735ffd2c3e591e7d5c044a4deda6e72720f Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Mon, 9 Dec 2024 16:41:43 +0000 Subject: [PATCH 09/10] feat: added emitting events for important state changes --- .../andromeda-pow-cw721/src/execute.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs index b11ae28b5..65aef5efc 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/src/execute.rs @@ -8,7 +8,7 @@ use andromeda_std::{ common::{actions::call_action, context::ExecuteContext, encode_binary}, error::ContractError, }; -use cosmwasm_std::{Binary, CosmosMsg, Response, WasmMsg}; +use cosmwasm_std::{Binary, CosmosMsg, Event, Response, WasmMsg}; use sha2::{Digest, Sha256}; use crate::contract::MINT_POW_NFT_ACTION; @@ -170,9 +170,18 @@ fn execute_submit_proof( let hash = hasher.finalize(); pow_nft.last_hash = Binary(hash.to_vec()); - POW_NFT.save(ctx.deps.storage, token_id, &pow_nft)?; + POW_NFT.save(ctx.deps.storage, token_id.clone(), &pow_nft)?; Ok(Response::new() .add_attribute("method", "submit_proof") - .add_attribute("sender", sender)) + .add_attribute("sender", sender) + .add_attribute("token_id", token_id.clone()) + .add_attribute("new_level", pow_nft.level.to_string()) + .add_attribute("new_difficulty", pow_nft.difficulty.to_string()) + .add_event( + Event::new("pow_nft_level_up") + .add_attribute("token_id", token_id) + .add_attribute("new_level", pow_nft.level.to_string()) + .add_attribute("new_difficulty", pow_nft.difficulty.to_string()), + )) } From 309cf937b1ea5657a8aafd35409e8879dc3f569b Mon Sep 17 00:00:00 2001 From: mdjakovic0920 Date: Thu, 12 Dec 2024 04:23:02 +0000 Subject: [PATCH 10/10] fix: changed version to b.1 --- Cargo.lock | 2 +- contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8daa6675b..449a94ddc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -764,7 +764,7 @@ dependencies = [ [[package]] name = "andromeda-pow-cw721" -version = "0.1.0-beta" +version = "0.1.0-b.1" dependencies = [ "andromeda-app", "andromeda-non-fungible-tokens", diff --git a/contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml b/contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml index ce9247980..4bb299509 100644 --- a/contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml +++ b/contracts/non-fungible-tokens/andromeda-pow-cw721/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "andromeda-pow-cw721" -version = "0.1.0-beta" +version = "0.1.0-b.1" authors = ["Mitar Djakovic "] edition = "2021" rust-version = "1.75.0"