Skip to main content

On-chain

How do you put assets 100% on-chain.

What does 100% on-chain mean

An on-chain NFT is a Non-Fungible Token that stores its metadata directly on the blockchain. This means that all the information about the NFT, such as its image URL, animation URL, and other attributes, are encoded and stored within the blockchain itself. This contrasts with off-chain NFTs, which store metadata on external storage systems (like IPFS) and reference it from the blockchain.

We use base64 encoding to store images and other data directly on the blockchain. The tokenURI function, which typically returns a URL pointing to an off-chain JSON metadata file, instead returns the entire metadata, including the base64 encoded image and animation URLs, directly from the blockchain. This ensures that the NFT is entirely self-contained and not dependent on any external storage or services.

How do you do it?

One of the options in the sdk, when setting up metadata allows for minting with onchain assets. There is a size limit of approximately 42kb base64 encoded. The image data must be base64 encoded and turned into a data:uri see below as the example

Example

const tokenID = await nftTokenBuilder
.setImage(
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
"onchain"
)
.setDesc("This is my NFT")
.setName("My NFT")
.setAttributes({ key: "value", otherKey: "value" })
.mint(mintToAddress);

Size limits

There is a size limit for minting via the sdk and not the contract interface. Some L2s have lower block limits (15million vs 30million gas), that prevent us from minting an NFT in a single tx with the metadata for a combined image and animation_url size greater than 42kb. We do support arbitrary size at which point 2mb tends to be the upper limit supported by node infrastructure providers. A good rule of thumb is no larger than 500kb, and that mostly guarantees no matter the context the data will be retrievable from all generally available nodes. Under the hood we have a system of data contracts that store the additional data at low gas cost for storage and retrieval.

Advanced Example

In this example we show how to chunk and add together the boundaries for imageData that's larger than 42kb or 70kb that some mainnet and l2s will allow.

// After calling mint token with  on nftContract
// ....
const nftContract = contracts.ERC721TokenBaseFacet__factory.connect(
nftContractAddr,
signer
);
const boundaries = await nftContract.getTokenChunkBoundaries(imageData.length);
const [imageChunks] = await nftContract.getMissingTokenChunks(tokenId);
let start = boundaries[0].toNumber();
for (let i = 1; i < boundaries.length; i++) {
// if last chunk then end is length of string
const end =
i === boundaries.length - 1
? imageData.length
: boundaries[i + 1].add(start).toNumber();
if (imageChunks[i - 1].toNumber() === 1) {
console.log("start", start, "end", end, "token", tokenId);
const res = await(
await nftContract["addTokenImageChunk(uint256,string,uint256)"](
tokenId,
imageData.slice(start, end),
i
)
).wait();
gas = res.gasUsed.add(gas);
}
start = end;
}