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

feat(token-metadata): add burn_print_edition handler #684

Merged
merged 27 commits into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
681652d
feat(token-metadata): add burn_print_edition handler
samuelvanderwaal Aug 19, 2022
8cb4b4f
Merge branch 'master' into feat/burn-print-editions
samuelvanderwaal Aug 19, 2022
fed8698
fix(burn-print-edition): clippy lints
samuelvanderwaal Aug 23, 2022
e949d4e
fix: clippy lints
samuelvanderwaal Aug 23, 2022
a20d4fc
tests(token-metadata): add more tests
samuelvanderwaal Aug 23, 2022
5f3d0cb
chore(token-metadata): generate JS code for burn_edition_nft
samuelvanderwaal Aug 23, 2022
8d62056
Merge branch 'master' into feat/burn-print-editions
samuelvanderwaal Aug 24, 2022
ea15701
test(burn_edition_nft): only holder of print edition can burn
samuelvanderwaal Aug 24, 2022
836937a
test(token-metadata/burn_edition_nft): reprint burned nft
samuelvanderwaal Aug 25, 2022
e0fb85e
chore(token-metadata): merge master into branch
samuelvanderwaal Aug 25, 2022
b9a4b0c
test(token-metadata) refactor burn_edition_nft tests a little
samuelvanderwaal Aug 25, 2022
455c745
test(token-metdata): make mask check more complex
samuelvanderwaal Aug 25, 2022
2ec0b82
fix: clippy lints
samuelvanderwaal Aug 25, 2022
ded9c23
refactor: fix supply account; decrement max_supply
samuelvanderwaal Aug 31, 2022
3ffe8a8
chore: merge master into branch
samuelvanderwaal Aug 31, 2022
662c5e7
Merge branch 'master' into feat/burn-print-editions
samuelvanderwaal Sep 8, 2022
4f19f68
finish spec implementation; update tests
samuelvanderwaal Sep 14, 2022
da20c43
Merge branch 'master' into feat/burn-print-editions
samuelvanderwaal Sep 14, 2022
72257b1
refactor: update tests for new supply accounting; remove unused fn
samuelvanderwaal Sep 14, 2022
ce68d7e
chore: generate JS lib for token metadata
samuelvanderwaal Sep 14, 2022
a836cbc
chore: clean up tests
samuelvanderwaal Sep 14, 2022
c657d1c
fix: decrement max supply correctly
samuelvanderwaal Sep 14, 2022
cdfebb8
add missing check; regnerate JS api
samuelvanderwaal Sep 16, 2022
6307d9a
refactor: address review comments
samuelvanderwaal Sep 19, 2022
b92e489
add tests checking invalid master & print derivations
samuelvanderwaal Sep 19, 2022
6aefd85
tests: add a few more tests
samuelvanderwaal Sep 19, 2022
3326dd1
chore(token-metadata/js): generate JS lib
samuelvanderwaal Sep 19, 2022
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
test(burn_edition_nft): only holder of print edition can burn
  • Loading branch information
samuelvanderwaal committed Aug 24, 2022
commit ea157013a28bc90f3ddaf3ae33cdbab45f3bb262
128 changes: 128 additions & 0 deletions token-metadata/program/tests/burn_edition_nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod burn_edition_nft {
};
use solana_program::pubkey::Pubkey;
use solana_sdk::signature::Keypair;
use spl_associated_token_account::get_associated_token_address;

use super::*;

Expand Down Expand Up @@ -122,6 +123,133 @@ mod burn_edition_nft {
assert!(master_edition_account.is_some());
}

#[tokio::test]
async fn burn_edition_nft_in_separate_wallet() {
// Burn a print edition that is in a separate wallet, so owned by a different account
// than the master edition nft.
let mut context = program_test().start_with_context().await;

let original_nft = Metadata::new();
original_nft
.create_v2(
&mut context,
"Test".to_string(),
"TST".to_string(),
"uri".to_string(),
None,
10,
false,
None,
None,
None,
)
.await
.unwrap();

let master_edition = MasterEditionV2::new(&original_nft);
master_edition
.create_v3(&mut context, Some(10))
.await
.unwrap();
let mut print_edition = EditionMarker::new(&original_nft, &master_edition, 1);
print_edition.create(&mut context).await.unwrap();

// Transfer to new owner.
let new_owner = Keypair::new();
let new_owner_pubkey = new_owner.pubkey();
airdrop(&mut context, &new_owner_pubkey, 1_000_000_000)
.await
.unwrap();

context.warp_to_slot(10).unwrap();

print_edition
.transfer(&mut context, &new_owner_pubkey)
.await
.unwrap();

let kpbytes = &context.payer;
let payer = Keypair::from_bytes(&kpbytes.to_bytes()).unwrap();

// Old owner should not be able to burn.
let err = burn_edition(
&mut context,
print_edition.new_metadata_pubkey,
&payer,
print_edition.mint.pubkey(),
original_nft.mint.pubkey(),
print_edition.token.pubkey(),
master_edition.pubkey,
print_edition.new_edition_pubkey,
print_edition.pubkey,
)
.await
.unwrap_err();

// We've passed in the correct token account associated with the old owner but
// it has 0 tokens so we get this error.
assert_custom_error!(err, MetadataError::NotEnoughTokens);
samuelvanderwaal marked this conversation as resolved.
Show resolved Hide resolved

// Old owner should not be able to burn even if we pass in the new token
// account associated with the new owner.
let new_owner_token_account =
get_associated_token_address(&new_owner_pubkey, &print_edition.mint.pubkey());

let err = burn_edition(
&mut context,
print_edition.new_metadata_pubkey,
&payer,
print_edition.mint.pubkey(),
original_nft.mint.pubkey(),
new_owner_token_account,
master_edition.pubkey,
print_edition.new_edition_pubkey,
print_edition.pubkey,
)
.await
.unwrap_err();

// We've passed in the correct token account associated with the new owner but
// the old owner is not the current owner of the account so this shuld fail with
// InvalidOwner error.
assert_custom_error!(err, MetadataError::InvalidOwner);

// New owner can burn.
burn_edition(
&mut context,
print_edition.new_metadata_pubkey,
&new_owner,
print_edition.mint.pubkey(),
original_nft.mint.pubkey(),
new_owner_token_account,
master_edition.pubkey,
print_edition.new_edition_pubkey,
print_edition.pubkey,
)
.await
.unwrap();

// Metadata, Edition and token account are burned.
let md_account = context
.banks_client
.get_account(print_edition.new_metadata_pubkey)
.await
.unwrap();
let edition_account = context
.banks_client
.get_account(print_edition.new_edition_pubkey)
.await
.unwrap();
let token_account = context
.banks_client
.get_account(new_owner_token_account)
.await
.unwrap();
assert!(md_account.is_none());
assert!(edition_account.is_none());
assert!(token_account.is_none());
}

#[tokio::test]
async fn only_owner_can_burn() {
let mut context = program_test().start_with_context().await;
Expand Down
35 changes: 35 additions & 0 deletions token-metadata/program/tests/utils/edition_marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ use solana_program_test::BanksClientError;
use solana_sdk::{
pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, transaction::Transaction,
};
use spl_associated_token_account::{
get_associated_token_address, instruction::create_associated_token_account,
};

#[derive(Debug)]
pub struct EditionMarker {
Expand Down Expand Up @@ -249,4 +252,36 @@ impl EditionMarker {

context.banks_client.process_transaction(tx).await
}

pub async fn transfer(
&mut self,
context: &mut ProgramTestContext,
new_owner: &Pubkey,
) -> Result<(), BanksClientError> {
let new_owner_token_account = get_associated_token_address(new_owner, &self.mint.pubkey());
let create_token_account_ix = create_associated_token_account(
&context.payer.pubkey(),
new_owner,
&self.mint.pubkey(),
);

let transfer_ix = spl_token::instruction::transfer(
&spl_token::id(),
&self.token.pubkey(),
&new_owner_token_account,
&context.payer.pubkey(),
&[],
1,
)
.unwrap();

let transfer_tx = Transaction::new_signed_with_payer(
&[create_token_account_ix, transfer_ix],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);

context.banks_client.process_transaction(transfer_tx).await
}
}