diff --git a/.circleci/config.yml b/.circleci/config.yml index f6fbf8f2..494146f5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ workflows: jobs: contract_marketplace: docker: - - image: rust:1.64.0 + - image: rust:1.65.0 working_directory: ~/project/contracts/marketplace steps: - checkout: @@ -31,7 +31,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-marketplace-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-marketplace-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -53,11 +53,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-marketplace-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-marketplace-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} contract_name-minter: docker: - - image: rust:1.64.0 + - image: rust:1.65.0 working_directory: ~/project/contracts/name-minter steps: - checkout: @@ -67,7 +67,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-name-minter-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-name-minter-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -89,11 +89,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-name-minter-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-name-minter-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} contract_sg721-name: docker: - - image: rust:1.64.0 + - image: rust:1.65.0 working_directory: ~/project/contracts/sg721-name steps: - checkout: @@ -103,7 +103,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-sg721-name-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-sg721-name-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -125,11 +125,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-sg721-name-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-sg721-name-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} package_sg-name: docker: - - image: rust:1.64.0 + - image: rust:1.65.0 working_directory: ~/project/package/sg-name steps: - checkout: @@ -139,7 +139,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-sg-name-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-sg-name-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Unit Tests environment: @@ -149,11 +149,11 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-sg-name-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-sg-name-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} lint: docker: - - image: rust:1.64.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -161,7 +161,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-lint-rust:1.64.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-lint-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Add rustfmt component command: rustup component add rustfmt @@ -180,7 +180,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-lint-rust:1.64.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-lint-rust:1.65.0-{{ checksum "Cargo.lock" }} # This runs one time on the top level to ensure all contracts compile properly into wasm. # We don't run the wasm build per contract build, and then reuse a lot of the same dependencies, so this speeds up CI time @@ -188,7 +188,7 @@ jobs: # We also sanity-check the resultant wasm files. wasm-build: docker: - - image: rust:1.64.0 + - image: rust:1.65.0 steps: - checkout: path: ~/project @@ -197,7 +197,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-wasm-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + - cargocache-wasm-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Add wasm32 target command: rustup target add wasm32-unknown-unknown @@ -217,7 +217,7 @@ jobs: paths: - /usr/local/cargo/registry - target - key: cargocache-wasm-rust:1.64.0-{{ checksum "~/project/Cargo.lock" }} + key: cargocache-wasm-rust:1.65.0-{{ checksum "~/project/Cargo.lock" }} - run: name: Check wasm contracts command: | diff --git a/Cargo.lock b/Cargo.lock index a86f7821..2e3b77bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -531,7 +531,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "name-marketplace" -version = "1.2.5" +version = "1.2.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -557,7 +557,7 @@ dependencies = [ [[package]] name = "name-minter" -version = "1.2.5" +version = "1.2.6" dependencies = [ "anyhow", "cosmwasm-schema", @@ -811,7 +811,7 @@ dependencies = [ [[package]] name = "sg-name" -version = "1.2.5" +version = "1.2.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -821,7 +821,7 @@ dependencies = [ [[package]] name = "sg-name-common" -version = "1.2.5" +version = "1.2.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -833,7 +833,7 @@ dependencies = [ [[package]] name = "sg-name-market" -version = "1.2.5" +version = "1.2.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -843,7 +843,7 @@ dependencies = [ [[package]] name = "sg-name-minter" -version = "1.2.5" +version = "1.2.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -869,7 +869,7 @@ dependencies = [ [[package]] name = "sg-whitelist-basic" -version = "1.2.5" +version = "1.2.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -926,7 +926,7 @@ dependencies = [ [[package]] name = "sg721-name" -version = "1.2.5" +version = "1.2.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1130,7 +1130,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "whitelist-updatable" -version = "1.2.5" +version = "1.2.6" dependencies = [ "cosmwasm-schema", "cosmwasm-std", diff --git a/Cargo.toml b/Cargo.toml index 03ab2c1e..9c490453 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["packages/*", "contracts/*"] resolver = "2" [workspace.package] -version = "1.2.5" +version = "1.2.6" edition = "2021" homepage = "https://stargaze.zone" repository = "https://github.com/public-awesome/names" diff --git a/contracts/marketplace/schema/name-marketplace.json b/contracts/marketplace/schema/name-marketplace.json index 17750162..36187503 100644 --- a/contracts/marketplace/schema/name-marketplace.json +++ b/contracts/marketplace/schema/name-marketplace.json @@ -1,6 +1,6 @@ { "contract_name": "name-marketplace", - "contract_version": "1.2.3", + "contract_version": "1.2.6", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/name-minter/schema/name-minter.json b/contracts/name-minter/schema/name-minter.json index ab79cf01..717aba04 100644 --- a/contracts/name-minter/schema/name-minter.json +++ b/contracts/name-minter/schema/name-minter.json @@ -1,6 +1,6 @@ { "contract_name": "name-minter", - "contract_version": "1.2.3", + "contract_version": "1.2.6", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/name-minter/src/integration_tests.rs b/contracts/name-minter/src/integration_tests.rs index 83893d24..97e64a27 100644 --- a/contracts/name-minter/src/integration_tests.rs +++ b/contracts/name-minter/src/integration_tests.rs @@ -67,6 +67,7 @@ pub fn contract_nft() -> Box> { const USER: &str = "user"; const USER2: &str = "user2"; const USER3: &str = "user3"; +const USER4: &str = "user4"; const BIDDER: &str = "bidder"; const BIDDER2: &str = "bidder2"; const ADMIN: &str = "admin"; @@ -182,6 +183,7 @@ fn instantiate_contracts( "addr0001".to_string(), "addr0002".to_string(), USER.to_string(), + USER4.to_string(), ADMIN2.to_string(), ], mint_discount_bps: None, @@ -816,7 +818,7 @@ mod admin { let msg = WhitelistQueryMsg::AddressCount {}; let count: u64 = app.wrap().query_wasm_smart(WHITELIST, &msg).unwrap(); - assert_eq!(count, 4); + assert_eq!(count, 5); let msg = WhitelistQueryMsg::IncludesAddress { address: USER.to_string(), @@ -1828,7 +1830,7 @@ mod whitelist { let msg = WhitelistQueryMsg::AddressCount {}; let wl_addr_count: u64 = app.wrap().query_wasm_smart(WHITELIST, &msg).unwrap(); - assert_eq!(wl_addr_count, 4); + assert_eq!(wl_addr_count, 5); let msg = WhitelistExecuteMsg::AddAddresses { addresses: vec![USER3.to_string()], @@ -1953,7 +1955,7 @@ mod public_start_time { } } -mod eoa_owner { +mod associate_address { use super::*; use collection::transfer; @@ -2019,4 +2021,222 @@ mod eoa_owner { let owner = owner_of(&app, NAME.to_string()); assert_eq!(owner, USER.to_string()); } + + #[test] + fn associate_with_a_contract_with_no_admin() { + let mut app = instantiate_contracts( + None, + Some(ADMIN.to_string()), + Some(PUBLIC_MINT_START_TIME_IN_SECONDS.minus_seconds(1)), + ); + + let nft_id = app.store_code(contract_nft()); + + // For the purposes of this test, a collection contract with no admin needs to be instantiated (contract_with_no_admin) + // This contract needs to have a creator that is itself a contract and this creator contract should have an admin (USER). + // The admin (USER) of the creator contract will mint a name and associate the name with the collection contract that doesn't have an admin successfully. + + // Instantiating the creator contract with an admin (USER) + let creator_init_msg = Sg721InstantiateMsg { + name: "NFT".to_string(), + symbol: "NFT".to_string(), + minter: Addr::unchecked(MINTER).to_string(), + collection_info: CollectionInfo { + creator: USER.to_string(), + description: "Stargaze Names".to_string(), + image: "ipfs://example.com".to_string(), + external_link: None, + explicit_content: None, + start_trading_time: None, + royalty_info: None, + }, + }; + let creator_addr = app + .instantiate_contract( + nft_id, + Addr::unchecked(MINTER), + &creator_init_msg, + &[], + "NFT", + Some(USER.to_string()), + ) + .unwrap(); + + // The creator contract instantiates the collection contract with no admin + let init_msg = Sg721InstantiateMsg { + name: "NFT".to_string(), + symbol: "NFT".to_string(), + minter: creator_addr.to_string(), + collection_info: CollectionInfo { + creator: USER.to_string(), + description: "Stargaze Names".to_string(), + image: "ipfs://example.com".to_string(), + external_link: None, + explicit_content: None, + start_trading_time: None, + royalty_info: None, + }, + }; + + let collection_with_no_admin_addr = app + .instantiate_contract(nft_id, creator_addr, &init_msg, &[], "NFT", None) + .unwrap(); + + // USER mints a name + mint_and_list(&mut app, NAME, USER, None).unwrap(); + + // USER associates the name with the collection contract that doesn't have an admin + let msg = SgNameExecuteMsg::AssociateAddress { + name: NAME.to_string(), + address: Some(collection_with_no_admin_addr.to_string()), + }; + let res = app.execute_contract( + Addr::unchecked(USER), + Addr::unchecked(COLLECTION), + &msg, + &[], + ); + assert!(res.is_ok()); + } + #[test] + fn associate_with_a_contract_with_no_admin_fail() { + let mut app = instantiate_contracts( + None, + Some(ADMIN.to_string()), + Some(PUBLIC_MINT_START_TIME_IN_SECONDS.minus_seconds(1)), + ); + + let nft_id = app.store_code(contract_nft()); + + // For the purposes of this test, a collection contract with no admin needs to be instantiated (contract_with_no_admin) + // This contract needs to have a creator that is itself a contract and this creator contract should have an admin (USER). + // An address other than the admin (USER) of the creator contract will mint a name, try to associate the name with the collection contract that doesn't have an admin and fail. + + // Instantiating the creator contract with an admin (USER) + let creator_init_msg = Sg721InstantiateMsg { + name: "NFT".to_string(), + symbol: "NFT".to_string(), + minter: Addr::unchecked(MINTER).to_string(), + collection_info: CollectionInfo { + creator: USER.to_string(), + description: "Stargaze Names".to_string(), + image: "ipfs://example.com".to_string(), + external_link: None, + explicit_content: None, + start_trading_time: None, + royalty_info: None, + }, + }; + let creator_addr = app + .instantiate_contract( + nft_id, + Addr::unchecked(MINTER), + &creator_init_msg, + &[], + "NFT", + Some(USER.to_string()), + ) + .unwrap(); + + // The creator contract instantiates the collection contract with no admin + let init_msg = Sg721InstantiateMsg { + name: "NFT".to_string(), + symbol: "NFT".to_string(), + minter: creator_addr.to_string(), + collection_info: CollectionInfo { + creator: USER.to_string(), + description: "Stargaze Names".to_string(), + image: "ipfs://example.com".to_string(), + external_link: None, + explicit_content: None, + start_trading_time: None, + royalty_info: None, + }, + }; + + let collection_with_no_admin_addr = app + .instantiate_contract(nft_id, creator_addr, &init_msg, &[], "NFT", None) + .unwrap(); + + // USER4 mints a name + mint_and_list(&mut app, NAME, USER4, None).unwrap(); + + // USER4 tries to associate the name with the collection contract that doesn't have an admin + let msg = SgNameExecuteMsg::AssociateAddress { + name: NAME.to_string(), + address: Some(collection_with_no_admin_addr.to_string()), + }; + let res = app + .execute_contract( + Addr::unchecked(USER4), + Addr::unchecked(COLLECTION), + &msg, + &[], + ) + .map_err(|e| e.downcast::().unwrap()) + .unwrap_err(); + assert!(matches!( + res, + sg721_name::ContractError::UnauthorizedCreatorOrAdmin {} + )) + } + + #[test] + fn associate_with_a_contract_with_an_admin_fail() { + let mut app = instantiate_contracts( + None, + Some(ADMIN.to_string()), + Some(PUBLIC_MINT_START_TIME_IN_SECONDS.minus_seconds(1)), + ); + + let nft_id = app.store_code(contract_nft()); + + // Instantiating the creator contract with an admin (USER) + let creator_init_msg = Sg721InstantiateMsg { + name: "NFT".to_string(), + symbol: "NFT".to_string(), + minter: Addr::unchecked(MINTER).to_string(), + collection_info: CollectionInfo { + creator: USER.to_string(), + description: "Stargaze Names".to_string(), + image: "ipfs://example.com".to_string(), + external_link: None, + explicit_content: None, + start_trading_time: None, + royalty_info: None, + }, + }; + let contract_with_an_admin = app + .instantiate_contract( + nft_id, + Addr::unchecked(MINTER), + &creator_init_msg, + &[], + "NFT", + Some(USER.to_string()), + ) + .unwrap(); + + // USER4 mints a name + mint_and_list(&mut app, NAME, USER4, None).unwrap(); + + // USER4 tries to associate the name with the collection contract that has an admin (USER) + let msg = SgNameExecuteMsg::AssociateAddress { + name: NAME.to_string(), + address: Some(contract_with_an_admin.to_string()), + }; + let res = app + .execute_contract( + Addr::unchecked(USER4), + Addr::unchecked(COLLECTION), + &msg, + &[], + ) + .map_err(|e| e.downcast::().unwrap()) + .unwrap_err(); + assert!(matches!( + res, + sg721_name::ContractError::UnauthorizedCreatorOrAdmin {} + )) + } } diff --git a/contracts/sg721-name/schema/sg721-name.json b/contracts/sg721-name/schema/sg721-name.json index bdb2292d..ae51a73e 100644 --- a/contracts/sg721-name/schema/sg721-name.json +++ b/contracts/sg721-name/schema/sg721-name.json @@ -1,6 +1,6 @@ { "contract_name": "sg721-name", - "contract_version": "1.2.3", + "contract_version": "1.2.6", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/contracts/sg721-name/src/contract.rs b/contracts/sg721-name/src/contract.rs index cafb45a6..9bed45b7 100644 --- a/contracts/sg721-name/src/contract.rs +++ b/contracts/sg721-name/src/contract.rs @@ -4,7 +4,7 @@ use crate::{ }; use cosmwasm_std::{ - to_binary, Addr, Binary, ContractInfoResponse, Deps, DepsMut, Env, Event, MessageInfo, + ensure, to_binary, Addr, Binary, ContractInfoResponse, Deps, DepsMut, Env, Event, MessageInfo, StdError, StdResult, WasmMsg, }; @@ -573,9 +573,19 @@ fn validate_address(deps: Deps, sender: &Addr, addr: Addr) -> Result