Mint a video NFT
Livepeer is blockchain agnostic. The example below shows how to mint a video NFT
using either wagmi
for EVM-compatible blockchains or
aptos
for the Aptos blockchain. However, you can use
Livepeer to mint NFTs with any blockchain by following the chain-specific NFT
standards.
Adding Dependencies
We first add the required dependencies using npm
(or your preferred package
manager).
Ethereum
Aptos
npm i wagmi ethers
We also use RainbowKit to show the connect wallet button, but this can be replaced by any wallet connection provider (e.g. Family’s ConnectKit). You can install this with:
npm i @rainbow-me/rainbowkit
Setting Up Ethereum and Aptos Providers
Ethereum
Aptos
We create both a new React client (using a CORS-protected API key)
and a wagmi
client which is configured to interact with our demo NFT contract on the
Polygon Mumbai chain.
This could be replaced with any EIP-721 or EIP-1155
contract on an EVM-compatible chain.
import {
LivepeerConfig,
createReactClient,
studioProvider,
} from '@livepeer/react';
import { WagmiConfig, chain, createClient } from 'wagmi';
const wagmiClient = ...; // set up the wagmi client with RainbowKit or ConnectKit
const livepeerClient = createReactClient({
provider: studioProvider({
apiKey: process.env.NEXT_PUBLIC_STUDIO_API_KEY,
}),
});
function App() {
return (
<WagmiConfig client={wagmiClient}>
<RainbowKitProvider {...}>
<LivepeerConfig client={livepeerClient}>
<CreateAndViewAsset />
</LivepeerConfig>
</RainbowKitProvider>
</WagmiConfig>
);
}
Add Connect Wallet Button
Ethereum
Aptos
Now that our providers are set up, we add a connect button which “logs in” a user using their wallet. We use
RainbowKit for the wallet connection flow. It integrates easily with wagmi
hooks, as well as WalletConnect and Metamask
to support a number of popular wallets.
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { useAccount } from 'wagmi';
export const WagmiNft = () => {
const { address } = useAccount();
return (
<div>
<ConnectButton />
</div>
);
};
Upload asset to IPFS
We then add a feature to let a user upload the asset to IPFS. Under the hood, the livepeer provider will upload the asset file to IPFS, then generate ERC-721 compatible metadata in IPFS which points to that asset’s CID.
Ethereum
Aptos
In this example, the asset ID is hardcoded in the component for simplicity, but could be dynamic (see the WagmiNft component used for this page, which uses the query string to get the asset ID).
import { useAsset, useUpdateAsset } from '@livepeer/react';
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { useMemo } from 'react';
import { useAccount } from 'wagmi';
const assetId = '64d3ddee-c44b-4c9c-8739-c3c530d6dfea';
export const WagmiNft = () => {
const { address } = useAccount();
const { mutate: updateAsset, status: updateStatus } = useUpdateAsset({
assetId,
storage: {
ipfs: true,
// metadata overrides can be added here
// see the source code behind this example
},
});
return (
<div>
<ConnectButton />
{address && assetId && (
<>
<p>{assetId}</p>
<button
onClick={() => {
updateAsset?.();
}}
>
Upload to IPFS
</button>
</>
)}
</div>
);
};
Here is an example of the ERC-721 compatible metadata which will be created in IPFS. The metadata can also be customized to override any of these default fields!
{
"name": "Spinning Earth",
"description": "The Earth is spinning in this amazing video, and the camera is still.",
"animation_url": "ipfs://bafybeiar26nqkdtiyrzbaxwcdm7zkr2o36xljqskdvg6z6ugwlmpkdhamy/?loop=1&v=efea4eqe0ottx346",
"external_url": "https://lvpr.tv/?muted=0&v=efea4eqe0ottx346",
"image": "ipfs://bafkreidmlgpjoxgvefhid2xjyqjnpmjjmq47yyrcm6ifvoovclty7sm4wm",
"properties": {
"com.livepeer.playbackId": "efea4eqe0ottx346",
"video": "ipfs://bafybeiew466bk3caift2gsnzeb23qmzmpqnim32utahanj5f5ks2ycvk7y"
}
}
Mint a Video NFT
We can now use the NFT metadata CID to mint a video NFT! After the transaction is successful, we show a link to a blockchain explorer so the user can see the blockchain confirmation.
Ethereum
Aptos
In this example, we rely on usePrepareContractWrite
to write to our demo
Polygon Mumbai NFT contract. This could be
replaced by ethers
or another library, but wagmi
hooks make it easy
to read/write with React.
import { useAsset, useUpdateAsset } from '@livepeer/react';
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { useRouter } from 'next/router';
import { useMemo } from 'react';
import { useAccount, useContractWrite, usePrepareContractWrite } from 'wagmi';
// The demo NFT contract ABI (exported as `const`)
// See: https://wagmi.sh/docs/typescript
import { videoNftAbi } from './videoNftAbi';
export const WagmiNft = () => {
const { address } = useAccount();
const router = useRouter();
const assetId = useMemo(
() => (router?.query?.id ? String(router?.query?.id) : undefined),
[router?.query],
);
const { data: asset } = useAsset({
assetId,
enabled: assetId?.length === 36,
refetchInterval: (asset) =>
asset?.storage?.status?.phase !== 'ready' ? 5000 : false,
});
const { mutate: updateAsset } = useUpdateAsset(
asset
? {
assetId: asset.id,
storage: {
ipfs: true,
metadata: {
name,
description,
},
},
}
: null,
);
const { config } = usePrepareContractWrite({
// The demo NFT contract address on Polygon Mumbai
address: '0xA4E1d8FE768d471B048F9d73ff90ED8fcCC03643',
abi: videoNftAbi,
// Function on the contract
functionName: 'mint',
// Arguments for the mint function
args:
address && asset?.storage?.ipfs?.nftMetadata?.url
? [address, asset?.storage?.ipfs?.nftMetadata?.url]
: undefined,
enabled: Boolean(address && asset?.storage?.ipfs?.nftMetadata?.url),
});
const {
data: contractWriteData,
isSuccess,
write,
error: contractWriteError,
} = useContractWrite(config);
return (
<div>
<ConnectButton />
{address && assetId && (
<>
<p>{assetId}</p>
{asset?.status?.phase === 'ready' &&
asset?.storage?.status?.phase !== 'ready' ? (
<button
onClick={() => {
updateAsset?.();
}}
>
Upload to IPFS
</button>
) : contractWriteData?.hash && isSuccess ? (
<a
target="_blank"
href={`https://mumbai.polygonscan.com/tx/${contractWriteData.hash}`}
>
<button>View Mint Transaction</button>
</a>
) : contractWriteError ? (
<p>{contractWriteError.message}</p>
) : asset?.storage?.status?.phase === 'ready' && write ? (
<button
onClick={() => {
write();
}}
>
Mint NFT
</button>
) : (
<></>
)}
</>
)}
</div>
);
};
Was this page helpful?