-
Notifications
You must be signed in to change notification settings - Fork 31
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
instantiate2 #71
Conversation
@@ -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()); |
There was a problem hiding this comment.
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,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Smart!
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.
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 { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the magic is here
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, ser
…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.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()))? |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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).
No description provided.