-
Notifications
You must be signed in to change notification settings - Fork 10
chore: nonfungible example contract tests with pop-drink
#565
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
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
@@ Coverage Diff @@
## main #565 +/- ##
=======================================
Coverage 63.47% 63.47%
=======================================
Files 121 121
Lines 23838 23838
Branches 23838 23838
=======================================
Hits 15130 15130
Misses 7664 7664
Partials 1044 1044 🚀 New features to boost your workflow:
|
Please target this to the fungibles PR branch, so that we dont have to review the same changes which appear in that PR again here. |
} | ||
|
||
/// Returns the owner of an item, if any. | ||
#[ink(message)] | ||
pub fn owner_of(&self, item: ItemId) -> Result<Option<AccountId>> { | ||
api::owner_of(self.id, item).map_err(Psp34Error::from) | ||
pub fn owner_of(&self, item: ItemId) -> Option<AccountId> { |
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.
To keep it consistent with the fungibles example
@chungquantin please can you kindly resolve the merge conflicts? |
* fix visibility for pallet_nfts types used as call arguments Changes from the commit: paritytech/polkadot-sdk#3634 * [pallet-nfts, pallet_uniques] - Expose private structs Changes made from upstream: paritytech/polkadot-sdk#6087 * Adds BlockNumberProvider in multisig, proxy and nft pallets Upstream commit: paritytech/polkadot-sdk#5723 * Removed unused dependencies (partial progress) Upstream commit: paritytech/polkadot-sdk#7329 * implement DecodeWithMemTracking for frame pallets paritytech/polkadot-sdk#7598 * chore: bump pallet-nfts version to 34.1.0 * feat: adds BlockNumberProvider in nonfungbiles pallet * chore: update lock file of pop-api to sync nfts * chore: BlockNumberProvider for devnet nonfungibles * chore: fix benchmarking to use new BlockNumberFor * chore: revert changes * fix: missing DecodeWithMemTracking * chore: update weights of pallets * refactor(nonfungibles): remove unused BlockNumberProvider type * chore: remove unused Zero trait * chore: revert non-relevant changes * chore: revert non-relevant changes This reverts commit 182486a. * chore: benchmarking replace with BlockNumberProvider * chore(pallet-nfts): update weights
* chore(api/examples/fungible): bump drink version * chore(ci): test contract in examples * chore: update drink dependency branch * chore: resolve review comments
@@ -10,6 +10,12 @@ pop-api = { path = "../../../pop-api", default-features = false, features = [ | |||
"nonfungibles", | |||
] } | |||
|
|||
[dev-dependencies] | |||
drink = { package = "pop-drink", git = "https://github.com/r0gue-io/pop-drink", branch = "chungquantin/feat-nfts", features = [ "devnet" ] } |
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 should be merged to main and updated here before being merged 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.
PR approved, so once merged this can be updated.
// The contract bundle provider. | ||
// | ||
// See https://github.com/r0gue-io/pop-drink/blob/main/crates/drink/drink/test-macro/src/lib.rs for more information. | ||
#[drink::contract_bundle_provider] |
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 this definition be moved within the deploy
helper function below? It eliminates the complexity/noise of having yet another thing to define, where someone can just copy the deploy function in its entirey and get this definition included.
The separate usage on 77 should then be using the deploy helper.
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.
Good idea! I have moved it to the deploy
method in 7607a1a
|
||
// Deployment and constructor method tests. | ||
|
||
fn deploy_with_default(session: &mut Session<Pop>) -> Result<AccountId> { |
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.
Would move all the helpers to the bottom after tests. I see value in firstly showing how the sandbox is defined/initialised, but then it should be showing the tests and not more boiler plate.
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.
Move this method into Contract
struct and rename it to default
. Hence, Contract::default()
means deploying a contract by default to keep it short.
|
||
// A set of helper methods to test the contract calls. | ||
|
||
fn collection_id(session: &mut Session<Pop>) -> CollectionId { |
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.
Would suggest a pattern like
pop-node/pop-api/integration-tests/src/messaging.rs
Lines 402 to 485 in 9cd371f
struct Contract { | |
address: AccountId32, | |
id: AccountId32, | |
} | |
impl Contract { | |
fn new() -> Self { | |
let address = instantiate(CONTRACT, INIT_VALUE, vec![]); | |
Self { address: address.clone(), id: address } | |
} | |
fn ismp_get( | |
&self, | |
id: MessageId, | |
request: Get, | |
fee: Balance, | |
callback: bool, | |
) -> Result<(), Error> { | |
let result = self.call("ismp_get", (id, request, fee, callback).encode(), 0); | |
<Result<(), Error>>::decode(&mut &result.data[1..]) | |
.unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) | |
} | |
fn ismp_post( | |
&self, | |
id: MessageId, | |
request: Post, | |
fee: Balance, | |
callback: bool, | |
) -> Result<(), Error> { | |
let result = self.call("ismp_post", (id, request, fee, callback).encode(), 0); | |
<Result<(), Error>>::decode(&mut &result.data[1..]) | |
.unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) | |
} | |
fn xcm_new_query( | |
&self, | |
id: MessageId, | |
responder: Location, | |
timeout: BlockNumber, | |
callback: bool, | |
) -> Result<Option<QueryId>, Error> { | |
let result = self.call("xcm_new_query", (id, responder, timeout, callback).encode(), 0); | |
<Result<Option<QueryId>, Error>>::decode(&mut &result.data[1..]) | |
.unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) | |
} | |
fn poll(&self, request: MessageId) -> Result<Option<Status>, Error> { | |
let result = self.call("poll", request.encode(), 0); | |
Result::<Option<Status>, Error>::decode(&mut &result.data[1..]) | |
.unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) | |
} | |
fn get(&self, request: MessageId) -> Result<Option<Vec<u8>>, Error> { | |
let result = self.call("get", request.encode(), 0); | |
Result::<Option<Vec<u8>>, Error>::decode(&mut &result.data[1..]) | |
.unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) | |
} | |
fn remove(&self, request: MessageId) -> Result<(), Error> { | |
let result = self.call("remove", request.encode(), 0); | |
<Result<(), Error>>::decode(&mut &result.data[1..]) | |
.unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) | |
} | |
fn call(&self, function: &str, params: Vec<u8>, value: u128) -> ExecReturnValue { | |
let function = function_selector(function); | |
let params = [function, params].concat(); | |
bare_call(self.address.clone(), params, value).expect("should work") | |
} | |
fn last_event(&self) -> Vec<u8> { | |
let events = System::read_events_for_pallet::<pallet_contracts::Event<Runtime>>(); | |
let contract_events = events | |
.iter() | |
.filter_map(|event| match event { | |
pallet_contracts::Event::<Runtime>::ContractEmitted { contract, data, .. } | |
if contract == &self.address => | |
Some(data.as_slice()), | |
_ => None, | |
}) | |
.collect::<Vec<&[u8]>>(); | |
contract_events.last().unwrap().to_vec() | |
} | |
} |
contract.collection_id
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.
Thanks for the suggestion, I have refactored to bring all helpers method under the Contract struct in 3dd2ad4
@chungquantin Please could you kindly rebase again to address the CI issues and then once all green I can review again. |
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.
A few improvement suggestions, along with identification of a few possible flaws.
@@ -10,6 +10,12 @@ pop-api = { path = "../../../pop-api", default-features = false, features = [ | |||
"nonfungibles", | |||
] } | |||
|
|||
[dev-dependencies] | |||
drink = { package = "pop-drink", git = "https://github.com/r0gue-io/pop-drink", branch = "chungquantin/feat-nfts", features = [ "devnet" ] } |
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.
PR approved, so once merged this can be updated.
fn new_constructor_works(mut session: Session) { | ||
let _ = env_logger::try_init(); | ||
// Deploys a new contract. | ||
let contract = Contract::default(&mut session).unwrap(); |
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.
let contract = Contract::default(&mut session).unwrap(); | |
let contract = Contract::new(&mut session, None).unwrap(); |
Why not new
, deploy
, or instantiate
? Default doesnt imply a deployment action to me, but rather returning a default value of the Contract
struct. I get it is a helper, but it doesnt convey the right intent imo.
Would just use the new() function with None instead, only as that most closely matches the contract constructor definition, which is the goal with the Contract struct - a type-safe wrapper for invoking calls on the contract.
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.
Thanks. I have removed the method default
, agree that it makes the code readers confusing if we deploy inside the method. I changed it to new
as you suggested. Resolved in 3e1b812
// Deploys a new contract. | ||
let contract = Contract::default(&mut session).unwrap(); | ||
// Collection exists after the deployment. | ||
assert_eq!(session.sandbox().collection_owner(&COLLECTION), Some(contract.address.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.
Shouldnt need to clone, can probably use .collection_owner(&COLLECTION).as_ref()
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.
Resolved in 3e1b812
format!("{:?}", DestroyWitness { item_metadatas: 0, item_configs: 0, attributes: 0 }); | ||
|
||
// Error returned "Not enough data to fill buffer" due to contract termination. | ||
assert!(session.call::<String, ()>("destroy", &[witness_string], None).is_err()); |
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 could be improved imo. it seems to stem from the fact that the call impl in pop-drink panics, eliminating the possibility of handling this cleanly. This means that every contract will have this messy approach to termination.
The "Not enough data to fill buffer" should not be leaked out beyond pop-drink - perhaps it can be converted to a known error type?
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.
Resolved in dd7ca2e. I investigated and seems like the data returned is empty if the contract is terminated: https://github.com/use-ink/ink/blob/master/integration-tests/public/contract-terminate/lib.rs#L81 which also needs this PR in drink to be merged to support that: r0gue-io/pop-drink#35
// A set of helper methods to test the contract deployment and calls. | ||
|
||
// Deploys the contract with `NO_SALT and `INIT_VALUE`. | ||
fn deploy( |
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 seems to only be used within Contract::new()
now, so why not eliminate this function and just define it there instead?
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.
Agree, I have moved its logic to the new
method: 3e1b812
Ok(Self { address: contract }) | ||
} | ||
|
||
fn default(session: &mut Session<Pop>) -> Result<Self> { |
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.
Would eliminate and just use new(session, None)
personally. Its a few extra characters but conveys intent better.
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.
Agree, I have removed the method in 3e1b812
// Deploy a new contract. | ||
fn new(session: &mut Session<Pop>, max_supply: Option<u32>) -> Result<Self> { | ||
let contract = deploy(session, "new", vec![format!("{:?}", max_supply)], NO_SALT)?; | ||
Ok(Self { address: contract }) |
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.
Address is never used, which indicates a flaw.
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.
With 76080ba that refactored to use call_with_address
, this is now valid.
} | ||
|
||
fn collection_id(&self, session: &mut Session<Pop>) -> CollectionId { | ||
call::<Pop, CollectionId, Psp34Error>(session, "collection_id", vec![], None).unwrap() |
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.
None of these functions use the contract address, indicating that the pop-drink::call function is flawed in a multi-contract setting. See https://github.com/use-ink/drink/blob/94835ac3124cc02d556be1d9ff9fdf419fc24465/examples/quick-start-with-drink/lib.rs#L169
My assumption is that currently if I deploy two instances via Contract::new()
, creating two collections, I will only ever be able to operate on the first collection despite calling methods on the second contract instance.
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.
Resolved in r0gue-io/pop-drink#35 and 76080ba with new call_with_address
added.
76080ba
to
20af3a3
Compare
* ci: build runtimes using production profile * docs(ci): Explain the usage of production profile
* chore: sync with polkadot-stable2503-1 * fix: integration-tests as per sdk#7913 * fix(integration-tests): accomodate for different xcm weights between runtime * chore(pop-api): update Cargo.lock
* chore: depend on polkadot-stable2503-4 github tag * chore: revert subxt version * chore: update sc-network dependency * chore(pop-api): update Cargo.lock
The PR add drink tests to nonfungible example contract.
Requires: r0gue-io/pop-drink#31