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

instantiate2 #71

Merged
merged 14 commits into from
Nov 17, 2023
Merged

instantiate2 #71

merged 14 commits into from
Nov 17, 2023

Conversation

taitruong
Copy link
Collaborator

No description provided.

@@ -258,8 +259,24 @@ where
let instantiate = if CLASS_ID_TO_NFT_CONTRACT.has(deps.storage, class.id.clone()) {
vec![]
} else {
let class_id = ClassId::new(class.id.clone());
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part 1 of instantiate2: using class_id as salt,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smart!

packages/ics721/src/execute.rs Show resolved Hide resolved
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())?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice looks like you can take name and symbol right from the class data object?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the magic is here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, this is executed when WasmMsg::Instantiate2 is called.

let message = SubMsg::<T>::reply_on_success(
WasmMsg::Instantiate {
WasmMsg::Instantiate2 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main message that we are trying to get back in this PR right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, ser

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)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can get a quick explanation of what is going on here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@@ -10,6 +10,10 @@
# 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we have an Osmosis script in ics721?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, fyi @jhernandezb

@@ -164,6 +164,9 @@ where
return Err(ContractError::Unauthorized {});
}

// cw721 doesn't support on-chain metadata yet
// 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()))?
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

@humanalgorithm humanalgorithm self-assigned this Nov 17, 2023
@humanalgorithm humanalgorithm added this pull request to the merge queue Nov 17, 2023
Merged via the queue into public-awesome:main with commit 4fe21cc Nov 17, 2023
1 check passed
@taitruong taitruong mentioned this pull request Nov 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants