instantiate2#71
Conversation
| let instantiate = if CLASS_ID_TO_NFT_CONTRACT.has(deps.storage, class.id.clone()) { | ||
| vec![] | ||
| } else { | ||
| let class_id = ClassId::new(class.id.clone()); |
There was a problem hiding this comment.
part 1 of instantiate2: using class_id as salt,
| CLASS_ID_TO_NFT_CONTRACT.save(deps.storage, class_id.clone(), &cw721_addr)?; | ||
| NFT_CONTRACT_TO_CLASS_ID.save(deps.storage, cw721_addr.clone(), &class_id)?; | ||
| // cw721 addr has already been stored, here just check whether it exists, otherwise a NotFound error is thrown | ||
| let class_id = NFT_CONTRACT_TO_CLASS_ID.load(deps.storage, cw721_addr.clone())?; |
There was a problem hiding this comment.
part 3, no need storing class id in name and symbol, instead: once instantiated by sub msg, query, whether return addr fits with addr created before using instantiate2.
| #[derive(Default)] | ||
| pub struct MockAddressGenerator; | ||
|
|
||
| impl AddressGenerator for MockAddressGenerator { |
There was a problem hiding this comment.
biggest changes are in test, using MockAddressGenerator and MockApiBech32. In generator, predictable_contract_address() function covers instantiate2.
Also all tests had to be refactored and cleanup for better readability.
| } | ||
| } | ||
| // set name and symbol | ||
| instantiate_msg.symbol = class_data.symbol; |
There was a problem hiding this comment.
nice looks like you can take name and symbol right from the class data object?
There was a problem hiding this comment.
Some background here: ics721 need to store class id <> nft contract map entries (e.g. for determining incoming NFTs are back- (=burn) or forward transfer (=mint).
Before usage of instantiate2 we had to store class id in collection, since address is unknown yet. and on instantiation reply we have address but no class id, so had to store it as part of collection.
Which led to the side effect frontends not having source name and symbol.
By using instantiate2 ics721 now knows address - even before instantiation. So it can store name and symbol provided by source chain.
| .iter() | ||
| .map(|part| part.trim().parse().unwrap()) | ||
| .collect(); | ||
| let canonical_addr = instantiate2_address(checksum, creator, &salt)?; |
There was a problem hiding this comment.
Correct, this is executed when WasmMsg::Instantiate2 is called.
|
|
||
| let message = SubMsg::<T>::reply_on_success( | ||
| WasmMsg::Instantiate { | ||
| WasmMsg::Instantiate2 { |
There was a problem hiding this comment.
This is the main message that we are trying to get back in this PR right?
…d make sure instantiate2 creates 2 different, predictable contracts
| let CodeInfoResponse { checksum, .. } = deps | ||
| .querier | ||
| .query_wasm_code_info(CW721_CODE_ID.load(deps.storage)?)?; | ||
| let canonical_cw721_addr = instantiate2_address(&checksum, &canonical_creator, &salt)?; |
There was a problem hiding this comment.
Can get a quick explanation of what is going on here?
There was a problem hiding this comment.
This is mainly explained in docs here: https://github.com/CosmWasm/cosmwasm/blob/main/packages/std/src/addresses.rs#L270-L309
Essentially it uses salt for creating a unique address. Since class_id is unique for ics721, it is an ideal candidate for salt. NFT module has a exactly same approach: instantiated nft contract is a hash of class id.
| else { | ||
| // Token metadata is set unconditionaly on mint. If we have no | ||
| // metadata entry, we have no entry for this token at all. | ||
| return Ok(None); |
There was a problem hiding this comment.
If there is no token metadata we just return a None. Does the caller see that as an error? What does Ok(None) mean to the contract that is calling this?
There was a problem hiding this comment.
ics721 has prepared for handling token metadata. prepared means it is stored in ics721 rn - since core cw721 package does not support it per se. see zeke's comment in callback_mint() in execute.rs:
fn callback_mint(
&self,
deps: DepsMut,
class_id: ClassId,
tokens: Vec<Token>,
receiver: String,
) -> Result<Response<T>, ContractError> {
let receiver = deps.api.addr_validate(&receiver)?;
let cw721_addr = CLASS_ID_TO_NFT_CONTRACT.load(deps.storage, class_id.clone())?;
let mint = tokens
.into_iter()
.map(|Token { id, uri, data }| {
// We save token metadata here as, ideally, once cw721
// supports on-chain metadata, this is where we will set
// that value on the debt-voucher token. Note that this is
// set for every token, regardless of if data is None.
TOKEN_METADATA.save(deps.storage, (class_id.clone(), id.clone()), &data)?;
...Flow is like this:
- on send from source, metadata is loaded (which is currently None)
- pass to target as part of
NonFungibleTokenPacketData - save metadata in ics721 on target chain
There was a problem hiding this comment.
I just want to be clear here, so ics721. is storing onchain metadata on ics721? As in, when someone moves their nft from A to B, metadata is saved on chain B's ics721 contract. In our original discussion of this issue, we talked about querying original chain to get the onchain metadata from its original home chain. Does this mean we have abandoned that method?
There was a problem hiding this comment.
No, we didn't abandoned it. What we store here is on initial transfer and storing data provided from NonFungibleTokenPacketData. Also added another commit with some more comments on metadata. Pls check also my other 2 comments on TOKEN_METADATA. Please note, this is not part of this PR - only added some comments in source for clarifications.
| # specified in this config (e.g. 0.25token1;0.0001token2). | ||
| minimum-gas-prices = "0uosmo" | ||
|
|
||
| # The maximum gas a query coming over rest/grpc may consume. |
There was a problem hiding this comment.
Why we have an Osmosis script in ics721?
There was a problem hiding this comment.
we use ts-relayer for integration tests - provided by confio. their template is here: https://github.com/confio/cw-ibc-demo/
it provides 3 chains for integration tests: wasmd, osmosis and gaia. first 2 supports cosmwasm, whilst gaia does not. in repo it also contains genesis data for each chain - making it easy to setup tests.
For maintenance it is better using these default chains - otherwise we have to maintain them on our own. docker images can be found in confio/relayer-ci-images repo.
Did also a PR on upgrading to wasmd 0.31 here: confio/relayer-ci-images#3
Would be great if SG contributes a local SG with base genesis setup. This way ICS721 could also cover specific SG tests.
| // here NFT is transferred to another chain, NFT itself may have been transferred to his chain before | ||
| // in this case ICS721 may have metadata stored | ||
| let token_metadata = TOKEN_METADATA | ||
| .may_load(deps.storage, (class.id.clone(), token_id.clone()))? |
There was a problem hiding this comment.
during receive_nft() (starting point from nft contract to ics721 for transfer) read TOKEN_METADATA (in case it has been provided on previous, initial transfer to this chain), this is used for transferring to next chain. Not part of this PR. just outlining what happens here.
| // Source chain may have provided token metadata, so we save token metadata here | ||
| // Note, once cw721 doesn't support on-chain metadata yet - but this is where we will set | ||
| // that value on the debt-voucher token once it is supported. | ||
| // Also note that this is set for every token, regardless of if data is None. |
There was a problem hiding this comment.
This TOKEN_METADATA part is on receival on target chain, here we store metadata, for later re-transfer. This is not used on mint, since cw721 doesnt support on-chain metadata (yet).
No description provided.