Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move to cw721 package #161

Merged
merged 47 commits into from
Jun 27, 2024
Merged
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2c684b8
rename states: tokens -> nft_info, contract_info -> collection_info
taitruong Feb 27, 2024
c3dc54c
renamed structs and deprecate: TokenInfo -> NftInfo, ContractInfo -> …
taitruong Feb 27, 2024
98b7300
cargo schema
taitruong Feb 27, 2024
5fd2f3b
change storage keys for collection info and nft info, consider in mig…
taitruong Feb 28, 2024
a2b5c12
use PA repo for `cw-ownable`
taitruong Feb 28, 2024
b2f73a0
rename generics and make more readable
taitruong Feb 29, 2024
d46d2b7
CollectionInfoResponse -> CollectionInfo
taitruong Feb 29, 2024
3d101cb
add CollectionInfoExtension with RoyaltyInfo
taitruong Feb 29, 2024
ddc3758
cargo schema
taitruong Feb 29, 2024
18dd4d3
typo
taitruong Feb 29, 2024
917103c
cleanup
taitruong Feb 29, 2024
b3aac9e
rename
taitruong Feb 29, 2024
8d61f85
cargo schema
taitruong Feb 29, 2024
89da1ab
cleanup
taitruong Mar 3, 2024
fcd5159
creator and owner changes:
taitruong Mar 4, 2024
56c6dae
refactor and move key logic to cw721 package:
taitruong Mar 7, 2024
895f462
fix ci
taitruong Mar 7, 2024
920a7d4
cargo fmt
taitruong Mar 7, 2024
641ede9
cargo fmt + clippy
taitruong Mar 7, 2024
0e27df8
cargo fmt
taitruong Mar 7, 2024
8d91900
cargo schema
taitruong Mar 7, 2024
9010cac
cargo clippy
taitruong Mar 7, 2024
bebe8b2
undo: do not rename token keys
taitruong Mar 8, 2024
3d77652
fix unit test
taitruong Mar 8, 2024
6a827a3
remove useless generic `TMetadataResponse`
taitruong Mar 8, 2024
9b8b140
remove response structs to msg.rs
taitruong Mar 8, 2024
ed37215
cargo schema
taitruong Mar 9, 2024
ffa7424
move to dedicated fn
taitruong Mar 9, 2024
5db17ec
docs
taitruong Mar 9, 2024
1e32fac
rename
taitruong Mar 9, 2024
53754d7
cargo clippy
taitruong Apr 25, 2024
dc8a01f
add build script
taitruong Apr 25, 2024
9e06b0b
undo collection info extension and royalty info additions, will be ad…
taitruong Apr 30, 2024
67522f7
cleanup
taitruong May 23, 2024
a5652e2
generate schemas for entry points, cleanup
taitruong Jun 13, 2024
e02c7f9
update rustc 1.65 -> 1.71
taitruong Jun 13, 2024
6b3af84
update cosmwasm version 1.2 -> 1.5, due to rustc compiler issues
taitruong Jun 13, 2024
80a2d17
update optimizer
taitruong Jun 13, 2024
0515aa9
update rustc 1.71 -> 1.78
taitruong Jun 13, 2024
6944bcb
use optimizer
taitruong Jun 13, 2024
f135fe8
formatting
taitruong Jun 13, 2024
61d3693
install rustup first
taitruong Jun 13, 2024
d4ff6e7
set $HOME/.cargo/env
taitruong Jun 13, 2024
e34879f
install libgcc
taitruong Jun 13, 2024
410d11e
install build-base
taitruong Jun 13, 2024
495cccb
cleanup
taitruong Jun 13, 2024
33c7abc
cleanup
taitruong Jun 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
change storage keys for collection info and nft info, consider in mig…
…ration and keep legacy data for backward migration
taitruong committed Feb 28, 2024
commit 5fd2f3b654c2fcba5911e5e9c7bff32261e163cd
256 changes: 242 additions & 14 deletions contracts/cw721-base/src/lib.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ pub mod helpers;
pub mod msg;
mod query;
pub mod state;
pub mod upgrades;

#[cfg(test)]
mod contract_tests;
@@ -32,18 +31,18 @@ pub type Extension = Option<Empty>;
pub const CONTRACT_NAME: &str = "crates.io:cw721-base";
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

// currently we only support migrating from 0.16.0. this is ok for now because
// we have not released any 0.16.x where x != 0
//
// TODO: parse semvar so that any version 0.16.x can be migrated from
pub const EXPECTED_FROM_VERSION: &str = "0.16.0";

pub mod entry {
use self::state::{token_owner_idx, NftInfo, TokenIndexes};

use super::*;

#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
use cosmwasm_std::{
Addr, Api, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, Storage,
};
use cw721::CollectionInfoResponse;
use cw_storage_plus::{IndexedMap, Item, MultiIndex};

// This makes a conscious choice on the various generics used by the contract
#[cfg_attr(not(feature = "library"), entry_point)]
@@ -77,23 +76,122 @@ pub mod entry {
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result<Response, ContractError> {
pub fn migrate(deps: DepsMut, env: Env, msg: Empty) -> Result<Response, ContractError> {
let response = migrate_version(deps.storage, &env, &msg)?;
let response = migrate_legacy_minter(deps.storage, deps.api, &env, &msg, response)?;
let response = migrate_legacy_contract_info(deps.storage, &env, &msg, response)?;
migrate_legacy_tokens(deps.storage, &env, &msg, response)
}

pub fn migrate_version(
storage: &mut dyn Storage,
_env: &Env,
_msg: &Empty,
) -> Result<Response, ContractError> {
// make sure the correct contract is being upgraded, and it's being
// upgraded from the correct version.
cw2::assert_contract_version(deps.as_ref().storage, CONTRACT_NAME, EXPECTED_FROM_VERSION)?;
let response = Response::<Empty>::default()
.add_attribute("from_version", cw2::get_contract_version(storage)?.version)
.add_attribute("to_version", CONTRACT_VERSION);

// update contract version
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
cw2::set_contract_version(storage, CONTRACT_NAME, CONTRACT_VERSION)?;
Ok(response)
}

/// Migrates only in case ownership is not present
pub fn migrate_legacy_minter(
storage: &mut dyn Storage,
api: &dyn Api,
_env: &Env,
_msg: &Empty,
response: Response,
) -> Result<Response, ContractError> {
let owner_store: Item<Ownership<Addr>> = Item::new("ownership"); // TODO: cw_ownable does not support may_load, change this once it has support
match owner_store.may_load(storage)? {
Some(_) => Ok(response),
None => {
let legacy_minter_store: Item<Addr> = Item::new("minter");
let legacy_minter = legacy_minter_store.load(storage)?;
let ownership =
cw_ownable::initialize_owner(storage, api, Some(legacy_minter.as_str()))?;
Ok(response
.add_attribute("old_minter", legacy_minter)
.add_attributes(ownership.into_attributes()))
}
}
}

// perform the upgrade
upgrades::v0_17::migrate::<Extension, Empty, Empty, Empty>(deps)
/// Migrates only in case collection_info is not present
pub fn migrate_legacy_contract_info(
storage: &mut dyn Storage,
_env: &Env,
_msg: &Empty,
response: Response,
) -> Result<Response, ContractError> {
let contract = Cw721Contract::<Extension, Empty, Empty, Empty>::default();
match contract.collection_info.may_load(storage)? {
Some(_) => Ok(response),
None => {
let legacy_collection_info_store: Item<CollectionInfoResponse> =
Item::new("nft_info");
let legacy_collection_info = legacy_collection_info_store.load(storage)?;
contract
.collection_info
.save(storage, &legacy_collection_info)?;
Ok(response
.add_attribute("migrated collection name", legacy_collection_info.name)
.add_attribute("migrated collection symbol", legacy_collection_info.symbol))
}
}
}

/// Migrates only in case collection_info is not present
pub fn migrate_legacy_tokens(
storage: &mut dyn Storage,
_env: &Env,
_msg: &Empty,
response: Response,
) -> Result<Response, ContractError> {
let contract = Cw721Contract::<Extension, Empty, Empty, Empty>::default();
match contract.nft_info.is_empty(storage) {
false => Ok(response),
true => {
let indexes = TokenIndexes {
owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"),
};
let legacy_tokens_store: IndexedMap<
&str,
NftInfo<Extension>,
TokenIndexes<Extension>,
> = IndexedMap::new("tokens", indexes);
let keys = legacy_tokens_store
.keys(storage, None, None, Order::Ascending)
.collect::<StdResult<Vec<String>>>()?;
for key in keys {
let legacy_token = legacy_tokens_store.load(storage, &key)?;
contract.nft_info.save(storage, &key, &legacy_token)?;
}
Ok(response)
}
}
}
}

#[cfg(test)]
mod tests {
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{
testing::{mock_dependencies, mock_env, mock_info},
Addr, Order, StdError, StdResult,
};
use cw2::ContractVersion;
use cw721::{CollectionInfoResponse, Cw721Query};
use cw_storage_plus::{IndexedMap, Item, MultiIndex};

use crate::{
query::MAX_LIMIT,
state::{token_owner_idx, NftInfo, TokenIndexes},
};

use super::*;

@@ -154,4 +252,134 @@ mod tests {
.map(|a| a.into_string());
assert_eq!(minter, Some("owner".to_string()));
}

#[test]
fn test_migrate() {
let mut deps = mock_dependencies();

let env = mock_env();
use cw721_base_016 as v16;
v16::entry::instantiate(
deps.as_mut(),
env.clone(),
mock_info("owner", &[]),
v16::InstantiateMsg {
name: "legacy_name".into(),
symbol: "legacy_symbol".into(),
minter: "legacy_minter".into(),
},
)
.unwrap();

// mint 200 NFTs before migration
for i in 0..200 {
let info = mock_info("legacy_minter", &[]);
let msg = v16::ExecuteMsg::Mint(v16::msg::MintMsg {
token_id: i.to_string(),
owner: "owner".into(),
token_uri: None,
extension: None,
});
v16::entry::execute(deps.as_mut(), env.clone(), info, msg).unwrap();
}

// assert new data before migration:
// - ownership and collection info throws NotFound Error
cw_ownable::get_ownership(deps.as_ref().storage).unwrap_err();
let contract = Cw721Contract::<Extension, Empty, Empty, Empty>::default();
contract.collection_info(deps.as_ref()).unwrap_err();
// - no tokens
let all_tokens = contract
.all_tokens(deps.as_ref(), None, Some(MAX_LIMIT))
.unwrap();
assert_eq!(all_tokens.tokens.len(), 0);

// assert legacy data before migration:
// - version
let version = cw2::get_contract_version(deps.as_ref().storage)
.unwrap()
.version;
assert_eq!(version, "0.16.0");
// - legacy minter is set
let legacy_minter_store: Item<Addr> = Item::new("minter");
let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap();
assert_eq!(legacy_minter, "legacy_minter");
// - legacy collection info is set
let legacy_collection_info_store: Item<CollectionInfoResponse> = Item::new("nft_info");
let legacy_collection_info = legacy_collection_info_store
.load(deps.as_ref().storage)
.unwrap();
assert_eq!(legacy_collection_info.name, "legacy_name");
assert_eq!(legacy_collection_info.symbol, "legacy_symbol");
// - legacy tokens are set
let indexes = TokenIndexes {
owner: MultiIndex::new(token_owner_idx, "tokens", "tokens__owner"),
};
let legacy_tokens_store: IndexedMap<&str, NftInfo<Extension>, TokenIndexes<Extension>> =
IndexedMap::new("tokens", indexes);
let keys = legacy_tokens_store
.keys(deps.as_ref().storage, None, None, Order::Ascending)
.collect::<StdResult<Vec<String>>>()
.unwrap();
assert_eq!(keys.len(), 200);
for key in keys {
let legacy_token = legacy_tokens_store
.load(deps.as_ref().storage, &key)
.unwrap();
assert_eq!(legacy_token.owner.as_str(), "owner");
}

entry::migrate(deps.as_mut(), env, Empty {}).unwrap();

// version
let version = cw2::get_contract_version(deps.as_ref().storage)
.unwrap()
.version;
assert_eq!(version, CONTRACT_VERSION);
assert_ne!(version, "0.16.0");

// assert ownership
let ownership = cw_ownable::get_ownership(deps.as_ref().storage)
.unwrap()
.owner
.map(|a| a.into_string());
assert_eq!(ownership, Some("legacy_minter".to_string()));

// assert collection info
let collection_info = contract.collection_info(deps.as_ref()).unwrap();
let legacy_contract_info = CollectionInfoResponse {
name: "legacy_name".to_string(),
symbol: "legacy_symbol".to_string(),
};
assert_eq!(collection_info, legacy_contract_info);

// assert tokens
let all_tokens = contract
.all_tokens(deps.as_ref(), None, Some(MAX_LIMIT))
.unwrap();
assert_eq!(all_tokens.tokens.len(), 200);

// assert legacy data is still there (allowing backward migration in case of issues)
// - minter
let legacy_minter = legacy_minter_store.load(deps.as_ref().storage).unwrap();
assert_eq!(legacy_minter, "legacy_minter");
// - collection info
let legacy_collection_info = legacy_collection_info_store
.load(deps.as_ref().storage)
.unwrap();
assert_eq!(legacy_collection_info.name, "legacy_name");
assert_eq!(legacy_collection_info.symbol, "legacy_symbol");
// - tokens
let keys = legacy_tokens_store
.keys(deps.as_ref().storage, None, None, Order::Ascending)
.collect::<StdResult<Vec<String>>>()
.unwrap();
assert_eq!(keys.len(), 200);
for key in keys {
let legacy_token = legacy_tokens_store
.load(deps.as_ref().storage, &key)
.unwrap();
assert_eq!(legacy_token.owner.as_str(), "owner");
}
}
}
4 changes: 2 additions & 2 deletions contracts/cw721-base/src/query.rs
Original file line number Diff line number Diff line change
@@ -16,8 +16,8 @@ use cw_utils::maybe_addr;
use crate::msg::{MinterResponse, QueryMsg};
use crate::state::{Approval, Cw721Contract, NftInfo};

const DEFAULT_LIMIT: u32 = 10;
const MAX_LIMIT: u32 = 1000;
pub const DEFAULT_LIMIT: u32 = 10;
pub const MAX_LIMIT: u32 = 1000;

impl<'a, T, C, E, Q> Cw721Query<T> for Cw721Contract<'a, T, C, E, Q>
where
8 changes: 5 additions & 3 deletions contracts/cw721-base/src/state.rs
Original file line number Diff line number Diff line change
@@ -14,10 +14,12 @@ where
Q: CustomMsg,
E: CustomMsg,
{
/// Note: do not use deprecated/legacy key "nft_info"!
pub collection_info: Item<'a, CollectionInfoResponse>,
pub token_count: Item<'a, u64>,
/// Stored as (granter, operator) giving operator full control over granter's account
pub operators: Map<'a, (&'a Addr, &'a Addr), Expiration>,
/// Note: do not use deprecated/legacy keys "tokens" and "tokens__owner"!
pub nft_info: IndexedMap<'a, &'a str, NftInfo<T>, TokenIndexes<'a, T>>,
pub withdraw_address: Item<'a, String>,

@@ -44,11 +46,11 @@ where
{
fn default() -> Self {
Self::new(
"nft_info",
"collection_info", // Note: do not use deprecated/legacy key "nft_info"
"num_tokens",
"operators",
"tokens",
"tokens__owner",
"nft", // Note: do not use deprecated/legacy key "tokens"
"nft__owner", // Note: do not use deprecated/legacy key "tokens__owner"
"withdraw_address",
)
}
1 change: 0 additions & 1 deletion contracts/cw721-base/src/upgrades/mod.rs

This file was deleted.

27 changes: 0 additions & 27 deletions contracts/cw721-base/src/upgrades/v0_17.rs

This file was deleted.