Skip to content

Commit

Permalink
114 - Adds destroy nfts transaction and associated burn command to cli (
Browse files Browse the repository at this point in the history
#169)

* added destroy nfts transaction and associated burn command to cli

* Fixed variadic argument

Co-authored-by: Peter Siemens <[email protected]>

* Added fromBucketName argument

Co-authored-by: Peter Siemens <[email protected]>

* Updated description

Co-authored-by: Peter Siemens <[email protected]>

* Fixed comments, test description, command description, added burn command to list, added destroyNFTs transaction to generateEditionProject

* Fixed argument

* Fixed argument types, removed FreshmintError wrapping

* Updated console log of burn command

Co-authored-by: Loic Lesavre <[email protected]>
Co-authored-by: Peter Siemens <[email protected]>
  • Loading branch information
3 people authored Dec 13, 2022
1 parent 8d820b2 commit 67b9239
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 1 deletion.
30 changes: 30 additions & 0 deletions cadence/nfts/common/transactions/destroy_nfts.template.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import NonFungibleToken from {{{ imports.NonFungibleToken }}}
import {{ contractName }} from {{{ contractAddress }}}

/// This transaction withdraws multiple NFTs from the signer's collection and destroys them.
///
transaction(ids: [UInt64], fromBucketName: String?) {

/// A reference to the signer's {{ contractName }} collection.
///
let collectionRef: &{{ contractName }}.Collection

prepare(signer: AuthAccount) {

// Derive the collection path from the bucket name
let collectionName = {{ contractName }}.makeCollectionName(bucketName: fromBucketName)
let collectionStoragePath = {{ contractName }}.getStoragePath(suffix: collectionName)

self.collectionRef = signer.borrow<&{{ contractName }}.Collection>(from: collectionStoragePath)
?? panic("failed to borrow collection")
}

execute {
for id in ids {
// withdraw the NFT from the signers's collection
let nft <- self.collectionRef.withdraw(withdrawID: id)

destroy nft
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
teardownEmulator,
} from '../testHelpers';

describe('Transfer NFTs', () => {
describe('Common NFT tests', () => {
const contract = new StandardNFTContract({
name: 'StandardNFT_Transfer_Test',
schema: getTestSchema(),
Expand Down Expand Up @@ -115,4 +115,44 @@ describe('Transfer NFTs', () => {
// The 3 NFTs should now be in the new account
expect([onChainNFT1.id, onChainNFT2.id, onChainNFT3.id]).toEqual([nft1.id, nft2.id, nft3.id]);
});

it('should destroy three NFTs', async () => {
// Create a new account and set up an NFT collection
const account = await createAccount();
await client.send(contract.setupCollection(account.authorizer));

// Mint 3 new NFTs
const [nft1, nft2, nft3] = await client.send(contract.mintNFTs(nfts.generate(3)));

// Destroy the 3 NFTs
await client.send(contract.destroyNFTs([nft1.id, nft2.id, nft3.id]));

const onChainNFT1 = await client.query(contract.getNFT(account.address, nft1.id));
const onChainNFT2 = await client.query(contract.getNFT(account.address, nft2.id));
const onChainNFT3 = await client.query(contract.getNFT(account.address, nft3.id));

// The 3 NFTs should be null
expect([onChainNFT1, onChainNFT2, onChainNFT3]).toEqual([null, null, null]);
});

it('should destroy three NFTs from a custom bucket', async () => {
// Create a new account and set up an NFT collection
const account = await createAccount();
await client.send(contract.setupCollection(account.authorizer));

const bucketName = 'foo';

// Mint 3 new NFTs
const [nft1, nft2, nft3] = await client.send(contract.mintNFTs(nfts.generate(3), bucketName));

// Destroy the 3 NFTs
await client.send(contract.destroyNFTs([nft1.id, nft2.id, nft3.id], bucketName));

const onChainNFT1 = await client.query(contract.getNFT(account.address, nft1.id));
const onChainNFT2 = await client.query(contract.getNFT(account.address, nft2.id));
const onChainNFT3 = await client.query(contract.getNFT(account.address, nft3.id));

// The 3 NFTs should be null
expect([onChainNFT1, onChainNFT2, onChainNFT3]).toEqual([null, null, null]);
});
});
17 changes: 17 additions & 0 deletions packages/core/contracts/NFTContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,23 @@ export abstract class NFTContract {
}, Transaction.VoidResult);
}

destroyNFTs(ids: string[], fromBucket?: string): Transaction<void> {
return new Transaction(({ imports }: FreshmintConfig) => {
const script = CommonNFTGenerator.destroyNFTs({
imports,
contractName: this.name,
contractAddress: this.getAddress(),
});

return {
script,
args: [fcl.arg(ids, t.Array(t.UInt64)), fcl.arg(fromBucket, t.Optional(t.String))],
computeLimit: 9999,
signers: this.getSigners(),
};
}, Transaction.VoidResult);
}

setupCollection(authorizer: TransactionAuthorizer): Transaction<void> {
return new Transaction(({ imports }: FreshmintConfig) => {
const script = CommonNFTGenerator.setupCollection({
Expand Down
16 changes: 16 additions & 0 deletions packages/core/generators/CommonNFTGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ export class CommonNFTGenerator extends TemplateGenerator {
});
}

static destroyNFTs({
imports,
contractName,
contractAddress,
}: {
imports: ContractImports;
contractName: string;
contractAddress: string;
}): string {
return this.generate(require('../../../cadence/nfts/common/transactions/destroy_nfts.template.cdc'), {
imports,
contractName,
contractAddress,
});
}

static setupCollection({
imports,
contractName,
Expand Down
32 changes: 32 additions & 0 deletions packages/freshmint/commands/burn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Command } from 'commander';
import ora from 'ora';
import chalk from 'chalk';

import { FlowGateway, FlowNetwork } from '../flow';
import { loadConfig } from '../config';

export default new Command('burn')
.argument('<ids...>', 'The IDs of NFTs to destroy (e.g. 3425 1235 4524 216661).')
.description('burn (i.e. destroy) one or more NFTs')
.option('-n, --network <network>', "Network to use. Either 'emulator', 'testnet' or 'mainnet'", 'emulator')
.action(destroyNFTs);

async function destroyNFTs(ids: string[], { network }: { network: FlowNetwork }) {
const config = await loadConfig();

const flow = new FlowGateway(network, config.getContractAccount(network));

const spinner = ora();

console.log(chalk.gray('\n> flow transactions send ./cadence/transactions/destroy_nfts.cdc <...>\n'));

spinner.start(`Destroying the NFTs...`);

await flow.destroyNFTs(ids);

if (ids.length == 1) {
spinner.succeed(`1 NFT was destroyed.`);
} else {
spinner.succeed(`${ids.length} NFTs were destroyed.`);
}
}
7 changes: 7 additions & 0 deletions packages/freshmint/flow/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ export class FlowGateway {

return parseGetEditionResults(results);
}

async destroyNFTs(ids: string[]) {
return await this.cli.transaction('./cadence/transactions/destroy_nfts.cdc', this.signer, [
{ type: t.Array(t.UInt64), value: ids },
{ type: t.Optional(t.String), value: null }, // bucketName
]);
}
}

function parseMintResults(txOutput: any): MintResult[] {
Expand Down
16 changes: 16 additions & 0 deletions packages/freshmint/generate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ async function generateStandardProject(dir: string, contract: ContractConfig, in

await writeFile(path.resolve(dir, 'cadence/transactions/mint_with_claim_key.cdc'), mintWithClaimKeyTransaction);

const destroyNFTsTransaction = CommonNFTGenerator.destroyNFTs({
imports: shiftedImports,
contractName: contract.name,
contractAddress,
});

await writeFile(path.resolve(dir, 'cadence/transactions/destroy_nfts.cdc'), destroyNFTsTransaction);

if (includeCSVFile) {
await createNFTsCSVFile(dir);
}
Expand Down Expand Up @@ -160,6 +168,14 @@ async function generateEditionProject(dir: string, contract: ContractConfig, inc

await writeFile(path.resolve(dir, 'cadence/transactions/mint_with_claim_key.cdc'), mintWithClaimKeyTransaction);

const destroyNFTsTransaction = CommonNFTGenerator.destroyNFTs({
imports: shiftedImports,
contractName: contract.name,
contractAddress,
});

await writeFile(path.resolve(dir, 'cadence/transactions/destroy_nfts.cdc'), destroyNFTsTransaction);

if (includeCSVFile) {
await createEditionsCSVFile(dir);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/freshmint/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import start from './commands/start';
import dev from './commands/dev';
import deploy from './commands/deploy';
import mint from './commands/mint';
import burn from './commands/burn';
import startDrop from './commands/start-drop';
import stopDrop from './commands/stop-drop';
import gen from './commands/gen';
Expand All @@ -36,6 +37,7 @@ async function main() {
addCommand(dev);
addCommand(deploy);
addCommand(mint);
addCommand(burn);
addCommand(startDrop);
addCommand(stopDrop);
addCommand(gen);
Expand Down

0 comments on commit 67b9239

Please sign in to comment.