The Freshmint Node.js package provides the core pieces needed to deploy, mint and distribute NFTs on Flow from a Node.js application.
Table of Contents
- Installation
- Create & deploy an NFT contract
- Mint NFTs
- Distribute NFTs
- Metadata views
- Royalties
npm i freshmint@alpha
NFTs on Flow are defined by a Cadence contract that implements the Flow NFT interface.
Freshmint includes several NFT contract templates:
StandardNFTContract
BlindNFTContract
EditionNFTContract
BlindEditionNFTContract
Before creating a contract, you'll need to define its metadata schema. The schema is a list of fields that define the structure of an NFT and the data it contains. You'll then use the schema to generate your custom contract.
import { metadata } from 'freshmint';
// Create a schema with two fields:
// - "foo" is a string field
// - "bar" is an integer field
const schema = metadata.createSchema({
foo: metadata.String(),
bar: metadata.Int()
});
Freshmint ships with a default metadata schema. This is the minimum set of fields required to implement the Display metadata view.
For a full list of supported metadata views, see the metadata views section.
const defaultSchema = metadata.createSchema({
fields: {
name: metadata.String(),
description: metadata.String(),
thumbnail: metadata.IPFSImage()
},
// The default schema implements the MetadataViews.Display view.
views: (fields) => [
metadata.DisplayView({
name: fields.name,
description: fields.description,
thumbnail: fields.thumbnail
})
]
});
You can use the default schema as a base and extend it with additional fields:
import { metadata } from 'freshmint';
const schema = metadata.defaultSchema.extend({
foo: metadata.String(),
bar: metadata.Int()
});
Name | Identifier |
---|---|
String | string |
Bool | bool |
Int | int |
UInt | uint |
Fix64 | fix64 |
UFix64 | ufix64 |
IPFSFile | ipfs-file |
You can represent a metadata schema as a plain JavaScript object, which can easily be serialized to JSON, YAML or other formats.
The type
field must match a field identifier from the table above.
const rawSchema = {
fields: [
{
name: 'foo',
type: 'string'
},
{
name: 'bar',
type: 'int'
}
]
};
Use the parseSchema
function to convert a raw schema into a metadata.Schema
object:
import { metadata } from 'freshmint';
const schema = metadata.parseSchema(rawSchema);
import { StandardNFTContract, metadata } from 'freshmint';
const contract = new StandardNFTContract({
name: 'MyNFTContract',
schema: metadata.defaultSchema.extend({
foo: metadata.String(),
bar: metadata.Int()
})
});
If your contract is already deployed to an account, you can set the contract address in the constructor. Otherwise the address will be set when you deploy the contract.
const contract = new StandardNFTContract({
name: 'MyNFTContract',
schema: metadata.defaultSchema.extend({
foo: metadata.String(),
bar: metadata.Int()
}),
address: '0xf8d6e0586b0a20c7' // The address where your contract is deployed
});
You will need to configure the contract owner before you can deploy the contract or mint NFTs. The owner is the account that will mint, reveal and manage your NFTs.
The owner is defined as a TransactionAuthorizer
, an object that can authorize transactions for a specific Flow account.
The snippet below shows how to define an authorizer from an ECDSA private key.
import { TransactionAuthorizer } from 'freshmint';
import {
InMemoryECPrivateKey,
InMemoryECSigner,
HashAlgorithm,
SignatureAlgorithm
} from 'freshmint/crypto';
const privateKey = InMemoryECPrivateKey.fromHex(
process.env.PRIVATE_KEY,
SignatureAlgorithm.ECDSA_P256,
);
const signer = new InMemoryECSigner(privateKey, HashAlgorithm.SHA3_256);
const authorizer = new TransactionAuthorizer({
address: '0xf8d6e0586b0a20c7',
keyIndex: 0,
signer,
});
const contract = new StandardNFTContract(...);
// ...
contract.setOwner(authorizer);
You can optionally specify separate payer or proposer authorizers. This is useful if you would like to create multiple contracts, each with a separate owner, but pay for all fees from a central account.
An NFT contract has three authorizer roles:
Role | Actions |
---|---|
Owner | Mints, reveals and distributes NFTs. This is the only account with administrator access to the NFT contract. |
Payer | Pays fees for all transactions. |
Proposer | Signs as the proposer on all transactions. |
Note: the contract owner will sign for any role that is not explicitly set.
contract.setOwner(ownerAuthorizer);
contract.setPayer(payerAuthorizer);
contract.setProposer(proposerAuthorizer);
For convenience, you can pass the authorizers directly in the contract constructor:
const contract = new StandardNFTContract({
// ...
owner: ownerAuthorizer,
payer: payerAuthorizer
proposer: proposerAuthorizer
});
First create a FreshmintClient
instance using FCL as a base.
import * as fcl from '@onflow/fcl';
import { FreshmintClient, FreshmintConfig } from 'freshmint';
const client = FreshmintClient.fromFCL(fcl, FreshmintConfig.TESTNET);
Then deploy the contract using the deploy()
transaction method.
import { HashAlgorithm } from 'freshmint/crypto';
const contract = new StandardNFTContract(...);
// Specify a public key (with hash algorithm)
// to attach to the contract account.
const publicKey = privateKey.getPublicKey();
const hashAlgorithm = HashAlgorithm.SHA3_256;
// Note: contract.address will automatically update once the transaction succeeds.
const deployTransaction = contract.deploy(publicKey, hashAlgorithm);
const contractAddress = await client.send(deployTransaction);
The StandardNFTContract
allows you to mint simple one-of-a-kind NFTs.
import { StandardNFTContract, TransactionAuthorizer, metadata } from 'freshmint';
// Intialize your owner authorizer.
const owner = new TransactionAuthorizer(...);
const contract = new StandardNFTContract({
name: 'MyEditionNFTContract',
schema: metadata.defaultSchema,
owner
});
import * as fcl from '@onflow/fcl';
import { FreshmintClient, FreshmintConfig } from 'freshmint';
import { HashAlgorithm } from 'freshmint/crypto';
// Specify a public key (with hash algorithm)
// to attach to the contract account.
const publicKey = privateKey.getPublicKey();
const hashAlgorithm = HashAlgorithm.SHA3_256;
const client = FreshmintClient.fromFCL(fcl, FreshmintConfig.TESTNET);
const address = await client.send(contract.deploy(
publicKey,
hashAlgorithm
));
// Note: the metadata fields provided must match those
// defined in your metadata schema.
const nfts = [
{
name: 'NFT 1',
description: 'NFT 1 is awesome.',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
},
{
name: 'NFT 2',
description: 'NFT 2 is even better.',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
},
];
const mintedNFTs = await client.send(contract.mintNFTs(nfts));
console.log(mintedNFTs);
This will print:
[
{
id: "0",
metadata: {
name: "NFT 1",
description: "NFT 1 is awesome.",
thumbnail: "bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam"
},
transactionId: "20d0c77028d9a23347330956e8cd253fbe96a225e5cb42a4450fdc2e5cefa8c1"
},
{
id: "1",
metadata: {
name: "NFT 2",
description: "NFT 2 is even better.",
thumbnail: "bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam"
},
transactionId: "20d0c77028d9a23347330956e8cd253fbe96a225e5cb42a4450fdc2e5cefa8c1"
}
]
The EditionNFTContract
allows you to mint edition-based NFTs.
In this model, a contract can define multiple NFT editions. All NFTs in an edition share the same metadata; only their serial numbers are different.
import { EditionNFTContract, TransactionAuthorizer, metadata } from 'freshmint';
// Intialize your owner authorizer.
const owner = new TransactionAuthorizer(...);
const contract = new EditionNFTContract({
name: 'MyEditionNFTContract',
schema: metadata.defaultSchema,
owner
});
import * as fcl from '@onflow/fcl';
import { FreshmintClient, FreshmintConfig } from 'freshmint';
import { HashAlgorithm } from 'freshmint/crypto';
// Specify a public key (with hash algorithm)
// to attach to the contract account.
const publicKey = privateKey.getPublicKey();
const hashAlgorithm = HashAlgorithm.SHA3_256;
const client = FreshmintClient.fromFCL(fcl, FreshmintConfig.TESTNET);
const address = await client.send(contract.deploy(
publicKey,
hashAlgorithm
));
const edition1 = {
// This edition will contain 100 NFTs.
size: 100,
// Note: the metadata fields provided must match those
// defined in your metadata schema.
metadata: {
name: 'Edition 1',
description: 'This is the first edition',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
}
};
const edition2 = {
// This edition will contain 200 NFTs.
size: 200,
// Note: the metadata fields provided must match those
// defined in your metadata schema.
metadata: {
name: 'Edition 2',
description: 'This is the second edition',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
}
};
// This function submits a transaction that publishes
// this edition to the blockchain.
//
// Once you create an edition, its metadata becomes publicly viewable.
const editions = await client.send(contract.createEditions([edition1, edition2]));
console.log(editions);
This will print:
[
{
id: '0',
size: 100,
metadata: {
name: 'Edition 1',
description: 'This is the first edition',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
}
},
{
id: '1',
size: 200,
metadata: {
name: 'Edition 2',
description: 'This is the second edition',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
}
}
]
The mintNFTs
function prepares a transaction to mint NFTs into an existing edition.
It requires two arguments:
editionId
is the ID of the edition to mint into.count
is the number of NFTs to mint. For smaller editions, you may be able mint the entire edition in a single transaction, but in most cases you will need to mint in smaller batches.
// This example shows how to mint editions into separate buckets.
//
// By default, Freshmint will mint all NFTs into a single collection
// on the minter's account. By specifying a bucket, you can split
// your minted NFTs into separate collections (i.e. buckets) in the same account.
//
// In the case of editions, this allows you to sell or distribute each edition
// separately, rather than mixing all editions into a single collection
for (const edition of editions) {
const mintedNFTs = await client.send(contract.mintNFTs({
editionId: edition.id,
// Mint NFTs in batches of 10.
count: 10,
}));
console.log(mintedNFTs);
}
This will print:
[
{
id: '0',
editionId: '0',
editionSerial: '1',
transactionId: 'e648c662c3eb8550030c95c0dcc01d6d179925b9fc33fbaabe90715555d78ead'
},
{
id: '1',
editionId: '0',
editionSerial: '2',
transactionId: 'e648c662c3eb8550030c95c0dcc01d6d179925b9fc33fbaabe90715555d78ead'
},
...
]
This example shows how to mint editions into separate buckets.
By default, Freshmint will mint all NFTs into a single collection on the minter's account. By specifying a bucket name, you can split your minted NFTs into separate collections (i.e. buckets) in the same account.
In the case of editions, this allows you to distribute each edition in a separate sale, rather than mixing all editions into a single collection.
const mintedNFTs = await client.send(contract.mintNFTs({
editionId: '1',
count: 10,
// Mint each edition into its own bucket.
bucket: 'edition-1-bucket'
}));
Use a BlindNFTContract
to create NFTs that can be blindly minted.
In a blind mint, NFTs are initially minted as partial objects with their metadata hidden.
The contract owner can then reveal the metadata at a later point in time.
Freshmint implements blind minting using two separate mint and reveal transactions:
- The first transaction mints an NFT in its hidden form. A hidden NFT contains a SHA256 hash of the complete metadata that can later be used to verify the integrity of the revealed metadata.
- The second transaction publishes the NFT metadata to the blockchain. The hidden NFT is converted into a full NFT containing a complete on-chain metadata record.
import { BlindNFTContract, TransactionAuthorizer, metadata } from 'freshmint';
// Intialize your owner authorizer.
const owner = new TransactionAuthorizer(...);
const contract = new BlindNFTContract({
name: 'MyNFTContract',
schema: metadata.defaultSchema,
owner
});
import * as fcl from '@onflow/fcl';
import { FreshmintClient, FreshmintConfig } from 'freshmint';
import { HashAlgorithm } from 'freshmint/crypto';
// Specify a public key (with hash algorithm)
// to attach to the contract account.
const publicKey = privateKey.getPublicKey();
const hashAlgorithm = HashAlgorithm.SHA3_256;
// Specify the IPFS hash of an image (JPEG, PNG, etc)
// to be used as a placeholder for hidden NFTs.
const placeholderImage = 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam';
const client = FreshmintClient.fromFCL(fcl, FreshmintConfig.TESTNET);
const address = await client.send(contract.deploy(
publicKey,
hashAlgorithm,
placeholderImage
));
// Note: the metadata fields provided must match those
// defined in your metadata schema.
const nfts = [
{
name: 'NFT 1',
description: 'NFT 1 is awesome.',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
},
{
name: 'NFT 2',
description: 'NFT 2 is even better.',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
},
];
const mintedNFTs = await client.send(contract.mintNFTs(nfts));
console.log(mintedNFTs);
This will print:
[
{
id: "0",
metadata: {
name: "NFT 1",
description: "NFT 1 is awesome.",
thumbnail: "bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam"
},
metadataHash: "ed94560233ee34cf059e846560b43b462d0337e21d563f668404ee4cee407c97",
metadataSalt: "727ca86ae4a338f21e83ec330f490bcf",
transactionId: "20d0c77028d9a23347330956e8cd253fbe96a225e5cb42a4450fdc2e5cefa8c1"
},
{
id: "1",
metadata: {
name: "NFT 2",
description: "NFT 2 is even better.",
thumbnail: "bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam"
},
metadataHash: "504b154e868692932e2ef77900459915d3ab97be6150b2eac03c65b233cfbb8c",
metadataSalt: "18087aeaa388597b81cafdf4d2f6d81f",
transactionId: "20d0c77028d9a23347330956e8cd253fbe96a225e5cb42a4450fdc2e5cefa8c1"
}
]
The mintNFTs
function generates a unique salt for each minted NFT.
The salt is used to compute the metadata hash and prevents users from hash-grinding
against a known set of possible metadata values.
Important: you must save the id
, metadata
and metadataSalt
fields for each NFT.
You'll need them to later reveal the NFTs.
Blind NFTs are often minted in a random order. To randomize your mint,
shuffle your input array before calling mintNFTs()
.
Freshmint does not support automatic randomization.
It's important to note that this will only randomize the NFT metadata. The minted NFT IDs will still be sequential (i.e. 0, 1, 2, etc).
You can reveal your NFTs at any time. You also have the option to reveal NFTs one by one, all at once, or in batches.
const nft0 = {
id: '0',
metadata: {
name: 'NFT 1',
description: 'NFT 1 is awesome.',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
},
metadataSalt: '727ca86ae4a338f21e83ec330f490bcf',
}
const nft1 = {
id: '1',
metadata: {
name: 'NFT 2',
description: 'NFT 2 is awesome.',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
},
metadataSalt: '18087aeaa388597b81cafdf4d2f6d81f',
}
// Reveal a single NFT
await client.send(contract.revealNFTs([nft0]));
// Reveal multiple NFTs
await client.send(contract.revealNFTs([nft0, nft1]));
The BlindEditionNFTContract
allows you to mint edition-based NFTs
that are hidden at mint time and revealed at a later date.
In this model, a contract can define multiple editions. All NFTs in an edition share the same metadata; only their serial numbers are different.
import { BlindEditionNFTContract, TransactionAuthorizer, metadata } from 'freshmint';
// Intialize your owner authorizer.
const owner = new TransactionAuthorizer(...);
const contract = new BlindEditionNFTContract({
name: 'MyEditionNFTContract',
schema: metadata.defaultSchema,
owner
});
import * as fcl from '@onflow/fcl';
import { FreshmintClient, FreshmintConfig } from 'freshmint';
import { HashAlgorithm } from 'freshmint/crypto';
// Specify a public key (with hash algorithm)
// to attach to the contract account.
const publicKey = privateKey.getPublicKey();
const hashAlgorithm = HashAlgorithm.SHA3_256;
// Specify the IPFS hash of an image (JPEG, PNG, etc)
// to be used as a placeholder for hidden NFTs.
const placeholderImage = 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam';
const client = FreshmintClient.fromFCL(fcl, FreshmintConfig.TESTNET);
const address = await client.send(contract.deploy(
publicKey,
hashAlgorithm,
placeholderImage
));
const edition1 = {
// This edition will contain 5 NFTs.
size: 5,
// Note: the metadata fields provided must match those
// defined in your metadata schema.
metadata: {
name: 'Edition 1',
description: 'This is the first edition',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
}
};
const edition2 = {
// This edition will contain 5 NFTs.
size: 5,
// Note: the metadata fields provided must match those
// defined in your metadata schema.
metadata: {
name: 'Edition 2',
description: 'This is the second edition',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
}
};
// This function submits a transaction that publishes
// this edition to the blockchain.
//
// Once you create an edition, its metadata becomes publicly viewable.
const editions = await client.send(contract.createEditions([edition1, edition2]));
console.log(editions);
This will print:
[
{
id: '0',
size: 5,
metadata: {
name: 'Edition 1',
description: 'This is the first edition',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
},
nfts: [
{ editionId: '0', editionSerial: '1' },
{ editionId: '0', editionSerial: '2' },
{ editionId: '0', editionSerial: '3' },
{ editionId: '0', editionSerial: '4' },
{ editionId: '0', editionSerial: '5' },
]
},
{
id: '1',
size: 5,
metadata: {
name: 'Edition 2',
description: 'This is the second edition',
thumbnail: 'bafybeidlkqhddsjrdue7y3dy27pu5d7ydyemcls4z24szlyik3we7vqvam',
},
nfts: [
{ editionId: '1', editionSerial: '1' },
{ editionId: '1', editionSerial: '2' },
{ editionId: '1', editionSerial: '3' },
{ editionId: '1', editionSerial: '4' },
{ editionId: '1', editionSerial: '5' },
]
}
]
You can mint NFTs to any existing edition. The input to each NFT is simply its edition ID and serial number.
However, there are several important steps you need to take to avoid leaking edition contents before they are revealed.
-
Always randomize the minting order.
This prevents users from being able to guess an NFT's serial number before it is revealed.
-
If you want to mix scramble multiple editions, always mint their NFTs in a mixed batch, rather than by edition.
This prevents users from being able to guess an NFT's edition before it is revealed.
// This example shows how to mint editions into separate buckets.
import shuffle from 'your-secure-randomization-lib';
for (const edition of editions) {
// As mentioned above, ALWAYS randomize the mint order.
const randomizedNFTs = shuffle(edition.nfts);
// Mint each edition into its own bucket.
const mintedNFTs = await client.send(contract.mintNFTs(
randomizedNFTs,
{ bucket: edition.id }
));
console.log(mintedNFTs);
}
This will print:
[
{
id: '0',
editionId: '0',
editionSerial: '3',
editionHash: 'ab17379badad7bcf91885104f449a679b4cc68d1e3ccd527c6c7b922d0ae2655',
editionSalt: '6d8f193cab051793a1864bb8d082cf32',
transactionId: 'e648c662c3eb8550030c95c0dcc01d6d179925b9fc33fbaabe90715555d78ead'
},
{
id: '1',
editionId: '0',
editionSerial: '1',
editionHash: '10a315ec07b64935cebe82f14cdc5d7320ad941db3a14ad998305851d75c2119',
editionSalt: '8c658bba126995b1f99b8c9af374716f',
transactionId: 'e648c662c3eb8550030c95c0dcc01d6d179925b9fc33fbaabe90715555d78ead'
},
...
]
Reveal edition NFTs by publishing their edition ID, serial number and unique salt.
const nft0 = {
id: '0',
editionId: '1',
editionSerial: '3',
editionSalt: '6d8f193cab051793a1864bb8d082cf32',
};
const nft1 = {
id: '1',
editionId: '0',
editionSerial: '3',
editionSalt: '8c658bba126995b1f99b8c9af374716f',
};
// Reveal a single NFT
await client.send(contract.revealNFT(nft0));
// Reveal multiple NFTs
await client.send(contract.revealNFTs([nft0, nft1]));
Freshmint provides several distribution methods that you can use after minting your NFTs.
In a claim sale, a user purchases an NFT from a contract but does not get to pick the specific NFT.
import * as fcl from '@onflow/fcl';
import {
FreshmintClient,
FreshmintConfig,
ClaimSaleContract,
StandardNFTContract
} from 'freshmint';
const client = FreshmintClient.fromFCL(fcl, FreshmintConfig.TESTNET);
const contract = new StandardNFTContract(...);
// After minting your NFTs...
const sale = new ClaimSaleContract(contract);
// Start a new claim sale for 10 FLOW
await client.send(sale.start({ id: "default", price: "10.0" }));
// Stop the claim sale.
// The unsold NFTs stay in the contract owner's account.
await client.send(sale.stop("default"));
When selling edition-based NFTs, you may want to allow users to claim each edition separately and at a different price.
import * as fcl from '@onflow/fcl';
import {
FreshmintClient,
FreshmintConfig,
ClaimSaleContract,
EditionNFTContract
} from 'freshmint';
const client = FreshmintClient.fromFCL(fcl, FreshmintConfig.TESTNET);
const contract = new EditionNFTContract(...);
const sale = new ClaimSale(contract);
// After creating and minting editions...
for (const edition in editions) {
// Specify the bucket to claim from. This should be the same bucket
// you used to mint that edition.
await client.send(sale.start({ id: edition.id, price: "10.0", bucket: edition.id }));
}
// Later, stop the claim sales:
for (const edition in editions) {
await client.send(sale.stop(edition.id));
}
In this strategy, each NFT is revealed immediately after it is claimed by a buyer. The NFTs are revealed one by one until all NFTs are claimed.
An individual user sees their revealed NFT shortly after they buy it.
This strategy assumes you are using the claim sale distribution method.
import * as fcl from '@onflow/fcl';
import { FreshmintClient, FreshmintConfig } from 'freshmint';
const client = FreshmintClient.fromFCL(fcl, FreshmintConfig.TESTNET);
// This implementation assumes you have the transaction ID
// of the buyer's claim transaction (i.e. by capturing on your frontend).
//
// An alternate and more robust implementation would subscribe
// to 'NFTClaimSale.Claimed' events and trigger a reveal.
async function revealAfterPurchase(transactionId) {
const { events } = await fcl.tx({ transationId }).onceSealed();
const claimEvent = events.find(
// Note: this is the event ID for testnet.
(event) => event.type === 'A.f6908f3ab6c14d81.NFTClaimSale.Claimed'
);
const nftId = claimEvent.data['nftID'];
// This implementation assumes that you have stored the NFT metadata
// and salt in your database.
const { metadata, metadataSalt } = await loadNFTFromDatabase(nftId);
await client.send(contract.reveal([{
id: nftId,
metadata,
metadataSalt
}]));
}
You can optionally configure a claim sale to be gated by an address-based allowlist.
Allowlists are referenced by a string name and stored as a resource in your minter account. You can create an allowlist before a sale, add addresses to it, and then attach it to the sale later.
The addToAllowlist
function builds a transaction that adds one or more addresses to an on-chain allowlist.
When calling addToAllowlist
for the first time with a new name,
Freshmint will automatically create the allowlist if it does not exist.
You can execute this transaction again to add more addresses to the list.
This example creates an allowlist with name early-access-users
and
adds accounts 0x0ae53cb6e3f42a79 and 0xf8d6e0586b0a20c7,
both of which will be allowed to claim 3 NFTs.
await client.send(
sale.addToAllowlist({
name: 'early-access-users',
addresses: ['0x0ae53cb6e3f42a79', '0xf8d6e0586b0a20c7'],
claims: 3, // Each account will be allowed to claim 3 NFTs
})
)
This example creates a sale and attaches the early-access-users
allowlist created above.
Only the the accounts 0x0ae53cb6e3f42a79 and 0xf8d6e0586b0a20c7 will be allowed to claim from the sale, up to a maximum of 3 NFTs each.
await client.send(sale.start({
id: 'default',
price: '10.0',
// Note: 'allowlist' is an optional argument.
//
// If omitted, the sale will be open to anybody and with no claim limits.
allowlist: 'early-access-users'
}));
Metadata views allow NFTs to return their metadata in a standardized way to consumers such as wallets, indexers and marketplaces. They were proposed in FLIP-0636 and implemented in the Flow NFT standard repository.
Freshmint allows you to specify views as part of your metadata schema. It will then generate the necessary Cadence code to attach the views to your contract.
import { metadata } from 'freshmint';
const defaultSchema = metadata.createSchema({
fields: {
name: metadata.String(),
description: metadata.String(),
thumbnail: metadata.IPFSImage()
},
// The views property returns a list of views
// to be attached to the contract.
views: (fields) => [
metadata.DisplayView({
name: fields.name,
description: fields.description,
thumbnail: fields.thumbnail
})
]
});
Freshmint has built-in support for the following metadata views.
The NFTCollectionDisplay
view returns a basic representation of the collection
that an NFT belongs to.
import { metadata } from 'freshmint';
const view = metadata.NFTCollectionDisplayView({
name: 'My NFT Collection',
description: 'This is my new NFT collection.',
url: 'http://my-nft-collection.com',
media: {
ipfs: 'bafkreicrfbblmaduqg2kmeqbymdifawex7rxqq2743mitmeia4zdybmmre',
type: 'image/jpeg'
}
})
The media object can either be an IPFS CID or an HTTP URL.
// Example: IPFS media file
const view = metadata.NFTCollectionDisplayView({
// ...
media: {
ipfs: 'bafkreicrfbblmaduqg2kmeqbymdifawex7rxqq2743mitmeia4zdybmmre',
type: 'image/jpeg'
}
})
// Alternatively, you can specify an IPFS CID and path as an object.
const view = metadata.NFTCollectionDisplayView({
// ...
media: {
ipfs: {
cid: 'bafkreicrfbblmaduqg2kmeqbymdifawex7rxqq2743mitmeia4zdybmmre',
path: 'banner.jpeg'
}
type: 'image/jpeg'
}
})
// Example: HTTP media file
const view = metadata.NFTCollectionDisplayView({
// ...
media: {
url: 'http://my-nft-collection.com/banner.jpeg',
type: 'image/jpeg'
}
})
Freshmint allows you to configure one or more royalty recipients who will receive a portion of the sales on all NFTs minted by your contract.
There are two steps to configuring royalties for your contract.
First, ensure that your schema contains the royalties metadata view. This view exposes your royalty information to wallets and marketplaces. Without it, your royalties will not be applied.
Note: metadata.defaultSchema
already contains the royalties view.
import { metadata } from 'freshmint';
const schema = metadata.createSchema({
// ...
views: (fields) => [
// ...
metadata.RoyaltiesView()
]
});
After you deploy your contract, you can use the setRoyalties
function
to set or update the royalty information for your contract.
You can call this function at any point to update your royalties. This function will update the royalty information for all NFTs minted by your contract, past and present, regardless of where they are stored.
const royalties = [
{
address: '0xf8d6e0586b0a20c7',
receiverPath: '/public/flowTokenReceiver',
cut: '0.03',
// Description is an optional field
description: '0.3% of sale proceeds go to 0xf8d6e0586b0a20c7 in FLOW.',
},
{
address: '0x0ae53cb6e3f42a79',
receiverPath: '/public/flowTokenReceiver',
cut: '0.05'
},
];
await client.send(contract.setRoyalties(royalties));