Saltar al contenido principal

Uploading to IPFS with NFT.storage

In this tutorial, we choose IPFS, a decentralized storage provider, to host the selected photo and the metadata object.

We'll also be using NFT.storage to help upload directly to IPFS, through their HTTP API. You can sign up for a free API key on their website.

Selecting the photo

You need to select an existing photo from our gallery. To present a picker UI and retrieve the file path, use launchImageLibrary from the react-native-image-picker library.

info

In the example app, this is done within the NftMinter component where handleSelectImage is called on a button press.

We save the image path as state, to be used in the next step.

import {launchImageLibrary} from 'react-native-image-picker';

const photo = await launchImageLibrary({
selectionLimit: 1,
mediaType: 'photo',
});
const selectedPhoto = photo?.assets?.[0];
if (!selectedPhoto?.uri) {
console.warn('Selected photo not found');
return;
}
const imagePath = selectedPhoto.uri;

Upload the photo

Now that we have the image path, we need to upload the raw bytes of the file to IPFS, using the NFT.storage /upload endpoint.

The steps:

  1. Use the rn-fetch-blob library to read the image file into a Base 64 string.
  2. Convert to raw bytes by decoding the Base64 string with Buffer.
  3. Use fetch to send a request containing the image bytes to the upload endpoint.
info

In the example app, this is handled in a separate helper function uploadToIPFS, which is called later within the larger the mintNft function.

caution

During this step, you'll need to provide your own API key from NFT.storage. In the example app, the NFT_STORAGE_API_KEY value is set through an environment variable config, using the react-native-config library.

// Read the image file and get the base64 string.
const imageBytesInBase64: string = await RNFetchBlob.fs.readFile(
imagePath,
'base64',
);

// Convert base64 into raw bytes.
const bytes = Buffer.from(imageBytesInBase64, 'base64');

// Upload the image to IPFS by sending a POST request to the NFT.storage upload endpoint.
const headers = {
Accept: 'application/json',
Authorization: `Bearer ${Config.NFT_STORAGE_API_KEY}`,
};
const imageUpload = await fetch('https://api.nft.storage/upload', {
method: 'POST',
headers: {
...headers,
'Content-Type': 'image/jpg',
},
body: bytes,
});

const imageData = await imageUpload.json();
console.log(imageData.value.cid);

If successful, the imageData.value.cid will contain a valid CID (Content Identifier). This is a string that uniquely identifies your uploaded asset.

You can view your uploaded asset on an IPFS gateway by passing in the CID in the URL (e.g: https://ipfs.io/ipfs/<cid>). View an example of an uploaded photo on ipfs.io.

Uploading the metadata

Next, we need to construct a metadata object that conforms to the Metaplex NFT Standard, then upload it to the same /upload endpoint.

Metadata fields:

  • Name: The name of the NFT.
  • Description: A description of the NFT.
  • Image: A URL that hosts the photo. In this case, we use an ipfs.io URL with the CID of the uploaded photo.
info

In the example app, the metadata upload step is also handled within the uploadToIPFS function.

There is a slight difference that should be noted. The image field uses a precomputed CID for the photo, rather than waiting for the photo upload to finish. This is an optimization that is explained in the next section.

// Construct the metadata fields.
const metadata = JSON.stringify({
name,
description,
image: `https://ipfs.io/ipfs/${imageData.value.cid}`,
});
// Upload to IPFS
const metadataUpload = await fetch('https://api.nft.storage/upload', {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: metadata,
});

const metadataData = await metadataUpload.json();
console.log(metadataData.value.cid);

If successful, metadataData.value.cid will now contain a CID that points to a JSON object representing the NFT metadata. View an example of an uploaded metadata object.

To recap, we now have two CIDs that are viewable on IPFS. First, the CID of our uploaded photo, and second, the CID of JSON Metadata (which has a reference to the photo CID in the image field).

Precomputing the CID

You may notice in the example app, that during the upload step in uploadToIPFS we're able to precompute the CID of the photo asset before actually uploading it to IPFS. This is an optimization that allows us to construct and upload the metadata object, without waiting for the photo upload to complete and return the CID.

We take advantage of this by uploading both the photo and metadata asynchronously.

// Fire off both uploads aysnc
return Promise.all([
imageUpload.then(response => response.json()),
metadataUpload.then(response => response.json()),
]);
tip

This is made possible because CIDs are generated deterministically from the binary data of any given asset. This mean we can compute the CID of an asset before uploading it to IPFS.

To compute the CID from the bytes of a given asset, see the getCid function.

import {CID, hasher} from 'multiformats';
const crypto = require('crypto-browserify');

const SHA_256_CODE = 0x12;
const IPLD_RAW_BINARY_CODE = 0x55;

const getCid = async (bytes: Buffer) => {
const sha256 = hasher.from({
// As per multiformats table
// https://github.com/multiformats/multicodec/blob/master/table.csv#L9
name: 'sha2-256',
code: SHA_256_CODE,
encode: input =>
new Uint8Array(crypto.createHash('sha256').update(input).digest()),
});
const hash = await sha256.digest(bytes);
const cid = await CID.create(1, IPLD_RAW_BINARY_CODE, hash);

return cid;
};

ДимкаРеактнативный

🖋

katsuhira02

📝

Go!

EnglishMoji!