Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@fresh-js/nft
Advanced tools
The Fresh NFT package provides the core pieces needed to deploy, mint and distribute NFTs on Flow. It also powers [Freshmint](https://github.com/packagelabs/freshmint).
The Fresh NFT package provides the core pieces needed to deploy, mint and distribute NFTs on Flow. It also powers Freshmint.
npm i @fresh-js/nft
An NFT collection is a set of NFTs that share the same type structure. A collection is defined by a Cadence contract that implements the Flow NFT interface. All NFTs in a collection are minted by the same contract.
Fresh NFT supports the following collection types:
Name | Metadata Format | Metadata Views Support | Blind Minting Support |
---|---|---|---|
OnChainCollection | On-chain | Yes | No |
OnChainBlindCollection | On-chain | Yes | Yes |
EditionBlindCollection | On-chain | Yes | Yes |
import { TestnetConfig } from '@fresh-js/core';
import { OnChainCollection, metadata } from '@fresh-js/nft';
const collection = new OnChainCollection({
config: TestnetConfig,
name: 'MyNFTContract',
address: '0xf8d6e0586b0a20c7', // Optional: will be set after call to deployContract()
schema: metadata.defaultSchema.extend({
foo: metadata.String(),
bar: metadata.Int()
}),
});
Read more: defining a metadata schema.
You will need to configure a collection owner before you can deploy a contract or mint NFTs. The owner is the account that will mint, reveal and manage your NFTs.
The owner is defined as an Authorizer
, 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 { Authorizer } from '@fresh-js/core';
import {
InMemoryECPrivateKey,
InMemoryECSigner,
HashAlgorithm,
SignatureAlgorithm
} from '@fresh-js/crypto';
const privateKey = InMemoryECPrivateKey.fromHex(
process.env.PRIVATE_KEY,
SignatureAlgorithm.ECDSA_P256,
);
const signer = new InMemoryECSigner(privateKey, HashAlgorithm.SHA3_256);
const authorizer = new Authorizer({
address: '0xf8d6e0586b0a20c7',
keyIndex: 0,
signer,
});
const collection = new OnChainCollection(...);
// ...
collection.setOwner(authorizer);
You can optionally specify separate payer or proposer authorizers. This is useful if you would like to create multiple collections, each with a separate owner, but pay for all fees from a central account.
An NFT collection 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 collection owner will sign for any role that is not explicitly set.
collection.setOwner(ownerAuthorizer);
collection.setPayer(payerAuthorizer);
collection.setProposer(proposerAuthorizer);
For convenience, you can pass the authorizers directly in the collection constructor:
const collection = new OnChainCollection({
// ...
owner: ownerAuthorizer,
payer: payerAuthorizer
proposer: proposerAuthorizer
});
Deploy a collection's contract using the deployContract()
method:
import { HashAlgorithm } from '@fresh-js/crypto';
const collection = new OnChainCollection(...);
// Specify a public key (with hash algorithm)
// to attach to the contract account.
const publicKey = privateKey.getPublicKey();
const hashAlgorithm = HashAlgorithm.SHA3_256;
// Note: the call to deployContract() will
// automatically update collection.address.
const contractAddress = await collection.deployContract(publicKey, hashAlgorithm);
A metadata schema defines the structure of an NFT collection.
Today, a schema is simply a list of field types. However, Fresh NFT may support more complex schema models in the future (e.g. sets and editions).
import { metadata } from '@fresh-js/nft';
// 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()
});
Fresh NFT defines a default schema. This is the minimum set of fields required to implement the Display metadata view.
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 starting place and extend it with additional fields.
import { metadata } from '@fresh-js/nft';
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 |
IPFSImage | ipfs-image |
You can define custom metadata fields using the defineField
function.
import * as t from '@onflow/types';
import { metadata } from '@fresh-js/nft';
const MyCustomFieldType = metadata.defineField({
id: 'my-custom-field-type',
label: 'My Custom Field Type',
cadenceType: t.String,
sampleValue: 'This is a custom field!',
})
const schema = metadata.createSchema({
foo: metadata.String(),
bar: MyCustomFieldType(),
});
You can represent a metadata schema as a raw JavaScript (or JSON) object.
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 '@fresh-js/nft';
const schema = metadata.parseSchema(rawSchema);
Use an OnChainBlindCollection
to create NFTs that can be blindly minted.
In a blind mint, NFTs are initially minted as partial objects with their metadata hidden.
The collection owner can then reveal the metadata at a later point in time.
Fresh NFT implements blind minting using two separate mint and reveal transactions:
import { TestnetConfig, Authorizer } from '@fresh-js/core';
import { OnChainBlindCollection, metadata } from '@fresh-js/nft';
// Intialize your owner authorizer.
const owner = new Authorizer(...);
const collection = new OnChainBlindCollection({
config: TestnetConfig,
name: 'MyNFTContract',
schema: metadata.defaultSchema,
owner,
});
import { HashAlgorithm } from '@fresh-js/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 address = await collection.deployContract(
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 collection.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()
.
Fresh NFT does not support automatic randomization at this point.
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 collection.revealNFTs([nft0]);
// Reveal multiple NFTs
await collection.revealNFTs([nft0, nft1]);
As a collection owner, there are multiple ways you can reveal your NFTs.
This is the simplest strategy. You can choose a point in time to reveal all NFTs at once (usually once the collection is sold out).
All users see their NFTs at the same time.
async function bulkReveal() {
// This implementation assumes that you have stored all NFT metadata
// and salt values in your database.
const nfts = await loadNFTsFromDatabase();
console.log(nfts);
// [
// {
// id: '0',
// metadata: { ... },
// metadataSalt: '727ca86ae4a338f21e83ec330f490bcf'
// },
// ...
// ]
await collection.revealNFTs(nfts);
}
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';
// 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 collection.reveal([{
id: nftId,
metadata,
metadataSalt
}]);
}
The EditionBlindCollection
allows developers to mint edition-based NFTs.
In this model, a collection can contain multiple editions.
All NFTs in an addition share the same metadata;
their only difference is their serial numbers.
import { TestnetConfig, Authorizer } from '@fresh-js/core';
import { EditionBlindCollection, metadata } from '@fresh-js/nft';
// Intialize your owner authorizer.
const owner = new Authorizer(...);
// An edition collection uses the same configuration as normal collections,
// including the metadata schema.
const collection = new EditionBlindCollection({
config: TestnetConfig,
name: 'MyEditionNFTContract',
schema: metadata.defaultSchema,
owner,
});
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, the metadata is publicly-viewable.
const nfts = await collection.createEditions([edition1, edition2]);
console.log(nfts);
This will print:
[
{ editionId: '0', editionSerial: '1' },
{ editionId: '0', editionSerial: '2' },
{ editionId: '0', editionSerial: '3' },
{ editionId: '0', editionSerial: '4' },
{ editionId: '0', editionSerial: '5' },
{ 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.
When creating 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.
import shuffle from 'your-secure-randomization-lib';
// Note: the metadata fields provided must match those
// defined in your metadata schema.
const nfts = [
{ editionId: '0', editionSerial: '1' },
{ editionId: '0', editionSerial: '2' },
{ editionId: '0', editionSerial: '3' },
{ editionId: '0', editionSerial: '4' },
{ editionId: '0', editionSerial: '5' },
{ editionId: '1', editionSerial: '1' },
{ editionId: '1', editionSerial: '2' },
{ editionId: '1', editionSerial: '3' },
{ editionId: '1', editionSerial: '4' },
{ editionId: '1', editionSerial: '5' },
];
// As mentioned above, ALWAYS randomize the mint order.
const randomizedNFTs = shuffle(nfts);
console.log(randomizedNFTs);
// [
// { editionId: '1', editionSerial: '3' },
// { editionId: '0', editionSerial: '2' },
// { editionId: '0', editionSerial: '1' },
// { editionId: '1', editionSerial: '4' },
// { editionId: '0', editionSerial: '4' },
// { editionId: '1', editionSerial: '5' },
// { editionId: '1', editionSerial: '2' },
// { editionId: '0', editionSerial: '3' },
// { editionId: '1', editionSerial: '1' },
// { editionId: '0', editionSerial: '5' },
// ]
const mintedNFTs = await collection.mintNFTs(randomizedNFTs);
console.log(mintedNFTs);
This will print:
[
{
id: '0',
editionId: '1',
editionSerial: '3',
editionHash: 'ab17379badad7bcf91885104f449a679b4cc68d1e3ccd527c6c7b922d0ae2655',
editionSalt: '6d8f193cab051793a1864bb8d082cf32',
transactionId: 'e648c662c3eb8550030c95c0dcc01d6d179925b9fc33fbaabe90715555d78ead'
},
{
id: '1',
editionId: '0',
editionSerial: '3',
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 collection.revealNFT(nft0);
// Reveal multiple NFTs
await collection.revealNFTs([nft0, nft1]);
Fresh NFT provides several distribution methods that you can use after minting your NFT collection.
In a claim sale, a user purchases an NFT from a collection but does not see the NFT until after the purchase.
import { ClaimSale } from '@fresh-js/nft';
const collection = new OnChainBlindCollection(...);
// After minting your NFTs...
const sale = new ClaimSale(collection);
// Start a new claim sale for 10 FLOW
await sale.start("10.0");
// Stop the claim sale.
// The unsold NFTs stay in the collection owner's account.
await sale.stop();
Coming soon!
FAQs
The Fresh NFT package provides the core pieces needed to deploy, mint and distribute NFTs on Flow. It also powers [Freshmint](https://github.com/packagelabs/freshmint).
The npm package @fresh-js/nft receives a total of 0 weekly downloads. As such, @fresh-js/nft popularity was classified as not popular.
We found that @fresh-js/nft demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.