<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>4undRaiser</title>
        <link>https://paragraph.com/@4undraiser</link>
        <description>undefined</description>
        <lastBuildDate>Tue, 07 Apr 2026 00:52:48 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>4undRaiser</title>
            <url>https://storage.googleapis.com/papyrus_images/fe8cd23a38075ab026bb90a123ad885f4d91d71d2a4e1a36f92067dd3cb7bfe2.png</url>
            <link>https://paragraph.com/@4undraiser</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[SEI ZK Series Part 10 - How to build a fullstack nft market place on the sei blockchain]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-10-how-to-build-a-fullstack-nft-market-place-on-the-sei-blockchain</link>
            <guid>fhbqMT1i2nW3AHFkRQfz</guid>
            <pubDate>Tue, 17 Jun 2025 12:34:03 GMT</pubDate>
            <description><![CDATA[Building a Fullstack NFT Marketplace on Sei BlockchainIntroductionThe Sei blockchain is a high-performance Layer 1 blockchain designed for trading applications. With its EVM compatibility, we can leverage familiar Ethereum development tools while benefiting from Sei&apos;s high throughput and low latency. In this tutorial, we&apos;ll build a complete NFT marketplace that allows users to:Mint NFTsList NFTs for saleBuy NFTsView listed NFTsManage their NFT collectionPrerequisitesBefore we begin,...]]></description>
            <content:encoded><![CDATA[<h1 id="h-building-a-fullstack-nft-marketplace-on-sei-blockchain" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Building a Fullstack NFT Marketplace on Sei Blockchain</h1><h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>The Sei blockchain is a high-performance Layer 1 blockchain designed for trading applications. With its EVM compatibility, we can leverage familiar Ethereum development tools while benefiting from Sei&apos;s high throughput and low latency. In this tutorial, we&apos;ll build a complete NFT marketplace that allows users to:</p><ul><li><p>Mint NFTs</p></li><li><p>List NFTs for sale</p></li><li><p>Buy NFTs</p></li><li><p>View listed NFTs</p></li><li><p>Manage their NFT collection</p></li></ul><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before we begin, ensure you have the following installed:</p><ul><li><p>Node.js (v16 or higher)</p></li><li><p>npm or yarn</p></li><li><p>MetaMask wallet</p></li><li><p>Git</p></li></ul><h2 id="h-project-setup" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Project Setup</h2><p>Let&apos;s start by setting up our development environment:</p><ol><li><p>Create a new directory and initialize the project:</p></li></ol><pre data-type="codeBlock" text="mkdir sei-nft-marketplace
cd sei-nft-marketplace
npm init -y
"><code>mkdir sei<span class="hljs-operator">-</span>nft<span class="hljs-operator">-</span>marketplace
cd sei<span class="hljs-operator">-</span>nft<span class="hljs-operator">-</span>marketplace
npm init <span class="hljs-operator">-</span>y
</code></pre><ol><li><p>Install Hardhat and required dependencies:</p></li></ol><pre data-type="codeBlock" text="npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @openzeppelin/contracts dotenv
"><code>npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save<span class="hljs-operator">-</span>dev hardhat @nomicfoundation<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>toolbox @openzeppelin<span class="hljs-operator">/</span>contracts dotenv
</code></pre><ol><li><p>Initialize Hardhat:</p></li></ol><pre data-type="codeBlock" text="npx hardhat init
"><code>npx hardhat <span class="hljs-keyword">init</span>
</code></pre><p>Choose &quot;Create a JavaScript project&quot; when prompted.</p><ol><li><p>Create a <code>.env</code> file in the root directory:</p></li></ol><pre data-type="codeBlock" text="PRIVATE_KEY=your_wallet_private_key
SEI_RPC_URL=your_sei_rpc_url
"><code><span class="hljs-attr">PRIVATE_KEY</span>=your_wallet_private_key
<span class="hljs-attr">SEI_RPC_URL</span>=your_sei_rpc_url
</code></pre><h2 id="h-smart-contract-development" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Smart Contract Development</h2><p>We&apos;ll create two main smart contracts:</p><ol><li><p>NFT Contract (ERC721)</p></li><li><p>Marketplace Contract</p></li></ol><h3 id="h-1-nft-contract" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. NFT Contract</h3><p>Create a new file <code>contracts/SeiNFT.sol</code>:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import &quot;@openzeppelin/contracts/token/ERC721/ERC721.sol&quot;;
import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol&quot;;
import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;;

contract SeiNFT is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721(&quot;SeiNFT&quot;, &quot;SEI&quot;) Ownable(msg.sender) {}

    function mintNFT(address recipient, string memory tokenURI)
        public
        returns (uint256)
    {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);
        return newItemId;
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/ERC721.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/Counters.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SeiNFT</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721URIStorage</span>, <span class="hljs-title">Ownable</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">Counters</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Counters</span>.<span class="hljs-title">Counter</span>;
    Counters.Counter <span class="hljs-keyword">private</span> _tokenIds;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"SeiNFT"</span>, <span class="hljs-string">"SEI"</span></span>) <span class="hljs-title">Ownable</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span>.sender</span>) </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintNFT</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> recipient, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> tokenURI</span>)
        <span class="hljs-title"><span class="hljs-keyword">public</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)
    </span>{
        _tokenIds.increment();
        <span class="hljs-keyword">uint256</span> newItemId <span class="hljs-operator">=</span> _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);
        <span class="hljs-keyword">return</span> newItemId;
    }
}
</code></pre><h3 id="h-2-marketplace-contract" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Marketplace Contract</h3><p>Create a new file <code>contracts/NFTMarketplace.sol</code>:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import &quot;@openzeppelin/contracts/token/ERC721/IERC721.sol&quot;;
import &quot;@openzeppelin/contracts/security/ReentrancyGuard.sol&quot;;
import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;

contract NFTMarketplace is ReentrancyGuard, Ownable {
    struct Listing {
        address seller;
        uint256 price;
        bool isActive;
    }

    // Mapping from NFT contract address to token ID to Listing
    mapping(address =&gt; mapping(uint256 =&gt; Listing)) public listings;

    // Platform fee percentage (2%)
    uint256 public platformFee = 200;
    uint256 public constant BASIS_POINTS = 10000;

    event NFTListed(
        address indexed nftContract,
        uint256 indexed tokenId,
        address indexed seller,
        uint256 price
    );

    event NFTSold(
        address indexed nftContract,
        uint256 indexed tokenId,
        address indexed seller,
        address buyer,
        uint256 price
    );

    event NFTListingCancelled(
        address indexed nftContract,
        uint256 indexed tokenId,
        address indexed seller
    );

    constructor() Ownable(msg.sender) {}

    function listNFT(
        address nftContract,
        uint256 tokenId,
        uint256 price
    ) external nonReentrant {
        require(price &gt; 0, &quot;Price must be greater than 0&quot;);
        require(
            IERC721(nftContract).ownerOf(tokenId) == msg.sender,
            &quot;Not the owner&quot;
        );
        require(
            IERC721(nftContract).isApprovedForAll(msg.sender, address(this)),
            &quot;Contract not approved&quot;
        );

        listings[nftContract][tokenId] = Listing({
            seller: msg.sender,
            price: price,
            isActive: true
        });

        emit NFTListed(nftContract, tokenId, msg.sender, price);
    }

    function buyNFT(address nftContract, uint256 tokenId)
        external
        payable
        nonReentrant
    {
        Listing memory listing = listings[nftContract][tokenId];
        require(listing.isActive, &quot;Listing not active&quot;);
        require(msg.value &gt;= listing.price, &quot;Insufficient payment&quot;);

        uint256 platformFeeAmount = (listing.price * platformFee) / BASIS_POINTS;
        uint256 sellerAmount = listing.price - platformFeeAmount;

        // Transfer NFT to buyer
        IERC721(nftContract).transferFrom(
            listing.seller,
            msg.sender,
            tokenId
        );

        // Transfer payment to seller
        (bool sellerTransfer, ) = payable(listing.seller).call{
            value: sellerAmount
        }(&quot;&quot;);
        require(sellerTransfer, &quot;Seller transfer failed&quot;);

        // Transfer platform fee to owner
        (bool feeTransfer, ) = payable(owner()).call{
            value: platformFeeAmount
        }(&quot;&quot;);
        require(feeTransfer, &quot;Fee transfer failed&quot;);

        // Clear listing
        delete listings[nftContract][tokenId];

        emit NFTSold(
            nftContract,
            tokenId,
            listing.seller,
            msg.sender,
            listing.price
        );
    }

    function cancelListing(address nftContract, uint256 tokenId)
        external
        nonReentrant
    {
        Listing memory listing = listings[nftContract][tokenId];
        require(listing.seller == msg.sender, &quot;Not the seller&quot;);
        require(listing.isActive, &quot;Listing not active&quot;);

        delete listings[nftContract][tokenId];

        emit NFTListingCancelled(nftContract, tokenId, msg.sender);
    }

    function updatePlatformFee(uint256 newFee) external onlyOwner {
        require(newFee &lt;= 1000, &quot;Fee too high&quot;); // Max 10%
        platformFee = newFee;
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/IERC721.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/security/ReentrancyGuard.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">NFTMarketplace</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ReentrancyGuard</span>, <span class="hljs-title">Ownable</span> </span>{
    <span class="hljs-keyword">struct</span> <span class="hljs-title">Listing</span> {
        <span class="hljs-keyword">address</span> seller;
        <span class="hljs-keyword">uint256</span> price;
        <span class="hljs-keyword">bool</span> isActive;
    }

    <span class="hljs-comment">// Mapping from NFT contract address to token ID to Listing</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> Listing)) <span class="hljs-keyword">public</span> listings;

    <span class="hljs-comment">// Platform fee percentage (2%)</span>
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> platformFee <span class="hljs-operator">=</span> <span class="hljs-number">200</span>;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> BASIS_POINTS <span class="hljs-operator">=</span> <span class="hljs-number">10000</span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NFTListed</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> nftContract,
        <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> tokenId,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> seller,
        <span class="hljs-keyword">uint256</span> price
    </span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NFTSold</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> nftContract,
        <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> tokenId,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> seller,
        <span class="hljs-keyword">address</span> buyer,
        <span class="hljs-keyword">uint256</span> price
    </span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NFTListingCancelled</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> nftContract,
        <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> tokenId,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> seller
    </span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">Ownable</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span>.sender</span>) </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">listNFT</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> nftContract,
        <span class="hljs-keyword">uint256</span> tokenId,
        <span class="hljs-keyword">uint256</span> price
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">nonReentrant</span> </span>{
        <span class="hljs-built_in">require</span>(price <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Price must be greater than 0"</span>);
        <span class="hljs-built_in">require</span>(
            IERC721(nftContract).ownerOf(tokenId) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            <span class="hljs-string">"Not the owner"</span>
        );
        <span class="hljs-built_in">require</span>(
            IERC721(nftContract).isApprovedForAll(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>)),
            <span class="hljs-string">"Contract not approved"</span>
        );

        listings[nftContract][tokenId] <span class="hljs-operator">=</span> Listing({
            seller: <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            price: price,
            isActive: <span class="hljs-literal">true</span>
        });

        <span class="hljs-keyword">emit</span> NFTListed(nftContract, tokenId, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, price);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buyNFT</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> nftContract, <span class="hljs-keyword">uint256</span> tokenId</span>)
        <span class="hljs-title"><span class="hljs-keyword">external</span></span>
        <span class="hljs-title"><span class="hljs-keyword">payable</span></span>
        <span class="hljs-title">nonReentrant</span>
    </span>{
        Listing <span class="hljs-keyword">memory</span> listing <span class="hljs-operator">=</span> listings[nftContract][tokenId];
        <span class="hljs-built_in">require</span>(listing.isActive, <span class="hljs-string">"Listing not active"</span>);
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">></span><span class="hljs-operator">=</span> listing.price, <span class="hljs-string">"Insufficient payment"</span>);

        <span class="hljs-keyword">uint256</span> platformFeeAmount <span class="hljs-operator">=</span> (listing.price <span class="hljs-operator">*</span> platformFee) <span class="hljs-operator">/</span> BASIS_POINTS;
        <span class="hljs-keyword">uint256</span> sellerAmount <span class="hljs-operator">=</span> listing.price <span class="hljs-operator">-</span> platformFeeAmount;

        <span class="hljs-comment">// Transfer NFT to buyer</span>
        IERC721(nftContract).transferFrom(
            listing.seller,
            <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            tokenId
        );

        <span class="hljs-comment">// Transfer payment to seller</span>
        (<span class="hljs-keyword">bool</span> sellerTransfer, ) <span class="hljs-operator">=</span> <span class="hljs-keyword">payable</span>(listing.seller).<span class="hljs-built_in">call</span>{
            <span class="hljs-built_in">value</span>: sellerAmount
        }(<span class="hljs-string">""</span>);
        <span class="hljs-built_in">require</span>(sellerTransfer, <span class="hljs-string">"Seller transfer failed"</span>);

        <span class="hljs-comment">// Transfer platform fee to owner</span>
        (<span class="hljs-keyword">bool</span> feeTransfer, ) <span class="hljs-operator">=</span> <span class="hljs-keyword">payable</span>(owner()).<span class="hljs-built_in">call</span>{
            <span class="hljs-built_in">value</span>: platformFeeAmount
        }(<span class="hljs-string">""</span>);
        <span class="hljs-built_in">require</span>(feeTransfer, <span class="hljs-string">"Fee transfer failed"</span>);

        <span class="hljs-comment">// Clear listing</span>
        <span class="hljs-keyword">delete</span> listings[nftContract][tokenId];

        <span class="hljs-keyword">emit</span> NFTSold(
            nftContract,
            tokenId,
            listing.seller,
            <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            listing.price
        );
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">cancelListing</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> nftContract, <span class="hljs-keyword">uint256</span> tokenId</span>)
        <span class="hljs-title"><span class="hljs-keyword">external</span></span>
        <span class="hljs-title">nonReentrant</span>
    </span>{
        Listing <span class="hljs-keyword">memory</span> listing <span class="hljs-operator">=</span> listings[nftContract][tokenId];
        <span class="hljs-built_in">require</span>(listing.seller <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-string">"Not the seller"</span>);
        <span class="hljs-built_in">require</span>(listing.isActive, <span class="hljs-string">"Listing not active"</span>);

        <span class="hljs-keyword">delete</span> listings[nftContract][tokenId];

        <span class="hljs-keyword">emit</span> NFTListingCancelled(nftContract, tokenId, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updatePlatformFee</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> newFee</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-built_in">require</span>(newFee <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> <span class="hljs-number">1000</span>, <span class="hljs-string">"Fee too high"</span>); <span class="hljs-comment">// Max 10%</span>
        platformFee <span class="hljs-operator">=</span> newFee;
    }
}
</code></pre><h2 id="h-frontend-development" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Frontend Development</h2><p>For the frontend, we&apos;ll use React with ethers.js. Let&apos;s set up the frontend project:</p><ol><li><p>Create a new React project:</p></li></ol><pre data-type="codeBlock" text="npx create-react-app frontend
cd frontend
npm install ethers@5.7.2 @web3-react/core @web3-react/injected-connector axios
"><code>npx create<span class="hljs-operator">-</span>react<span class="hljs-operator">-</span>app frontend
cd frontend
npm install ethers@<span class="hljs-number">5.7</span><span class="hljs-number">.2</span> @web3<span class="hljs-operator">-</span>react<span class="hljs-operator">/</span>core @web3<span class="hljs-operator">-</span>react<span class="hljs-operator">/</span>injected<span class="hljs-operator">-</span>connector axios
</code></pre><ol><li><p>Create the main components:</p></li></ol><h3 id="h-appjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">App.js</h3><pre data-type="codeBlock" text="import React from &quot;react&quot;;
import { Web3ReactProvider } from &quot;@web3-react/core&quot;;
import { ethers } from &quot;ethers&quot;;
import { BrowserRouter as Router, Route, Switch } from &quot;react-router-dom&quot;;
import Navbar from &quot;./components/Navbar&quot;;
import Home from &quot;./components/Home&quot;;
import CreateNFT from &quot;./components/CreateNFT&quot;;
import MyNFTs from &quot;./components/MyNFTs&quot;;

function getLibrary(provider) {
  return new ethers.providers.Web3Provider(provider);
}

function App() {
  return (
    &lt;Web3ReactProvider getLibrary={getLibrary}&gt;
      &lt;Router&gt;
        &lt;div className=&quot;App&quot;&gt;
          &lt;Navbar /&gt;
          &lt;Switch&gt;
            &lt;Route exact path=&quot;/&quot; component={Home} /&gt;
            &lt;Route path=&quot;/create&quot; component={CreateNFT} /&gt;
            &lt;Route path=&quot;/my-nfts&quot; component={MyNFTs} /&gt;
          &lt;/Switch&gt;
        &lt;/div&gt;
      &lt;/Router&gt;
    &lt;/Web3ReactProvider&gt;
  );
}

export default App;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Web3ReactProvider</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@web3-react/core"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">BrowserRouter</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">Router</span>, <span class="hljs-title">Route</span>, <span class="hljs-title">Switch</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Navbar</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components/Navbar"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">Home</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components/Home"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">CreateNFT</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components/CreateNFT"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">MyNFTs</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components/MyNFTs"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLibrary</span>(<span class="hljs-params">provider</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(provider);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>Web3ReactProvider getLibrary<span class="hljs-operator">=</span>{getLibrary}<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>Router<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"App"</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Navbar <span class="hljs-operator">/</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Switch<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Route exact path<span class="hljs-operator">=</span><span class="hljs-string">"/"</span> component<span class="hljs-operator">=</span>{Home} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Route path<span class="hljs-operator">=</span><span class="hljs-string">"/create"</span> component<span class="hljs-operator">=</span>{CreateNFT} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Route path<span class="hljs-operator">=</span><span class="hljs-string">"/my-nfts"</span> component<span class="hljs-operator">=</span>{MyNFTs} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Switch<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Router<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Web3ReactProvider<span class="hljs-operator">></span>
  );
}

export default App;
</code></pre><h3 id="h-componentsnavbarjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">components/Navbar.js</h3><pre data-type="codeBlock" text="import React from &quot;react&quot;;
import { useWeb3React } from &quot;@web3-react/core&quot;;
import { InjectedConnector } from &quot;@web3-react/injected-connector&quot;;
import { Link } from &quot;react-router-dom&quot;;

const injected = new InjectedConnector({
  supportedChainIds: [713715], // Sei Testnet
});

function Navbar() {
  const { active, account, activate, deactivate } = useWeb3React();

  async function connect() {
    try {
      await activate(injected);
    } catch (error) {
      console.log(error);
    }
  }

  async function disconnect() {
    try {
      deactivate();
    } catch (error) {
      console.log(error);
    }
  }

  return (
    &lt;nav className=&quot;navbar&quot;&gt;
      &lt;div className=&quot;nav-brand&quot;&gt;
        &lt;Link to=&quot;/&quot;&gt;Sei NFT Marketplace&lt;/Link&gt;
      &lt;/div&gt;
      &lt;div className=&quot;nav-links&quot;&gt;
        &lt;Link to=&quot;/&quot;&gt;Home&lt;/Link&gt;
        &lt;Link to=&quot;/create&quot;&gt;Create NFT&lt;/Link&gt;
        &lt;Link to=&quot;/my-nfts&quot;&gt;My NFTs&lt;/Link&gt;
      &lt;/div&gt;
      &lt;div className=&quot;nav-connect&quot;&gt;
        {active ? (
          &lt;&gt;
            &lt;span&gt;{account}&lt;/span&gt;
            &lt;button onClick={disconnect}&gt;Disconnect&lt;/button&gt;
          &lt;/&gt;
        ) : (
          &lt;button onClick={connect}&gt;Connect Wallet&lt;/button&gt;
        )}
      &lt;/div&gt;
    &lt;/nav&gt;
  );
}

export default Navbar;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useWeb3React</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@web3-react/core"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">InjectedConnector</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@web3-react/injected-connector"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Link</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react-router-dom"</span>;

const injected <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> InjectedConnector({
  supportedChainIds: [<span class="hljs-number">713715</span>], <span class="hljs-comment">// Sei Testnet</span>
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Navbar</span>(<span class="hljs-params"></span>) </span>{
  const { active, account, activate, deactivate } <span class="hljs-operator">=</span> useWeb3React();

  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">connect</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">try</span> {
      await activate(injected);
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
      console.log(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    }
  }

  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">disconnect</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">try</span> {
      deactivate();
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
      console.log(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    }
  }

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>nav className<span class="hljs-operator">=</span><span class="hljs-string">"navbar"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"nav-brand"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Link to<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">></span>Sei NFT Marketplace<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"nav-links"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Link to<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">></span>Home<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Link to<span class="hljs-operator">=</span><span class="hljs-string">"/create"</span><span class="hljs-operator">></span>Create NFT<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Link to<span class="hljs-operator">=</span><span class="hljs-string">"/my-nfts"</span><span class="hljs-operator">></span>My NFTs<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"nav-connect"</span><span class="hljs-operator">></span>
        {active ? (
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>span<span class="hljs-operator">></span>{account}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>span<span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>button onClick<span class="hljs-operator">=</span>{disconnect}<span class="hljs-operator">></span>Disconnect<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
        ) : (
          <span class="hljs-operator">&#x3C;</span>button onClick<span class="hljs-operator">=</span>{connect}<span class="hljs-operator">></span>Connect Wallet<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
        )}
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>nav<span class="hljs-operator">></span>
  );
}

export default Navbar;
</code></pre><h3 id="h-componentscreatenftjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">components/CreateNFT.js</h3><pre data-type="codeBlock" text="import React, { useState } from &quot;react&quot;;
import { useWeb3React } from &quot;@web3-react/core&quot;;
import { ethers } from &quot;ethers&quot;;
import NFTMarketplaceABI from &quot;../contracts/NFTMarketplace.json&quot;;
import SeiNFTABI from &quot;../contracts/SeiNFT.json&quot;;

const NFTMarketplaceAddress = &quot;YOUR_MARKETPLACE_CONTRACT_ADDRESS&quot;;
const SeiNFTAddress = &quot;YOUR_NFT_CONTRACT_ADDRESS&quot;;

function CreateNFT() {
  const { library, account } = useWeb3React();
  const [name, setName] = useState(&quot;&quot;);
  const [description, setDescription] = useState(&quot;&quot;);
  const [price, setPrice] = useState(&quot;&quot;);
  const [file, setFile] = useState(null);
  const [loading, setLoading] = useState(false);

  async function uploadToIPFS() {
    // Implement IPFS upload logic here
    // Return the IPFS hash
  }

  async function mintNFT(e) {
    e.preventDefault();
    if (!library || !account) return;

    try {
      setLoading(true);
      const ipfsHash = await uploadToIPFS();

      const signer = library.getSigner();
      const nftContract = new ethers.Contract(
        SeiNFTAddress,
        SeiNFTABI.abi,
        signer
      );

      const tx = await nftContract.mintNFT(account, ipfsHash);
      await tx.wait();

      alert(&quot;NFT minted successfully!&quot;);
    } catch (error) {
      console.error(&quot;Error minting NFT:&quot;, error);
      alert(&quot;Error minting NFT&quot;);
    } finally {
      setLoading(false);
    }
  }

  return (
    &lt;div className=&quot;create-nft&quot;&gt;
      &lt;h2&gt;Create New NFT&lt;/h2&gt;
      &lt;form onSubmit={mintNFT}&gt;
        &lt;div&gt;
          &lt;label&gt;Name:&lt;/label&gt;
          &lt;input
            type=&quot;text&quot;
            value={name}
            onChange={(e) =&gt; setName(e.target.value)}
            required
          /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Description:&lt;/label&gt;
          &lt;textarea
            value={description}
            onChange={(e) =&gt; setDescription(e.target.value)}
            required
          /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Price (in SEI):&lt;/label&gt;
          &lt;input
            type=&quot;number&quot;
            value={price}
            onChange={(e) =&gt; setPrice(e.target.value)}
            required
          /&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;label&gt;Image:&lt;/label&gt;
          &lt;input
            type=&quot;file&quot;
            onChange={(e) =&gt; setFile(e.target.files[0])}
            required
          /&gt;
        &lt;/div&gt;
        &lt;button type=&quot;submit&quot; disabled={loading}&gt;
          {loading ? &quot;Minting...&quot; : &quot;Mint NFT&quot;}
        &lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  );
}

export default CreateNFT;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span>, { <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useWeb3React</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@web3-react/core"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">NFTMarketplaceABI</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../contracts/NFTMarketplace.json"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">SeiNFTABI</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../contracts/SeiNFT.json"</span>;

const NFTMarketplaceAddress <span class="hljs-operator">=</span> <span class="hljs-string">"YOUR_MARKETPLACE_CONTRACT_ADDRESS"</span>;
const SeiNFTAddress <span class="hljs-operator">=</span> <span class="hljs-string">"YOUR_NFT_CONTRACT_ADDRESS"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CreateNFT</span>(<span class="hljs-params"></span>) </span>{
  const { <span class="hljs-class"><span class="hljs-keyword">library</span>, <span class="hljs-title">account</span> } = <span class="hljs-title">useWeb3React</span>(<span class="hljs-params"></span>);
  <span class="hljs-title">const</span> [<span class="hljs-title">name</span>, <span class="hljs-title">setName</span>] = <span class="hljs-title">useState</span>(<span class="hljs-params"><span class="hljs-string">""</span></span>);
  <span class="hljs-title">const</span> [<span class="hljs-title">description</span>, <span class="hljs-title">setDescription</span>] = <span class="hljs-title">useState</span>(<span class="hljs-params"><span class="hljs-string">""</span></span>);
  <span class="hljs-title">const</span> [<span class="hljs-title">price</span>, <span class="hljs-title">setPrice</span>] = <span class="hljs-title">useState</span>(<span class="hljs-params"><span class="hljs-string">""</span></span>);
  <span class="hljs-title">const</span> [<span class="hljs-title">file</span>, <span class="hljs-title">setFile</span>] = <span class="hljs-title">useState</span>(<span class="hljs-params">null</span>);
  <span class="hljs-title">const</span> [<span class="hljs-title">loading</span>, <span class="hljs-title">setLoading</span>] = <span class="hljs-title">useState</span>(<span class="hljs-params"><span class="hljs-literal">false</span></span>);

  <span class="hljs-title">async</span> <span class="hljs-title"><span class="hljs-keyword">function</span></span> <span class="hljs-title">uploadToIPFS</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Implement IPFS upload logic here</span>
    <span class="hljs-comment">// Return the IPFS hash</span>
  }

  async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintNFT</span>(<span class="hljs-params">e</span>) </span>{
    e.preventDefault();
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span><span class="hljs-class"><span class="hljs-keyword">library</span> || !<span class="hljs-title">account</span>) <span class="hljs-title"><span class="hljs-keyword">return</span></span>;

    <span class="hljs-title"><span class="hljs-keyword">try</span></span> </span>{
      setLoading(<span class="hljs-literal">true</span>);
      const ipfsHash <span class="hljs-operator">=</span> await uploadToIPFS();

      const signer <span class="hljs-operator">=</span> <span class="hljs-keyword">library</span>.getSigner();
      const nftContract <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.Contract(
        SeiNFTAddress,
        SeiNFTABI.abi,
        signer
      );

      const <span class="hljs-built_in">tx</span> <span class="hljs-operator">=</span> await nftContract.mintNFT(account, ipfsHash);
      await <span class="hljs-built_in">tx</span>.wait();

      alert(<span class="hljs-string">"NFT minted successfully!"</span>);
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
      console.error(<span class="hljs-string">"Error minting NFT:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
      alert(<span class="hljs-string">"Error minting NFT"</span>);
    } finally {
      setLoading(<span class="hljs-literal">false</span>);
    }
  }

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"create-nft"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>h2<span class="hljs-operator">></span>Create New NFT<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>form onSubmit<span class="hljs-operator">=</span>{mintNFT}<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>label<span class="hljs-operator">></span>Name:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>input
            <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"text"</span>
            value<span class="hljs-operator">=</span>{name}
            onChange<span class="hljs-operator">=</span>{(e) <span class="hljs-operator">=</span><span class="hljs-operator">></span> setName(e.target.<span class="hljs-built_in">value</span>)}
            required
          <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>label<span class="hljs-operator">></span>Description:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>textarea
            value<span class="hljs-operator">=</span>{description}
            onChange<span class="hljs-operator">=</span>{(e) <span class="hljs-operator">=</span><span class="hljs-operator">></span> setDescription(e.target.<span class="hljs-built_in">value</span>)}
            required
          <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>label<span class="hljs-operator">></span>Price (in SEI):<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>input
            <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"number"</span>
            value<span class="hljs-operator">=</span>{price}
            onChange<span class="hljs-operator">=</span>{(e) <span class="hljs-operator">=</span><span class="hljs-operator">></span> setPrice(e.target.<span class="hljs-built_in">value</span>)}
            required
          <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>label<span class="hljs-operator">></span>Image:<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>label<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>input
            <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"file"</span>
            onChange<span class="hljs-operator">=</span>{(e) <span class="hljs-operator">=</span><span class="hljs-operator">></span> setFile(e.target.files[<span class="hljs-number">0</span>])}
            required
          <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>button <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"submit"</span> disabled<span class="hljs-operator">=</span>{loading}<span class="hljs-operator">></span>
          {loading ? <span class="hljs-string">"Minting..."</span> : <span class="hljs-string">"Mint NFT"</span>}
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>form<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  );
}

export default CreateNFT;
</code></pre><h2 id="h-testing-and-deployment" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Testing and Deployment</h2><ol><li><p>Create a test file <code>test/NFTMarketplace.test.js</code>:</p></li></ol><pre data-type="codeBlock" text="const { expect } = require(&quot;chai&quot;);
const { ethers } = require(&quot;hardhat&quot;);

describe(&quot;NFTMarketplace&quot;, function () {
  let nftMarketplace;
  let seiNFT;
  let owner;
  let seller;
  let buyer;

  beforeEach(async function () {
    [owner, seller, buyer] = await ethers.getSigners();

    const SeiNFT = await ethers.getContractFactory(&quot;SeiNFT&quot;);
    seiNFT = await SeiNFT.deploy();
    await seiNFT.deployed();

    const NFTMarketplace = await ethers.getContractFactory(&quot;NFTMarketplace&quot;);
    nftMarketplace = await NFTMarketplace.deploy();
    await nftMarketplace.deployed();
  });

  it(&quot;Should allow listing and buying NFTs&quot;, async function () {
    // Mint NFT
    const tokenURI = &quot;ipfs://Qm...&quot;;
    await seiNFT.connect(seller).mintNFT(seller.address, tokenURI);

    // Approve marketplace
    await seiNFT
      .connect(seller)
      .setApprovalForAll(nftMarketplace.address, true);

    // List NFT
    const price = ethers.utils.parseEther(&quot;1.0&quot;);
    await nftMarketplace.connect(seller).listNFT(seiNFT.address, 1, price);

    // Buy NFT
    await nftMarketplace.connect(buyer).buyNFT(seiNFT.address, 1, {
      value: price,
    });

    // Verify ownership
    expect(await seiNFT.ownerOf(1)).to.equal(buyer.address);
  });
});
"><code>const { expect } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"chai"</span>);
const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

describe(<span class="hljs-string">"NFTMarketplace"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  let nftMarketplace;
  let seiNFT;
  let owner;
  let seller;
  let buyer;

  beforeEach(async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    [owner, seller, buyer] <span class="hljs-operator">=</span> await ethers.getSigners();

    const SeiNFT <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"SeiNFT"</span>);
    seiNFT <span class="hljs-operator">=</span> await SeiNFT.deploy();
    await seiNFT.deployed();

    const NFTMarketplace <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"NFTMarketplace"</span>);
    nftMarketplace <span class="hljs-operator">=</span> await NFTMarketplace.deploy();
    await nftMarketplace.deployed();
  });

  it(<span class="hljs-string">"Should allow listing and buying NFTs"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Mint NFT</span>
    const tokenURI <span class="hljs-operator">=</span> <span class="hljs-string">"ipfs://Qm..."</span>;
    await seiNFT.connect(seller).mintNFT(seller.<span class="hljs-built_in">address</span>, tokenURI);

    <span class="hljs-comment">// Approve marketplace</span>
    await seiNFT
      .connect(seller)
      .setApprovalForAll(nftMarketplace.<span class="hljs-built_in">address</span>, <span class="hljs-literal">true</span>);

    <span class="hljs-comment">// List NFT</span>
    const price <span class="hljs-operator">=</span> ethers.utils.parseEther(<span class="hljs-string">"1.0"</span>);
    await nftMarketplace.connect(seller).listNFT(seiNFT.<span class="hljs-built_in">address</span>, <span class="hljs-number">1</span>, price);

    <span class="hljs-comment">// Buy NFT</span>
    await nftMarketplace.connect(buyer).buyNFT(seiNFT.<span class="hljs-built_in">address</span>, <span class="hljs-number">1</span>, {
      <span class="hljs-built_in">value</span>: price,
    });

    <span class="hljs-comment">// Verify ownership</span>
    expect(await seiNFT.ownerOf(<span class="hljs-number">1</span>)).to.equal(buyer.<span class="hljs-built_in">address</span>);
  });
});
</code></pre><ol><li><p>Deploy the contracts:</p></li></ol><p>Create a deployment script <code>scripts/deploy.js</code>:</p><pre data-type="codeBlock" text="const hre = require(&quot;hardhat&quot;);

async function main() {
  const SeiNFT = await hre.ethers.getContractFactory(&quot;SeiNFT&quot;);
  const seiNFT = await SeiNFT.deploy();
  await seiNFT.deployed();
  console.log(&quot;SeiNFT deployed to:&quot;, seiNFT.address);

  const NFTMarketplace = await hre.ethers.getContractFactory(&quot;NFTMarketplace&quot;);
  const nftMarketplace = await NFTMarketplace.deploy();
  await nftMarketplace.deployed();
  console.log(&quot;NFTMarketplace deployed to:&quot;, nftMarketplace.address);
}

main()
  .then(() =&gt; process.exit(0))
  .catch((error) =&gt; {
    console.error(error);
    process.exit(1);
  });
"><code>const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  const SeiNFT <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"SeiNFT"</span>);
  const seiNFT <span class="hljs-operator">=</span> await SeiNFT.deploy();
  await seiNFT.deployed();
  console.log(<span class="hljs-string">"SeiNFT deployed to:"</span>, seiNFT.<span class="hljs-built_in">address</span>);

  const NFTMarketplace <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"NFTMarketplace"</span>);
  const nftMarketplace <span class="hljs-operator">=</span> await NFTMarketplace.deploy();
  await nftMarketplace.deployed();
  console.log(<span class="hljs-string">"NFTMarketplace deployed to:"</span>, nftMarketplace.<span class="hljs-built_in">address</span>);
}

main()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> process.exit(<span class="hljs-number">0</span>))
  .catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
    console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  });
</code></pre><ol><li><p>Update <code>hardhat.config.js</code>:</p></li></ol><pre data-type="codeBlock" text="require(&quot;@nomicfoundation/hardhat-toolbox&quot;);
require(&quot;dotenv&quot;).config();

module.exports = {
  solidity: &quot;0.8.20&quot;,
  networks: {
    seiTestnet: {
      url: process.env.SEI_RPC_URL,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};
"><code><span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomicfoundation/hardhat-toolbox"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();

module.exports <span class="hljs-operator">=</span> {
  solidity: <span class="hljs-string">"0.8.20"</span>,
  networks: {
    seiTestnet: {
      url: process.env.SEI_RPC_URL,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};
</code></pre><ol><li><p>Deploy to Sei Testnet:</p></li></ol><pre data-type="codeBlock" text="npx hardhat run scripts/deploy.js --network seiTestnet
"><code>npx hardhat run scripts<span class="hljs-operator">/</span>deploy.js <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network seiTestnet
</code></pre><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>In this tutorial, we&apos;ve built a complete NFT marketplace on the Sei blockchain. We&apos;ve covered:</p><ol><li><p>Setting up the development environment</p></li><li><p>Creating and deploying smart contracts</p></li><li><p>Building a React frontend</p></li><li><p>Implementing core marketplace features</p></li><li><p>Testing and deployment</p></li></ol><p>The marketplace includes essential features like:</p><ul><li><p>NFT minting</p></li><li><p>Listing NFTs for sale</p></li><li><p>Buying NFTs</p></li><li><p>Managing listings</p></li><li><p>Platform fees</p></li></ul><p>To enhance this marketplace further, you could add:</p><ul><li><p>Advanced search and filtering</p></li><li><p>Auction functionality</p></li><li><p>Collection management</p></li><li><p>Social features</p></li><li><p>Analytics dashboard</p></li></ul><h2 id="h-additional-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Additional Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.sei.io">Sei Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts">OpenZeppelin Contracts</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hardhat.org/getting-started">Hardhat Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ethers.org">Ethers.js Documentation</a></p></li></ul>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 9 - How to create a full stack NFT monster game on the sei blockchain]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-9-how-to-create-a-full-stack-nft-monster-game-on-the-sei-blockchain</link>
            <guid>DCZURMFhF7zKqCqaKbrP</guid>
            <pubDate>Tue, 17 Jun 2025 12:33:25 GMT</pubDate>
            <description><![CDATA[Building a Full-Stack NFT Monster Game on Sei BlockchainTable of ContentsIntroductionPrerequisitesProject SetupSmart Contract DevelopmentFrontend DevelopmentTesting and DeploymentConclusionIntroductionIn this tutorial, we&apos;ll build a full-stack NFT monster game on the Sei blockchain. Our game will allow users to:Mint unique monster NFTsBattle monsters against each otherLevel up monsters through battlesTrade monsters on a marketplaceWe&apos;ll use the following tech stack:Smart Contracts: ...]]></description>
            <content:encoded><![CDATA[<h1 id="h-building-a-full-stack-nft-monster-game-on-sei-blockchain" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Building a Full-Stack NFT Monster Game on Sei Blockchain</h1><h2 id="h-table-of-contents" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Table of Contents</h2><ol><li><p>Introduction</p></li><li><p>Prerequisites</p></li><li><p>Project Setup</p></li><li><p>Smart Contract Development</p></li><li><p>Frontend Development</p></li><li><p>Testing and Deployment</p></li><li><p>Conclusion</p></li></ol><h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>In this tutorial, we&apos;ll build a full-stack NFT monster game on the Sei blockchain. Our game will allow users to:</p><ul><li><p>Mint unique monster NFTs</p></li><li><p>Battle monsters against each other</p></li><li><p>Level up monsters through battles</p></li><li><p>Trade monsters on a marketplace</p></li></ul><p>We&apos;ll use the following tech stack:</p><ul><li><p>Smart Contracts: Solidity with Hardhat</p></li><li><p>Frontend: React.js with ethers.js</p></li><li><p>Blockchain: Sei EVM</p></li><li><p>IPFS: For storing monster metadata</p></li></ul><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before we begin, ensure you have the following installed:</p><ul><li><p>Node.js (v16 or higher)</p></li><li><p>npm or yarn</p></li><li><p>MetaMask wallet</p></li><li><p>Git</p></li></ul><h2 id="h-project-setup" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Project Setup</h2><h3 id="h-1-initialize-the-project" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Initialize the Project</h3><p>First, let&apos;s create our project structure:</p><pre data-type="codeBlock" text="mkdir sei-monster-game
cd sei-monster-game
npm init -y
"><code>mkdir sei<span class="hljs-operator">-</span>monster<span class="hljs-operator">-</span>game
cd sei<span class="hljs-operator">-</span>monster<span class="hljs-operator">-</span>game
npm init <span class="hljs-operator">-</span>y
</code></pre><h3 id="h-2-install-dependencies" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Install Dependencies</h3><pre data-type="codeBlock" text="npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @openzeppelin/contracts dotenv
npm install @sei-io/sei-evm-sdk ethers@^5.7.2 react react-dom next @chakra-ui/react @emotion/react @emotion/styled framer-motion
"><code>npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save<span class="hljs-operator">-</span>dev hardhat @nomicfoundation<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>toolbox @openzeppelin<span class="hljs-operator">/</span>contracts dotenv
npm install @sei<span class="hljs-operator">-</span>io<span class="hljs-operator">/</span>sei<span class="hljs-operator">-</span>evm<span class="hljs-operator">-</span>sdk ethers@<span class="hljs-operator">^</span><span class="hljs-number">5.7</span><span class="hljs-number">.2</span> react react<span class="hljs-operator">-</span>dom next @chakra<span class="hljs-operator">-</span>ui<span class="hljs-operator">/</span>react @emotion<span class="hljs-operator">/</span>react @emotion<span class="hljs-operator">/</span>styled framer<span class="hljs-operator">-</span>motion
</code></pre><h3 id="h-3-initialize-hardhat" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. Initialize Hardhat</h3><pre data-type="codeBlock" text="npx hardhat init
"><code>npx hardhat <span class="hljs-keyword">init</span>
</code></pre><p>Choose &quot;Create a JavaScript project&quot; when prompted.</p><h3 id="h-4-configure-hardhat" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4. Configure Hardhat</h3><p>Create a <code>.env</code> file in the root directory:</p><pre data-type="codeBlock" text="PRIVATE_KEY=your_private_key_here
SEI_RPC_URL=your_sei_rpc_url
ETHERSCAN_API_KEY=your_etherscan_api_key
"><code><span class="hljs-attr">PRIVATE_KEY</span>=your_private_key_here
<span class="hljs-attr">SEI_RPC_URL</span>=your_sei_rpc_url
<span class="hljs-attr">ETHERSCAN_API_KEY</span>=your_etherscan_api_key
</code></pre><p>Update <code>hardhat.config.js</code>:</p><pre data-type="codeBlock" text="require(&quot;@nomicfoundation/hardhat-toolbox&quot;);
require(&quot;dotenv&quot;).config();

module.exports = {
  solidity: &quot;0.8.19&quot;,
  networks: {
    sei: {
      url: process.env.SEI_RPC_URL,
      accounts: [process.env.PRIVATE_KEY],
      chainId: 713715, // Sei testnet chain ID
    },
  },
  etherscan: {
    apiKey: process.env.ETHERSCAN_API_KEY,
  },
};
"><code><span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomicfoundation/hardhat-toolbox"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();

module.exports <span class="hljs-operator">=</span> {
  solidity: <span class="hljs-string">"0.8.19"</span>,
  networks: {
    sei: {
      url: process.env.SEI_RPC_URL,
      accounts: [process.env.PRIVATE_KEY],
      chainId: <span class="hljs-number">713715</span>, <span class="hljs-comment">// Sei testnet chain ID</span>
    },
  },
  etherscan: {
    apiKey: process.env.ETHERSCAN_API_KEY,
  },
};
</code></pre><h2 id="h-smart-contract-development" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Smart Contract Development</h2><h3 id="h-1-monster-nft-contract" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Monster NFT Contract</h3><p>Create <code>contracts/MonsterNFT.sol</code>:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol&quot;;
import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;;

contract MonsterNFT is ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    struct Monster {
        string name;
        uint256 level;
        uint256 experience;
        uint256 strength;
        uint256 defense;
        uint256 speed;
        string imageURI;
    }

    mapping(uint256 =&gt; Monster) public monsters;
    uint256 public mintPrice = 0.01 ether;

    event MonsterMinted(address indexed owner, uint256 indexed tokenId, string name);
    event MonsterLevelUp(uint256 indexed tokenId, uint256 newLevel);

    constructor() ERC721(&quot;MonsterNFT&quot;, &quot;MNFT&quot;) {}

    function mintMonster(string memory _name, string memory _imageURI) public payable {
        require(msg.value &gt;= mintPrice, &quot;Insufficient payment&quot;);

        _tokenIds.increment();
        uint256 newTokenId = _tokenIds.current();

        // Generate random stats
        uint256 strength = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, newTokenId, &quot;strength&quot;))) % 100;
        uint256 defense = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, newTokenId, &quot;defense&quot;))) % 100;
        uint256 speed = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, newTokenId, &quot;speed&quot;))) % 100;

        monsters[newTokenId] = Monster({
            name: _name,
            level: 1,
            experience: 0,
            strength: strength,
            defense: defense,
            speed: speed,
            imageURI: _imageURI
        });

        _safeMint(msg.sender, newTokenId);
        emit MonsterMinted(msg.sender, newTokenId, _name);
    }

    function battle(uint256 _attackerId, uint256 _defenderId) public {
        require(_isApprovedOrOwner(msg.sender, _attackerId), &quot;Not owner or approved&quot;);
        require(_exists(_defenderId), &quot;Defender does not exist&quot;);

        Monster storage attacker = monsters[_attackerId];
        Monster storage defender = monsters[_defenderId];

        // Simple battle logic
        uint256 attackerPower = attacker.strength + attacker.speed;
        uint256 defenderPower = defender.defense + defender.speed;

        if (attackerPower &gt; defenderPower) {
            // Attacker wins
            attacker.experience += 10;
            checkLevelUp(_attackerId);
        } else {
            // Defender wins
            defender.experience += 10;
            checkLevelUp(_defenderId);
        }
    }

    function checkLevelUp(uint256 _tokenId) internal {
        Monster storage monster = monsters[_tokenId];
        if (monster.experience &gt;= monster.level * 100) {
            monster.level += 1;
            monster.strength += 5;
            monster.defense += 5;
            monster.speed += 5;
            emit MonsterLevelUp(_tokenId, monster.level);
        }
    }

    function getMonster(uint256 _tokenId) public view returns (
        string memory name,
        uint256 level,
        uint256 experience,
        uint256 strength,
        uint256 defense,
        uint256 speed,
        string memory imageURI
    ) {
        Monster memory monster = monsters[_tokenId];
        return (
            monster.name,
            monster.level,
            monster.experience,
            monster.strength,
            monster.defense,
            monster.speed,
            monster.imageURI
        );
    }

    function withdraw() public onlyOwner {
        payable(owner()).transfer(address(this).balance);
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.19;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/Counters.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MonsterNFT</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721Enumerable</span>, <span class="hljs-title">Ownable</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">Counters</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Counters</span>.<span class="hljs-title">Counter</span>;
    Counters.Counter <span class="hljs-keyword">private</span> _tokenIds;

    <span class="hljs-keyword">struct</span> <span class="hljs-title">Monster</span> {
        <span class="hljs-keyword">string</span> name;
        <span class="hljs-keyword">uint256</span> level;
        <span class="hljs-keyword">uint256</span> experience;
        <span class="hljs-keyword">uint256</span> strength;
        <span class="hljs-keyword">uint256</span> defense;
        <span class="hljs-keyword">uint256</span> speed;
        <span class="hljs-keyword">string</span> imageURI;
    }

    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> Monster) <span class="hljs-keyword">public</span> monsters;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> mintPrice <span class="hljs-operator">=</span> <span class="hljs-number">0</span><span class="hljs-number">.01</span> <span class="hljs-literal">ether</span>;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">MonsterMinted</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> owner, <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> tokenId, <span class="hljs-keyword">string</span> name</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">MonsterLevelUp</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> tokenId, <span class="hljs-keyword">uint256</span> newLevel</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"MonsterNFT"</span>, <span class="hljs-string">"MNFT"</span></span>) </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintMonster</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _name, <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> _imageURI</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span> <span class="hljs-operator">></span><span class="hljs-operator">=</span> mintPrice, <span class="hljs-string">"Insufficient payment"</span>);

        _tokenIds.increment();
        <span class="hljs-keyword">uint256</span> newTokenId <span class="hljs-operator">=</span> _tokenIds.current();

        <span class="hljs-comment">// Generate random stats</span>
        <span class="hljs-keyword">uint256</span> strength <span class="hljs-operator">=</span> <span class="hljs-keyword">uint256</span>(<span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newTokenId, <span class="hljs-string">"strength"</span>))) <span class="hljs-operator">%</span> <span class="hljs-number">100</span>;
        <span class="hljs-keyword">uint256</span> defense <span class="hljs-operator">=</span> <span class="hljs-keyword">uint256</span>(<span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newTokenId, <span class="hljs-string">"defense"</span>))) <span class="hljs-operator">%</span> <span class="hljs-number">100</span>;
        <span class="hljs-keyword">uint256</span> speed <span class="hljs-operator">=</span> <span class="hljs-keyword">uint256</span>(<span class="hljs-built_in">keccak256</span>(<span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newTokenId, <span class="hljs-string">"speed"</span>))) <span class="hljs-operator">%</span> <span class="hljs-number">100</span>;

        monsters[newTokenId] <span class="hljs-operator">=</span> Monster({
            name: _name,
            level: <span class="hljs-number">1</span>,
            experience: <span class="hljs-number">0</span>,
            strength: strength,
            defense: defense,
            speed: speed,
            imageURI: _imageURI
        });

        _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newTokenId);
        <span class="hljs-keyword">emit</span> MonsterMinted(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, newTokenId, _name);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">battle</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _attackerId, <span class="hljs-keyword">uint256</span> _defenderId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(_isApprovedOrOwner(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, _attackerId), <span class="hljs-string">"Not owner or approved"</span>);
        <span class="hljs-built_in">require</span>(_exists(_defenderId), <span class="hljs-string">"Defender does not exist"</span>);

        Monster <span class="hljs-keyword">storage</span> attacker <span class="hljs-operator">=</span> monsters[_attackerId];
        Monster <span class="hljs-keyword">storage</span> defender <span class="hljs-operator">=</span> monsters[_defenderId];

        <span class="hljs-comment">// Simple battle logic</span>
        <span class="hljs-keyword">uint256</span> attackerPower <span class="hljs-operator">=</span> attacker.strength <span class="hljs-operator">+</span> attacker.speed;
        <span class="hljs-keyword">uint256</span> defenderPower <span class="hljs-operator">=</span> defender.defense <span class="hljs-operator">+</span> defender.speed;

        <span class="hljs-keyword">if</span> (attackerPower <span class="hljs-operator">></span> defenderPower) {
            <span class="hljs-comment">// Attacker wins</span>
            attacker.experience <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">10</span>;
            checkLevelUp(_attackerId);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Defender wins</span>
            defender.experience <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">10</span>;
            checkLevelUp(_defenderId);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">checkLevelUp</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        Monster <span class="hljs-keyword">storage</span> monster <span class="hljs-operator">=</span> monsters[_tokenId];
        <span class="hljs-keyword">if</span> (monster.experience <span class="hljs-operator">></span><span class="hljs-operator">=</span> monster.level <span class="hljs-operator">*</span> <span class="hljs-number">100</span>) {
            monster.level <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
            monster.strength <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">5</span>;
            monster.defense <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">5</span>;
            monster.speed <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">5</span>;
            <span class="hljs-keyword">emit</span> MonsterLevelUp(_tokenId, monster.level);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMonster</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> name,
        <span class="hljs-keyword">uint256</span> level,
        <span class="hljs-keyword">uint256</span> experience,
        <span class="hljs-keyword">uint256</span> strength,
        <span class="hljs-keyword">uint256</span> defense,
        <span class="hljs-keyword">uint256</span> speed,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> imageURI
    </span>) </span>{
        Monster <span class="hljs-keyword">memory</span> monster <span class="hljs-operator">=</span> monsters[_tokenId];
        <span class="hljs-keyword">return</span> (
            monster.<span class="hljs-built_in">name</span>,
            monster.level,
            monster.experience,
            monster.strength,
            monster.defense,
            monster.speed,
            monster.imageURI
        );
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-keyword">payable</span>(owner()).<span class="hljs-built_in">transfer</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span>);
    }
}
</code></pre><h3 id="h-2-battle-arena-contract" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Battle Arena Contract</h3><p>Create <code>contracts/BattleArena.sol</code>:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import &quot;./MonsterNFT.sol&quot;;

contract BattleArena {
    MonsterNFT public monsterNFT;

    struct Battle {
        uint256 attackerId;
        uint256 defenderId;
        address winner;
        uint256 timestamp;
    }

    Battle[] public battles;

    event BattleCompleted(uint256 indexed battleId, address winner, uint256 attackerId, uint256 defenderId);

    constructor(address _monsterNFTAddress) {
        monsterNFT = MonsterNFT(_monsterNFTAddress);
    }

    function startBattle(uint256 _attackerId, uint256 _defenderId) public {
        require(monsterNFT.ownerOf(_attackerId) == msg.sender, &quot;Not owner of attacker&quot;);

        monsterNFT.battle(_attackerId, _defenderId);

        Battle memory newBattle = Battle({
            attackerId: _attackerId,
            defenderId: _defenderId,
            winner: msg.sender,
            timestamp: block.timestamp
        });

        battles.push(newBattle);

        emit BattleCompleted(battles.length - 1, msg.sender, _attackerId, _defenderId);
    }

    function getBattleHistory() public view returns (Battle[] memory) {
        return battles;
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.19;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./MonsterNFT.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">BattleArena</span> </span>{
    MonsterNFT <span class="hljs-keyword">public</span> monsterNFT;

    <span class="hljs-keyword">struct</span> <span class="hljs-title">Battle</span> {
        <span class="hljs-keyword">uint256</span> attackerId;
        <span class="hljs-keyword">uint256</span> defenderId;
        <span class="hljs-keyword">address</span> winner;
        <span class="hljs-keyword">uint256</span> timestamp;
    }

    Battle[] <span class="hljs-keyword">public</span> battles;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">BattleCompleted</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> battleId, <span class="hljs-keyword">address</span> winner, <span class="hljs-keyword">uint256</span> attackerId, <span class="hljs-keyword">uint256</span> defenderId</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _monsterNFTAddress</span>) </span>{
        monsterNFT <span class="hljs-operator">=</span> MonsterNFT(_monsterNFTAddress);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">startBattle</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> _attackerId, <span class="hljs-keyword">uint256</span> _defenderId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(monsterNFT.ownerOf(_attackerId) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-string">"Not owner of attacker"</span>);

        monsterNFT.battle(_attackerId, _defenderId);

        Battle <span class="hljs-keyword">memory</span> newBattle <span class="hljs-operator">=</span> Battle({
            attackerId: _attackerId,
            defenderId: _defenderId,
            winner: <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>,
            timestamp: <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>
        });

        battles.<span class="hljs-built_in">push</span>(newBattle);

        <span class="hljs-keyword">emit</span> BattleCompleted(battles.<span class="hljs-built_in">length</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>, <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, _attackerId, _defenderId);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBattleHistory</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">Battle[] <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> battles;
    }
}
</code></pre><h2 id="h-frontend-development" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Frontend Development</h2><h3 id="h-1-initialize-nextjs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Initialize Next.js</h3><pre data-type="codeBlock" text="npx create-next-app@latest frontend --typescript
cd frontend
"><code>npx create<span class="hljs-operator">-</span>next<span class="hljs-operator">-</span>app@latest frontend <span class="hljs-operator">-</span><span class="hljs-operator">-</span>typescript
cd frontend
</code></pre><h3 id="h-2-create-components" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Create Components</h3><p>Create <code>components/MonsterCard.tsx</code>:</p><pre data-type="codeBlock" text="import { Box, Image, Text, Button, VStack, HStack } from &quot;@chakra-ui/react&quot;;
import { ethers } from &quot;ethers&quot;;
import { MonsterNFT } from &quot;../typechain-types&quot;;

interface MonsterCardProps {
  tokenId: number;
  monster: {
    name: string;
    level: number;
    experience: number;
    strength: number;
    defense: number;
    speed: number;
    imageURI: string;
  };
  onBattle?: (tokenId: number) =&gt; void;
}

export const MonsterCard = ({
  tokenId,
  monster,
  onBattle,
}: MonsterCardProps) =&gt; {
  return (
    &lt;Box
      borderWidth=&quot;1px&quot;
      borderRadius=&quot;lg&quot;
      overflow=&quot;hidden&quot;
      p={4}
      maxW=&quot;sm&quot;
      bg=&quot;white&quot;
      boxShadow=&quot;md&quot;
    &gt;
      &lt;Image src={monster.imageURI} alt={monster.name} /&gt;
      &lt;VStack align=&quot;start&quot; mt={4} spacing={2}&gt;
        &lt;Text fontSize=&quot;xl&quot; fontWeight=&quot;bold&quot;&gt;
          {monster.name}
        &lt;/Text&gt;
        &lt;Text&gt;Level: {monster.level}&lt;/Text&gt;
        &lt;Text&gt;Experience: {monster.experience}&lt;/Text&gt;
        &lt;HStack spacing={4}&gt;
          &lt;Text&gt;Strength: {monster.strength}&lt;/Text&gt;
          &lt;Text&gt;Defense: {monster.defense}&lt;/Text&gt;
          &lt;Text&gt;Speed: {monster.speed}&lt;/Text&gt;
        &lt;/HStack&gt;
        {onBattle &amp;&amp; (
          &lt;Button
            colorScheme=&quot;blue&quot;
            onClick={() =&gt; onBattle(tokenId)}
            width=&quot;full&quot;
          &gt;
            Battle
          &lt;/Button&gt;
        )}
      &lt;/VStack&gt;
    &lt;/Box&gt;
  );
};
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">Box</span>, <span class="hljs-title">Image</span>, <span class="hljs-title">Text</span>, <span class="hljs-title">Button</span>, <span class="hljs-title">VStack</span>, <span class="hljs-title">HStack</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@chakra-ui/react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">MonsterNFT</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../typechain-types"</span>;

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">MonsterCardProps</span> </span>{
  tokenId: number;
  monster: {
    name: <span class="hljs-keyword">string</span>;
    level: number;
    experience: number;
    strength: number;
    defense: number;
    speed: number;
    imageURI: <span class="hljs-keyword">string</span>;
  };
  onBattle?: (tokenId: number) <span class="hljs-operator">=</span><span class="hljs-operator">></span> void;
}

export const MonsterCard <span class="hljs-operator">=</span> ({
  tokenId,
  monster,
  onBattle,
}: MonsterCardProps) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>Box
      borderWidth<span class="hljs-operator">=</span><span class="hljs-string">"1px"</span>
      borderRadius<span class="hljs-operator">=</span><span class="hljs-string">"lg"</span>
      overflow<span class="hljs-operator">=</span><span class="hljs-string">"hidden"</span>
      p<span class="hljs-operator">=</span>{<span class="hljs-number">4</span>}
      maxW<span class="hljs-operator">=</span><span class="hljs-string">"sm"</span>
      bg<span class="hljs-operator">=</span><span class="hljs-string">"white"</span>
      boxShadow<span class="hljs-operator">=</span><span class="hljs-string">"md"</span>
    <span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>Image src<span class="hljs-operator">=</span>{monster.imageURI} alt<span class="hljs-operator">=</span>{monster.<span class="hljs-built_in">name</span>} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>VStack align<span class="hljs-operator">=</span><span class="hljs-string">"start"</span> mt<span class="hljs-operator">=</span>{<span class="hljs-number">4</span>} spacing<span class="hljs-operator">=</span>{<span class="hljs-number">2</span>}<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Text fontSize<span class="hljs-operator">=</span><span class="hljs-string">"xl"</span> fontWeight<span class="hljs-operator">=</span><span class="hljs-string">"bold"</span><span class="hljs-operator">></span>
          {monster.<span class="hljs-built_in">name</span>}
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Text<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Text<span class="hljs-operator">></span>Level: {monster.level}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Text<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Text<span class="hljs-operator">></span>Experience: {monster.experience}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Text<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>HStack spacing<span class="hljs-operator">=</span>{<span class="hljs-number">4</span>}<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Text<span class="hljs-operator">></span>Strength: {monster.strength}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Text<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Text<span class="hljs-operator">></span>Defense: {monster.defense}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Text<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>Text<span class="hljs-operator">></span>Speed: {monster.speed}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Text<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>HStack<span class="hljs-operator">></span>
        {onBattle <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (
          <span class="hljs-operator">&#x3C;</span>Button
            colorScheme<span class="hljs-operator">=</span><span class="hljs-string">"blue"</span>
            onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> onBattle(tokenId)}
            width<span class="hljs-operator">=</span><span class="hljs-string">"full"</span>
          <span class="hljs-operator">></span>
            Battle
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
        )}
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>VStack<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Box<span class="hljs-operator">></span>
  );
};
</code></pre><p>Create <code>pages/index.tsx</code>:</p><pre data-type="codeBlock" text="import { useState, useEffect } from &quot;react&quot;;
import {
  Box,
  Container,
  Grid,
  Heading,
  Button,
  useToast,
  Input,
  VStack,
} from &quot;@chakra-ui/react&quot;;
import { ethers } from &quot;ethers&quot;;
import { MonsterCard } from &quot;../components/MonsterCard&quot;;
import { MonsterNFT__factory } from &quot;../typechain-types&quot;;

const MonsterNFT_ADDRESS = &quot;YOUR_DEPLOYED_CONTRACT_ADDRESS&quot;;

export default function Home() {
  const [account, setAccount] = useState&lt;string&gt;(&quot;&quot;);
  const [monsters, setMonsters] = useState&lt;any[]&gt;([]);
  const [loading, setLoading] = useState(false);
  const toast = useToast();

  const connectWallet = async () =&gt; {
    if (typeof window.ethereum !== &quot;undefined&quot;) {
      try {
        const accounts = await window.ethereum.request({
          method: &quot;eth_requestAccounts&quot;,
        });
        setAccount(accounts[0]);
      } catch (error) {
        console.error(&quot;Error connecting wallet:&quot;, error);
      }
    }
  };

  const mintMonster = async () =&gt; {
    if (!account) return;

    try {
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const signer = provider.getSigner();
      const contract = MonsterNFT__factory.connect(MonsterNFT_ADDRESS, signer);

      const tx = await contract.mintMonster(
        &quot;New Monster&quot;,
        &quot;https://your-ipfs-gateway/monster1.png&quot;,
        { value: ethers.utils.parseEther(&quot;0.01&quot;) }
      );

      await tx.wait();
      toast({
        title: &quot;Monster Minted!&quot;,
        status: &quot;success&quot;,
        duration: 5000,
      });

      loadMonsters();
    } catch (error) {
      console.error(&quot;Error minting monster:&quot;, error);
      toast({
        title: &quot;Error minting monster&quot;,
        status: &quot;error&quot;,
        duration: 5000,
      });
    }
  };

  const loadMonsters = async () =&gt; {
    if (!account) return;

    try {
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      const contract = MonsterNFT__factory.connect(
        MonsterNFT_ADDRESS,
        provider
      );

      const balance = await contract.balanceOf(account);
      const monsterPromises = [];

      for (let i = 0; i &lt; balance.toNumber(); i++) {
        const tokenId = await contract.tokenOfOwnerByIndex(account, i);
        monsterPromises.push(contract.getMonster(tokenId));
      }

      const monsterData = await Promise.all(monsterPromises);
      setMonsters(monsterData);
    } catch (error) {
      console.error(&quot;Error loading monsters:&quot;, error);
    }
  };

  useEffect(() =&gt; {
    if (account) {
      loadMonsters();
    }
  }, [account]);

  return (
    &lt;Container maxW=&quot;container.xl&quot; py={8}&gt;
      &lt;VStack spacing={8}&gt;
        &lt;Heading&gt;Monster NFT Game&lt;/Heading&gt;

        {!account ? (
          &lt;Button onClick={connectWallet} colorScheme=&quot;blue&quot;&gt;
            Connect Wallet
          &lt;/Button&gt;
        ) : (
          &lt;&gt;
            &lt;Button onClick={mintMonster} colorScheme=&quot;green&quot;&gt;
              Mint New Monster
            &lt;/Button&gt;

            &lt;Grid
              templateColumns=&quot;repeat(auto-fill, minmax(250px, 1fr))&quot;
              gap={6}
            &gt;
              {monsters.map((monster, index) =&gt; (
                &lt;MonsterCard
                  key={index}
                  tokenId={index}
                  monster={monster}
                  onBattle={(tokenId) =&gt; {
                    // Implement battle logic
                  }}
                /&gt;
              ))}
            &lt;/Grid&gt;
          &lt;/&gt;
        )}
      &lt;/VStack&gt;
    &lt;/Container&gt;
  );
}
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">useState</span>, <span class="hljs-title">useEffect</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {
  <span class="hljs-title">Box</span>,
  <span class="hljs-title">Container</span>,
  <span class="hljs-title">Grid</span>,
  <span class="hljs-title">Heading</span>,
  <span class="hljs-title">Button</span>,
  <span class="hljs-title">useToast</span>,
  <span class="hljs-title">Input</span>,
  <span class="hljs-title">VStack</span>,
} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@chakra-ui/react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">MonsterCard</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../components/MonsterCard"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">MonsterNFT__factory</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../typechain-types"</span>;

const MonsterNFT_ADDRESS <span class="hljs-operator">=</span> <span class="hljs-string">"YOUR_DEPLOYED_CONTRACT_ADDRESS"</span>;

export default <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  const [account, setAccount] <span class="hljs-operator">=</span> useState<span class="hljs-operator">&#x3C;</span><span class="hljs-keyword">string</span><span class="hljs-operator">></span>(<span class="hljs-string">""</span>);
  const [monsters, setMonsters] <span class="hljs-operator">=</span> useState<span class="hljs-operator">&#x3C;</span>any[]<span class="hljs-operator">></span>([]);
  const [loading, setLoading] <span class="hljs-operator">=</span> useState(<span class="hljs-literal">false</span>);
  const toast <span class="hljs-operator">=</span> useToast();

  const connectWallet <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (typeof window.ethereum <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"undefined"</span>) {
      <span class="hljs-keyword">try</span> {
        const accounts <span class="hljs-operator">=</span> await window.ethereum.request({
          method: <span class="hljs-string">"eth_requestAccounts"</span>,
        });
        setAccount(accounts[<span class="hljs-number">0</span>]);
      } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
        console.error(<span class="hljs-string">"Error connecting wallet:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
      }
    }
  };

  const mintMonster <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>account) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">try</span> {
      const provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(window.ethereum);
      const signer <span class="hljs-operator">=</span> provider.getSigner();
      const <span class="hljs-class"><span class="hljs-keyword">contract</span> = <span class="hljs-title">MonsterNFT__factory</span>.<span class="hljs-title">connect</span>(<span class="hljs-params">MonsterNFT_ADDRESS, signer</span>);

      <span class="hljs-title">const</span> <span class="hljs-title"><span class="hljs-built_in">tx</span></span> = <span class="hljs-title">await</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">mintMonster</span>(<span class="hljs-params">
        <span class="hljs-string">"New Monster"</span>,
        <span class="hljs-string">"https://your-ipfs-gateway/monster1.png"</span>,
        { value: ethers.utils.parseEther(<span class="hljs-params"><span class="hljs-string">"0.01"</span></span>) }
      </span>);

      <span class="hljs-title">await</span> <span class="hljs-title"><span class="hljs-built_in">tx</span></span>.<span class="hljs-title">wait</span>(<span class="hljs-params"></span>);
      <span class="hljs-title">toast</span>(<span class="hljs-params">{
        title: <span class="hljs-string">"Monster Minted!"</span>,
        status: <span class="hljs-string">"success"</span>,
        duration: <span class="hljs-number">5000</span>,
      }</span>);

      <span class="hljs-title">loadMonsters</span>(<span class="hljs-params"></span>);
    } <span class="hljs-title"><span class="hljs-keyword">catch</span></span> (<span class="hljs-params"><span class="hljs-keyword">error</span></span>) </span>{
      console.error(<span class="hljs-string">"Error minting monster:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
      toast({
        title: <span class="hljs-string">"Error minting monster"</span>,
        status: <span class="hljs-string">"error"</span>,
        duration: <span class="hljs-number">5000</span>,
      });
    }
  };

  const loadMonsters <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>account) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">try</span> {
      const provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(window.ethereum);
      const <span class="hljs-class"><span class="hljs-keyword">contract</span> = <span class="hljs-title">MonsterNFT__factory</span>.<span class="hljs-title">connect</span>(<span class="hljs-params">
        MonsterNFT_ADDRESS,
        provider
      </span>);

      <span class="hljs-title">const</span> <span class="hljs-title">balance</span> = <span class="hljs-title">await</span> <span class="hljs-title"><span class="hljs-keyword">contract</span></span>.<span class="hljs-title">balanceOf</span>(<span class="hljs-params">account</span>);
      <span class="hljs-title">const</span> <span class="hljs-title">monsterPromises</span> = [];

      <span class="hljs-title"><span class="hljs-keyword">for</span></span> (<span class="hljs-params">let i = <span class="hljs-number">0</span>; i &#x3C; balance.toNumber(<span class="hljs-params"></span>); i++</span>) </span>{
        const tokenId <span class="hljs-operator">=</span> await <span class="hljs-keyword">contract</span>.tokenOfOwnerByIndex(account, i);
        monsterPromises.<span class="hljs-built_in">push</span>(<span class="hljs-keyword">contract</span>.getMonster(tokenId));
      }

      const monsterData <span class="hljs-operator">=</span> await Promise.all(monsterPromises);
      setMonsters(monsterData);
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
      console.error(<span class="hljs-string">"Error loading monsters:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    }
  };

  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (account) {
      loadMonsters();
    }
  }, [account]);

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>Container maxW<span class="hljs-operator">=</span><span class="hljs-string">"container.xl"</span> py<span class="hljs-operator">=</span>{<span class="hljs-number">8</span>}<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>VStack spacing<span class="hljs-operator">=</span>{<span class="hljs-number">8</span>}<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Heading<span class="hljs-operator">></span>Monster NFT Game<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Heading<span class="hljs-operator">></span>

        {<span class="hljs-operator">!</span>account ? (
          <span class="hljs-operator">&#x3C;</span>Button onClick<span class="hljs-operator">=</span>{connectWallet} colorScheme<span class="hljs-operator">=</span><span class="hljs-string">"blue"</span><span class="hljs-operator">></span>
            Connect Wallet
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
        ) : (
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>Button onClick<span class="hljs-operator">=</span>{mintMonster} colorScheme<span class="hljs-operator">=</span><span class="hljs-string">"green"</span><span class="hljs-operator">></span>
              Mint New Monster
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>

            <span class="hljs-operator">&#x3C;</span>Grid
              templateColumns<span class="hljs-operator">=</span><span class="hljs-string">"repeat(auto-fill, minmax(250px, 1fr))"</span>
              gap<span class="hljs-operator">=</span>{<span class="hljs-number">6</span>}
            <span class="hljs-operator">></span>
              {monsters.map((monster, index) <span class="hljs-operator">=</span><span class="hljs-operator">></span> (
                <span class="hljs-operator">&#x3C;</span>MonsterCard
                  key<span class="hljs-operator">=</span>{index}
                  tokenId<span class="hljs-operator">=</span>{index}
                  monster<span class="hljs-operator">=</span>{monster}
                  onBattle<span class="hljs-operator">=</span>{(tokenId) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
                    <span class="hljs-comment">// Implement battle logic</span>
                  }}
                <span class="hljs-operator">/</span><span class="hljs-operator">></span>
              ))}
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Grid<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
        )}
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>VStack<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Container<span class="hljs-operator">></span>
  );
}
</code></pre><h2 id="h-testing-and-deployment" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Testing and Deployment</h2><h3 id="h-1-write-tests" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Write Tests</h3><p>Create <code>test/MonsterNFT.test.js</code>:</p><pre data-type="codeBlock" text="const { expect } = require(&quot;chai&quot;);
const { ethers } = require(&quot;hardhat&quot;);

describe(&quot;MonsterNFT&quot;, function () {
  let MonsterNFT;
  let monsterNFT;
  let owner;
  let addr1;
  let addr2;

  beforeEach(async function () {
    [owner, addr1, addr2] = await ethers.getSigners();
    MonsterNFT = await ethers.getContractFactory(&quot;MonsterNFT&quot;);
    monsterNFT = await MonsterNFT.deploy();
    await monsterNFT.deployed();
  });

  describe(&quot;Minting&quot;, function () {
    it(&quot;Should mint a new monster&quot;, async function () {
      const mintPrice = ethers.utils.parseEther(&quot;0.01&quot;);

      await expect(
        monsterNFT
          .connect(addr1)
          .mintMonster(&quot;Test Monster&quot;, &quot;ipfs://test&quot;, { value: mintPrice })
      )
        .to.emit(monsterNFT, &quot;MonsterMinted&quot;)
        .withArgs(addr1.address, 1, &quot;Test Monster&quot;);

      const monster = await monsterNFT.getMonster(1);
      expect(monster.name).to.equal(&quot;Test Monster&quot;);
    });
  });

  describe(&quot;Battles&quot;, function () {
    it(&quot;Should allow monsters to battle&quot;, async function () {
      const mintPrice = ethers.utils.parseEther(&quot;0.01&quot;);

      // Mint two monsters
      await monsterNFT
        .connect(addr1)
        .mintMonster(&quot;Attacker&quot;, &quot;ipfs://attacker&quot;, { value: mintPrice });

      await monsterNFT
        .connect(addr2)
        .mintMonster(&quot;Defender&quot;, &quot;ipfs://defender&quot;, { value: mintPrice });

      // Battle
      await monsterNFT.connect(addr1).battle(1, 2);

      const monster1 = await monsterNFT.getMonster(1);
      expect(monster1.experience).to.be.gt(0);
    });
  });
});
"><code>const { expect } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"chai"</span>);
const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

describe(<span class="hljs-string">"MonsterNFT"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  let MonsterNFT;
  let monsterNFT;
  let owner;
  let addr1;
  let addr2;

  beforeEach(async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    [owner, addr1, addr2] <span class="hljs-operator">=</span> await ethers.getSigners();
    MonsterNFT <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"MonsterNFT"</span>);
    monsterNFT <span class="hljs-operator">=</span> await MonsterNFT.deploy();
    await monsterNFT.deployed();
  });

  describe(<span class="hljs-string">"Minting"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    it(<span class="hljs-string">"Should mint a new monster"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      const mintPrice <span class="hljs-operator">=</span> ethers.utils.parseEther(<span class="hljs-string">"0.01"</span>);

      await expect(
        monsterNFT
          .connect(addr1)
          .mintMonster(<span class="hljs-string">"Test Monster"</span>, <span class="hljs-string">"ipfs://test"</span>, { <span class="hljs-built_in">value</span>: mintPrice })
      )
        .to.emit(monsterNFT, <span class="hljs-string">"MonsterMinted"</span>)
        .withArgs(addr1.<span class="hljs-built_in">address</span>, <span class="hljs-number">1</span>, <span class="hljs-string">"Test Monster"</span>);

      const monster <span class="hljs-operator">=</span> await monsterNFT.getMonster(<span class="hljs-number">1</span>);
      expect(monster.<span class="hljs-built_in">name</span>).to.equal(<span class="hljs-string">"Test Monster"</span>);
    });
  });

  describe(<span class="hljs-string">"Battles"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    it(<span class="hljs-string">"Should allow monsters to battle"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      const mintPrice <span class="hljs-operator">=</span> ethers.utils.parseEther(<span class="hljs-string">"0.01"</span>);

      <span class="hljs-comment">// Mint two monsters</span>
      await monsterNFT
        .connect(addr1)
        .mintMonster(<span class="hljs-string">"Attacker"</span>, <span class="hljs-string">"ipfs://attacker"</span>, { <span class="hljs-built_in">value</span>: mintPrice });

      await monsterNFT
        .connect(addr2)
        .mintMonster(<span class="hljs-string">"Defender"</span>, <span class="hljs-string">"ipfs://defender"</span>, { <span class="hljs-built_in">value</span>: mintPrice });

      <span class="hljs-comment">// Battle</span>
      await monsterNFT.connect(addr1).battle(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);

      const monster1 <span class="hljs-operator">=</span> await monsterNFT.getMonster(<span class="hljs-number">1</span>);
      expect(monster1.experience).to.be.gt(<span class="hljs-number">0</span>);
    });
  });
});
</code></pre><h3 id="h-2-deploy-script" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Deploy Script</h3><p>Create <code>scripts/deploy.js</code>:</p><pre data-type="codeBlock" text="const hre = require(&quot;hardhat&quot;);

async function main() {
  const MonsterNFT = await hre.ethers.getContractFactory(&quot;MonsterNFT&quot;);
  const monsterNFT = await MonsterNFT.deploy();
  await monsterNFT.deployed();

  console.log(&quot;MonsterNFT deployed to:&quot;, monsterNFT.address);

  const BattleArena = await hre.ethers.getContractFactory(&quot;BattleArena&quot;);
  const battleArena = await BattleArena.deploy(monsterNFT.address);
  await battleArena.deployed();

  console.log(&quot;BattleArena deployed to:&quot;, battleArena.address);
}

main()
  .then(() =&gt; process.exit(0))
  .catch((error) =&gt; {
    console.error(error);
    process.exit(1);
  });
"><code>const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  const MonsterNFT <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"MonsterNFT"</span>);
  const monsterNFT <span class="hljs-operator">=</span> await MonsterNFT.deploy();
  await monsterNFT.deployed();

  console.log(<span class="hljs-string">"MonsterNFT deployed to:"</span>, monsterNFT.<span class="hljs-built_in">address</span>);

  const BattleArena <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"BattleArena"</span>);
  const battleArena <span class="hljs-operator">=</span> await BattleArena.deploy(monsterNFT.<span class="hljs-built_in">address</span>);
  await battleArena.deployed();

  console.log(<span class="hljs-string">"BattleArena deployed to:"</span>, battleArena.<span class="hljs-built_in">address</span>);
}

main()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> process.exit(<span class="hljs-number">0</span>))
  .catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
    console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  });
</code></pre><h3 id="h-3-deployment-commands" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. Deployment Commands</h3><pre data-type="codeBlock" text="# Compile contracts
npx hardhat compile

# Run tests
npx hardhat test

# Deploy to Sei testnet
npx hardhat run scripts/deploy.js --network sei
"><code><span class="hljs-comment"># Compile contracts</span>
npx hardhat compile

<span class="hljs-comment"># Run tests</span>
npx hardhat <span class="hljs-built_in">test</span>

<span class="hljs-comment"># Deploy to Sei testnet</span>
npx hardhat run scripts/deploy.js --network sei
</code></pre><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>In this tutorial, we&apos;ve built a full-stack NFT monster game on the Sei blockchain. We&apos;ve covered:</p><ol><li><p>Setting up the development environment</p></li><li><p>Creating smart contracts for NFTs and battles</p></li><li><p>Building a frontend with Next.js</p></li><li><p>Implementing game mechanics</p></li><li><p>Testing and deployment</p></li></ol><p>To extend this game, you could:</p><ul><li><p>Add more complex battle mechanics</p></li><li><p>Implement a marketplace for trading monsters</p></li><li><p>Add special abilities and items</p></li><li><p>Create a breeding system</p></li><li><p>Implement a tournament system</p></li></ul><h2 id="h-additional-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Additional Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.sei.io">Sei Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hardhat.org/getting-started/">Hardhat Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts">OpenZeppelin Contracts</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ethers.org/">Ethers.js Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://nextjs.org/docs">Next.js Documentation</a></p></li></ul>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 8 - How to create dynamic nft's on the sei blockchain]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-8-how-to-create-dynamic-nft-s-on-the-sei-blockchain</link>
            <guid>tH5fag56V6gqI64Twi48</guid>
            <pubDate>Tue, 17 Jun 2025 12:31:27 GMT</pubDate>
            <description><![CDATA[Creating Dynamic NFTs on the Sei Blockchain: A Comprehensive GuideIntroductionDynamic NFTs (dNFTs) are a fascinating evolution of traditional NFTs that can change their properties over time based on certain conditions or external inputs. On the Sei blockchain, we can leverage the EVM compatibility to create sophisticated dynamic NFTs using Solidity smart contracts. This tutorial will guide you through the process of creating, deploying, and interacting with dynamic NFTs on Sei.PrerequisitesBe...]]></description>
            <content:encoded><![CDATA[<h1 id="h-creating-dynamic-nfts-on-the-sei-blockchain-a-comprehensive-guide" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Creating Dynamic NFTs on the Sei Blockchain: A Comprehensive Guide</h1><h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>Dynamic NFTs (dNFTs) are a fascinating evolution of traditional NFTs that can change their properties over time based on certain conditions or external inputs. On the Sei blockchain, we can leverage the EVM compatibility to create sophisticated dynamic NFTs using Solidity smart contracts. This tutorial will guide you through the process of creating, deploying, and interacting with dynamic NFTs on Sei.</p><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before we begin, ensure you have the following installed:</p><ul><li><p>Node.js (v16 or higher)</p></li><li><p>npm or yarn</p></li><li><p>MetaMask wallet</p></li><li><p>Git</p></li></ul><h2 id="h-setting-up-the-development-environment" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Setting Up the Development Environment</h2><ol><li><p>First, let&apos;s create a new Hardhat project:</p></li></ol><pre data-type="codeBlock" text="mkdir sei-dynamic-nft
cd sei-dynamic-nft
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @openzeppelin/contracts dotenv
npx hardhat init
"><code>mkdir sei<span class="hljs-operator">-</span>dynamic<span class="hljs-operator">-</span>nft
cd sei<span class="hljs-operator">-</span>dynamic<span class="hljs-operator">-</span>nft
npm init <span class="hljs-operator">-</span>y
npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save<span class="hljs-operator">-</span>dev hardhat @nomicfoundation<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>toolbox @openzeppelin<span class="hljs-operator">/</span>contracts dotenv
npx hardhat init
</code></pre><ol><li><p>Create a <code>.env</code> file in your project root:</p></li></ol><pre data-type="codeBlock" text="PRIVATE_KEY=your_private_key_here
SEI_TESTNET_RPC=https://evm-rpc-testnet.sei.io
SEI_MAINNET_RPC=https://evm-rpc.sei.io
"><code><span class="hljs-attr">PRIVATE_KEY</span>=your_private_key_here
<span class="hljs-attr">SEI_TESTNET_RPC</span>=https://evm-rpc-testnet.sei.io
<span class="hljs-attr">SEI_MAINNET_RPC</span>=https://evm-rpc.sei.io
</code></pre><h2 id="h-understanding-dynamic-nfts" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding Dynamic NFTs</h2><p>Dynamic NFTs differ from static NFTs in that they can:</p><ul><li><p>Change their metadata over time</p></li><li><p>Update their properties based on external conditions</p></li><li><p>Evolve based on user interactions</p></li><li><p>Respond to on-chain or off-chain events</p></li></ul><h2 id="h-implementing-a-dynamic-nft-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Implementing a Dynamic NFT Contract</h2><p>Let&apos;s create a dynamic NFT that evolves based on time and user interactions. We&apos;ll implement a &quot;Growing Plant&quot; NFT that changes its appearance based on how well it&apos;s taken care of.</p><ol><li><p>Create a new file <code>contracts/GrowingPlantNFT.sol</code>:</p></li></ol><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol&quot;;
import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;;

contract GrowingPlantNFT is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    // Plant stages
    enum PlantStage { SEED, SPROUT, SEEDLING, MATURE }

    // Plant struct to store plant data
    struct Plant {
        PlantStage stage;
        uint256 lastWatered;
        uint256 health;
        uint256 growth;
    }

    // Mapping from token ID to Plant
    mapping(uint256 =&gt; Plant) public plants;

    // Constants
    uint256 public constant WATERING_COOLDOWN = 1 days;
    uint256 public constant GROWTH_THRESHOLD = 100;

    // Events
    event PlantWatered(uint256 tokenId, uint256 newHealth);
    event PlantEvolved(uint256 tokenId, PlantStage newStage);

    constructor() ERC721(&quot;Growing Plant NFT&quot;, &quot;PLANT&quot;) Ownable(msg.sender) {}

    function mintPlant(address recipient) public returns (uint256) {
        _tokenIds.increment();
        uint256 newTokenId = _tokenIds.current();

        // Initialize new plant
        plants[newTokenId] = Plant({
            stage: PlantStage.SEED,
            lastWatered: block.timestamp,
            health: 100,
            growth: 0
        });

        _mint(recipient, newTokenId);
        _setTokenURI(newTokenId, _getPlantURI(PlantStage.SEED));

        return newTokenId;
    }

    function waterPlant(uint256 tokenId) public {
        require(_exists(tokenId), &quot;Plant does not exist&quot;);
        require(ownerOf(tokenId) == msg.sender, &quot;Not the plant owner&quot;);
        require(block.timestamp &gt;= plants[tokenId].lastWatered + WATERING_COOLDOWN, &quot;Too soon to water&quot;);

        Plant storage plant = plants[tokenId];
        plant.lastWatered = block.timestamp;
        plant.health = 100;
        plant.growth += 10;

        // Check if plant should evolve
        if (plant.growth &gt;= GROWTH_THRESHOLD) {
            _evolvePlant(tokenId);
        }

        emit PlantWatered(tokenId, plant.health);
    }

    function _evolvePlant(uint256 tokenId) internal {
        Plant storage plant = plants[tokenId];

        if (plant.stage == PlantStage.SEED) {
            plant.stage = PlantStage.SPROUT;
        } else if (plant.stage == PlantStage.SPROUT) {
            plant.stage = PlantStage.SEEDLING;
        } else if (plant.stage == PlantStage.SEEDLING) {
            plant.stage = PlantStage.MATURE;
        }

        _setTokenURI(tokenId, _getPlantURI(plant.stage));
        emit PlantEvolved(tokenId, plant.stage);
    }

    function _getPlantURI(PlantStage stage) internal pure returns (string memory) {
        if (stage == PlantStage.SEED) {
            return &quot;ipfs://QmSeedURI&quot;;
        } else if (stage == PlantStage.SPROUT) {
            return &quot;ipfs://QmSproutURI&quot;;
        } else if (stage == PlantStage.SEEDLING) {
            return &quot;ipfs://QmSeedlingURI&quot;;
        } else {
            return &quot;ipfs://QmMatureURI&quot;;
        }
    }

    function getPlantDetails(uint256 tokenId) public view returns (
        PlantStage stage,
        uint256 lastWatered,
        uint256 health,
        uint256 growth
    ) {
        require(_exists(tokenId), &quot;Plant does not exist&quot;);
        Plant memory plant = plants[tokenId];
        return (plant.stage, plant.lastWatered, plant.health, plant.growth);
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/Counters.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">GrowingPlantNFT</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721URIStorage</span>, <span class="hljs-title">Ownable</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">Counters</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Counters</span>.<span class="hljs-title">Counter</span>;
    Counters.Counter <span class="hljs-keyword">private</span> _tokenIds;

    <span class="hljs-comment">// Plant stages</span>
    <span class="hljs-keyword">enum</span> <span class="hljs-title">PlantStage</span> { SEED, SPROUT, SEEDLING, MATURE }

    <span class="hljs-comment">// Plant struct to store plant data</span>
    <span class="hljs-keyword">struct</span> <span class="hljs-title">Plant</span> {
        PlantStage stage;
        <span class="hljs-keyword">uint256</span> lastWatered;
        <span class="hljs-keyword">uint256</span> health;
        <span class="hljs-keyword">uint256</span> growth;
    }

    <span class="hljs-comment">// Mapping from token ID to Plant</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> Plant) <span class="hljs-keyword">public</span> plants;

    <span class="hljs-comment">// Constants</span>
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> WATERING_COOLDOWN <span class="hljs-operator">=</span> <span class="hljs-number">1</span> <span class="hljs-literal">days</span>;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">constant</span> GROWTH_THRESHOLD <span class="hljs-operator">=</span> <span class="hljs-number">100</span>;

    <span class="hljs-comment">// Events</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">PlantWatered</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">uint256</span> newHealth</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">PlantEvolved</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId, PlantStage newStage</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"Growing Plant NFT"</span>, <span class="hljs-string">"PLANT"</span></span>) <span class="hljs-title">Ownable</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span>.sender</span>) </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintPlant</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> recipient</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        _tokenIds.increment();
        <span class="hljs-keyword">uint256</span> newTokenId <span class="hljs-operator">=</span> _tokenIds.current();

        <span class="hljs-comment">// Initialize new plant</span>
        plants[newTokenId] <span class="hljs-operator">=</span> Plant({
            stage: PlantStage.SEED,
            lastWatered: <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>,
            health: <span class="hljs-number">100</span>,
            growth: <span class="hljs-number">0</span>
        });

        _mint(recipient, newTokenId);
        _setTokenURI(newTokenId, _getPlantURI(PlantStage.SEED));

        <span class="hljs-keyword">return</span> newTokenId;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">waterPlant</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(_exists(tokenId), <span class="hljs-string">"Plant does not exist"</span>);
        <span class="hljs-built_in">require</span>(ownerOf(tokenId) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-string">"Not the plant owner"</span>);
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span> <span class="hljs-operator">></span><span class="hljs-operator">=</span> plants[tokenId].lastWatered <span class="hljs-operator">+</span> WATERING_COOLDOWN, <span class="hljs-string">"Too soon to water"</span>);

        Plant <span class="hljs-keyword">storage</span> plant <span class="hljs-operator">=</span> plants[tokenId];
        plant.lastWatered <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;
        plant.health <span class="hljs-operator">=</span> <span class="hljs-number">100</span>;
        plant.growth <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">10</span>;

        <span class="hljs-comment">// Check if plant should evolve</span>
        <span class="hljs-keyword">if</span> (plant.growth <span class="hljs-operator">></span><span class="hljs-operator">=</span> GROWTH_THRESHOLD) {
            _evolvePlant(tokenId);
        }

        <span class="hljs-keyword">emit</span> PlantWatered(tokenId, plant.health);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_evolvePlant</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        Plant <span class="hljs-keyword">storage</span> plant <span class="hljs-operator">=</span> plants[tokenId];

        <span class="hljs-keyword">if</span> (plant.stage <span class="hljs-operator">=</span><span class="hljs-operator">=</span> PlantStage.SEED) {
            plant.stage <span class="hljs-operator">=</span> PlantStage.SPROUT;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (plant.stage <span class="hljs-operator">=</span><span class="hljs-operator">=</span> PlantStage.SPROUT) {
            plant.stage <span class="hljs-operator">=</span> PlantStage.SEEDLING;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (plant.stage <span class="hljs-operator">=</span><span class="hljs-operator">=</span> PlantStage.SEEDLING) {
            plant.stage <span class="hljs-operator">=</span> PlantStage.MATURE;
        }

        _setTokenURI(tokenId, _getPlantURI(plant.stage));
        <span class="hljs-keyword">emit</span> PlantEvolved(tokenId, plant.stage);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_getPlantURI</span>(<span class="hljs-params">PlantStage stage</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">if</span> (stage <span class="hljs-operator">=</span><span class="hljs-operator">=</span> PlantStage.SEED) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"ipfs://QmSeedURI"</span>;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stage <span class="hljs-operator">=</span><span class="hljs-operator">=</span> PlantStage.SPROUT) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"ipfs://QmSproutURI"</span>;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stage <span class="hljs-operator">=</span><span class="hljs-operator">=</span> PlantStage.SEEDLING) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"ipfs://QmSeedlingURI"</span>;
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"ipfs://QmMatureURI"</span>;
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPlantDetails</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">
        PlantStage stage,
        <span class="hljs-keyword">uint256</span> lastWatered,
        <span class="hljs-keyword">uint256</span> health,
        <span class="hljs-keyword">uint256</span> growth
    </span>) </span>{
        <span class="hljs-built_in">require</span>(_exists(tokenId), <span class="hljs-string">"Plant does not exist"</span>);
        Plant <span class="hljs-keyword">memory</span> plant <span class="hljs-operator">=</span> plants[tokenId];
        <span class="hljs-keyword">return</span> (plant.stage, plant.lastWatered, plant.health, plant.growth);
    }
}
</code></pre><h2 id="h-deploying-the-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Deploying the Contract</h2><ol><li><p>Create a deployment script in <code>scripts/deploy.js</code>:</p></li></ol><pre data-type="codeBlock" text="const hre = require(&quot;hardhat&quot;);

async function main() {
  const GrowingPlantNFT = await hre.ethers.getContractFactory(
    &quot;GrowingPlantNFT&quot;
  );
  const plantNFT = await GrowingPlantNFT.deploy();

  await plantNFT.waitForDeployment();

  console.log(&quot;GrowingPlantNFT deployed to:&quot;, await plantNFT.getAddress());
}

main()
  .then(() =&gt; process.exit(0))
  .catch((error) =&gt; {
    console.error(error);
    process.exit(1);
  });
"><code>const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  const GrowingPlantNFT <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(
    <span class="hljs-string">"GrowingPlantNFT"</span>
  );
  const plantNFT <span class="hljs-operator">=</span> await GrowingPlantNFT.deploy();

  await plantNFT.waitForDeployment();

  console.log(<span class="hljs-string">"GrowingPlantNFT deployed to:"</span>, await plantNFT.getAddress());
}

main()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> process.exit(<span class="hljs-number">0</span>))
  .catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
    console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  });
</code></pre><ol><li><p>Update your <code>hardhat.config.js</code>:</p></li></ol><pre data-type="codeBlock" text="require(&quot;@nomicfoundation/hardhat-toolbox&quot;);
require(&quot;dotenv&quot;).config();

module.exports = {
  solidity: &quot;0.8.20&quot;,
  networks: {
    seiTestnet: {
      url: process.env.SEI_TESTNET_RPC,
      accounts: [process.env.PRIVATE_KEY],
    },
    seiMainnet: {
      url: process.env.SEI_MAINNET_RPC,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};
"><code><span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomicfoundation/hardhat-toolbox"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();

module.exports <span class="hljs-operator">=</span> {
  solidity: <span class="hljs-string">"0.8.20"</span>,
  networks: {
    seiTestnet: {
      url: process.env.SEI_TESTNET_RPC,
      accounts: [process.env.PRIVATE_KEY],
    },
    seiMainnet: {
      url: process.env.SEI_MAINNET_RPC,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};
</code></pre><ol><li><p>Deploy to Sei Testnet:</p></li></ol><pre data-type="codeBlock" text="npx hardhat run scripts/deploy.js --network seiTestnet
"><code>npx hardhat run scripts<span class="hljs-operator">/</span>deploy.js <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network seiTestnet
</code></pre><h2 id="h-interacting-with-the-dynamic-nft" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Interacting with the Dynamic NFT</h2><p>Here&apos;s how to interact with your deployed contract using ethers.js:</p><pre data-type="codeBlock" text="const { ethers } = require(&quot;hardhat&quot;);

async function interactWithPlant() {
  const contractAddress = &quot;YOUR_DEPLOYED_CONTRACT_ADDRESS&quot;;
  const plantNFT = await ethers.getContractAt(
    &quot;GrowingPlantNFT&quot;,
    contractAddress
  );

  // Mint a new plant
  const mintTx = await plantNFT.mintPlant(&quot;YOUR_WALLET_ADDRESS&quot;);
  await mintTx.wait();

  // Get the token ID of the newly minted plant
  const tokenId = await plantNFT.tokenOfOwnerByIndex(&quot;YOUR_WALLET_ADDRESS&quot;, 0);

  // Water the plant
  const waterTx = await plantNFT.waterPlant(tokenId);
  await waterTx.wait();

  // Get plant details
  const details = await plantNFT.getPlantDetails(tokenId);
  console.log(&quot;Plant Details:&quot;, details);
}
"><code>const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">interactWithPlant</span>(<span class="hljs-params"></span>) </span>{
  const contractAddress <span class="hljs-operator">=</span> <span class="hljs-string">"YOUR_DEPLOYED_CONTRACT_ADDRESS"</span>;
  const plantNFT <span class="hljs-operator">=</span> await ethers.getContractAt(
    <span class="hljs-string">"GrowingPlantNFT"</span>,
    contractAddress
  );

  <span class="hljs-comment">// Mint a new plant</span>
  const mintTx <span class="hljs-operator">=</span> await plantNFT.mintPlant(<span class="hljs-string">"YOUR_WALLET_ADDRESS"</span>);
  await mintTx.wait();

  <span class="hljs-comment">// Get the token ID of the newly minted plant</span>
  const tokenId <span class="hljs-operator">=</span> await plantNFT.tokenOfOwnerByIndex(<span class="hljs-string">"YOUR_WALLET_ADDRESS"</span>, <span class="hljs-number">0</span>);

  <span class="hljs-comment">// Water the plant</span>
  const waterTx <span class="hljs-operator">=</span> await plantNFT.waterPlant(tokenId);
  await waterTx.wait();

  <span class="hljs-comment">// Get plant details</span>
  const details <span class="hljs-operator">=</span> await plantNFT.getPlantDetails(tokenId);
  console.log(<span class="hljs-string">"Plant Details:"</span>, details);
}
</code></pre><h2 id="h-understanding-the-dynamic-nft-implementation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding the Dynamic NFT Implementation</h2><p>Let&apos;s break down the key components of our dynamic NFT:</p><ol><li><p><strong>Plant Stages</strong>: The NFT evolves through different stages (SEED → SPROUT → SEEDLING → MATURE)</p></li><li><p><strong>Growth Mechanism</strong>: Plants grow when watered, with a cooldown period</p></li><li><p><strong>Health System</strong>: Plants have a health value that decreases over time</p></li><li><p><strong>Evolution System</strong>: Plants evolve to the next stage when they reach the growth threshold</p></li><li><p><strong>Metadata Updates</strong>: The token URI changes as the plant evolves</p></li></ol><h2 id="h-best-practices-for-dynamic-nfts" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Best Practices for Dynamic NFTs</h2><ol><li><p><strong>Gas Optimization</strong>:</p><ul><li><p>Use efficient data structures</p></li><li><p>Implement batch operations when possible</p></li><li><p>Consider using events for off-chain tracking</p></li></ul></li><li><p><strong>Security Considerations</strong>:</p><ul><li><p>Implement proper access controls</p></li><li><p>Add checks for reentrancy</p></li><li><p>Validate all inputs</p></li><li><p>Use safe math operations</p></li></ul></li><li><p><strong>Metadata Management</strong>:</p><ul><li><p>Store metadata on IPFS or similar decentralized storage</p></li><li><p>Use a metadata server for dynamic content</p></li><li><p>Implement proper URI management</p></li></ul></li></ol><h2 id="h-testing-your-dynamic-nft" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Testing Your Dynamic NFT</h2><p>Create a test file <code>test/GrowingPlantNFT.test.js</code>:</p><pre data-type="codeBlock" text="const { expect } = require(&quot;chai&quot;);
const { ethers } = require(&quot;hardhat&quot;);

describe(&quot;GrowingPlantNFT&quot;, function () {
  let plantNFT;
  let owner;
  let addr1;

  beforeEach(async function () {
    [owner, addr1] = await ethers.getSigners();
    const GrowingPlantNFT = await ethers.getContractFactory(&quot;GrowingPlantNFT&quot;);
    plantNFT = await GrowingPlantNFT.deploy();
  });

  it(&quot;Should mint a new plant&quot;, async function () {
    await plantNFT.mintPlant(addr1.address);
    expect(await plantNFT.ownerOf(1)).to.equal(addr1.address);
  });

  it(&quot;Should evolve plant after watering&quot;, async function () {
    await plantNFT.mintPlant(addr1.address);
    // Water plant multiple times to trigger evolution
    for (let i = 0; i &lt; 10; i++) {
      await plantNFT.connect(addr1).waterPlant(1);
    }
    const details = await plantNFT.getPlantDetails(1);
    expect(details.stage).to.equal(1); // Should be SPROUT stage
  });
});
"><code>const { expect } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"chai"</span>);
const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

describe(<span class="hljs-string">"GrowingPlantNFT"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  let plantNFT;
  let owner;
  let addr1;

  beforeEach(async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    [owner, addr1] <span class="hljs-operator">=</span> await ethers.getSigners();
    const GrowingPlantNFT <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"GrowingPlantNFT"</span>);
    plantNFT <span class="hljs-operator">=</span> await GrowingPlantNFT.deploy();
  });

  it(<span class="hljs-string">"Should mint a new plant"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    await plantNFT.mintPlant(addr1.<span class="hljs-built_in">address</span>);
    expect(await plantNFT.ownerOf(<span class="hljs-number">1</span>)).to.equal(addr1.<span class="hljs-built_in">address</span>);
  });

  it(<span class="hljs-string">"Should evolve plant after watering"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    await plantNFT.mintPlant(addr1.<span class="hljs-built_in">address</span>);
    <span class="hljs-comment">// Water plant multiple times to trigger evolution</span>
    <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">10</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
      await plantNFT.connect(addr1).waterPlant(<span class="hljs-number">1</span>);
    }
    const details <span class="hljs-operator">=</span> await plantNFT.getPlantDetails(<span class="hljs-number">1</span>);
    expect(details.stage).to.equal(<span class="hljs-number">1</span>); <span class="hljs-comment">// Should be SPROUT stage</span>
  });
});
</code></pre><p>Run the tests with:</p><pre data-type="codeBlock" text="npx hardhat test
"><code>npx hardhat <span class="hljs-built_in">test</span>
</code></pre><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>This tutorial has covered the creation of a dynamic NFT on the Sei blockchain, implementing a growing plant that evolves based on user interaction. The contract demonstrates key concepts of dynamic NFTs:</p><ul><li><p>State management</p></li><li><p>User interaction</p></li><li><p>Evolution mechanics</p></li><li><p>Metadata updates</p></li><li><p>Event emission</p></li></ul><p>You can extend this implementation by:</p><ul><li><p>Adding more complex evolution mechanics</p></li><li><p>Implementing off-chain metadata updates</p></li><li><p>Adding more interaction types</p></li><li><p>Creating a frontend interface</p></li><li><p>Implementing a reward system</p></li></ul><p>Remember to thoroughly test your contract before deploying to mainnet and consider the gas costs of different operations.</p><h2 id="h-additional-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Additional Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.sei.io/evm/evm-hardhat">Sei EVM Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts">OpenZeppelin Contracts</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hardhat.org/getting-started/">Hardhat Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ipfs.io/">IPFS Documentation</a></p></li></ul>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 7 - Real World assets(RWA's) as nft's on the sei blockchain]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-7-real-world-assets-rwa-s-as-nft-s-on-the-sei-blockchain</link>
            <guid>hYHYITBzuiWb8HDpfmxa</guid>
            <pubDate>Tue, 17 Jun 2025 12:30:54 GMT</pubDate>
            <description><![CDATA[Real World Assets as NFTs on the Sei Blockchain: A Comprehensive GuideIntroductionReal World Assets (RWAs) tokenization is revolutionizing how we think about asset ownership and trading. By representing physical assets as NFTs on the blockchain, we can create more liquid, transparent, and accessible markets. This guide will walk you through implementing RWAs as NFTs on the Sei blockchain, leveraging its high performance and EVM compatibility.PrerequisitesBefore we begin, ensure you have:Node....]]></description>
            <content:encoded><![CDATA[<h1 id="h-real-world-assets-as-nfts-on-the-sei-blockchain-a-comprehensive-guide" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Real World Assets as NFTs on the Sei Blockchain: A Comprehensive Guide</h1><h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>Real World Assets (RWAs) tokenization is revolutionizing how we think about asset ownership and trading. By representing physical assets as NFTs on the blockchain, we can create more liquid, transparent, and accessible markets. This guide will walk you through implementing RWAs as NFTs on the Sei blockchain, leveraging its high performance and EVM compatibility.</p><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before we begin, ensure you have:</p><ul><li><p>Node.js and npm installed</p></li><li><p>Basic understanding of Solidity and smart contracts</p></li><li><p>A code editor (VS Code recommended)</p></li><li><p>MetaMask or another Web3 wallet</p></li><li><p>Some testnet SEI tokens (from Sei faucet)</p></li></ul><h2 id="h-project-setup" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Project Setup</h2><ol><li><p>First, let&apos;s create a new Hardhat project:</p></li></ol><pre data-type="codeBlock" text="mkdir sei-rwa-nft
cd sei-rwa-nft
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @openzeppelin/contracts dotenv
npx hardhat init
"><code>mkdir sei<span class="hljs-operator">-</span>rwa<span class="hljs-operator">-</span>nft
cd sei<span class="hljs-operator">-</span>rwa<span class="hljs-operator">-</span>nft
npm init <span class="hljs-operator">-</span>y
npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save<span class="hljs-operator">-</span>dev hardhat @nomicfoundation<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>toolbox @openzeppelin<span class="hljs-operator">/</span>contracts dotenv
npx hardhat init
</code></pre><ol><li><p>Create a <code>.env</code> file for your environment variables:</p></li></ol><pre data-type="codeBlock" text="PRIVATE_KEY=your_private_key_here
SEI_TESTNET_RPC_URL=https://rpc-testnet.sei.io
ETHERSCAN_API_KEY=your_etherscan_api_key
"><code><span class="hljs-attr">PRIVATE_KEY</span>=your_private_key_here
<span class="hljs-attr">SEI_TESTNET_RPC_URL</span>=https://rpc-testnet.sei.io
<span class="hljs-attr">ETHERSCAN_API_KEY</span>=your_etherscan_api_key
</code></pre><ol><li><p>Update your <code>hardhat.config.js</code>:</p></li></ol><pre data-type="codeBlock" text="require(&quot;@nomicfoundation/hardhat-toolbox&quot;);
require(&quot;dotenv&quot;).config();

module.exports = {
  solidity: &quot;0.8.20&quot;,
  networks: {
    seitestnet: {
      url: process.env.SEI_TESTNET_RPC_URL,
      accounts: [process.env.PRIVATE_KEY],
      chainId: 713715,
    },
  },
  etherscan: {
    apiKey: process.env.ETHERSCAN_API_KEY,
  },
};
"><code><span class="hljs-built_in">require</span>(<span class="hljs-string">"@nomicfoundation/hardhat-toolbox"</span>);
<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();

module.exports <span class="hljs-operator">=</span> {
  solidity: <span class="hljs-string">"0.8.20"</span>,
  networks: {
    seitestnet: {
      url: process.env.SEI_TESTNET_RPC_URL,
      accounts: [process.env.PRIVATE_KEY],
      chainId: <span class="hljs-number">713715</span>,
    },
  },
  etherscan: {
    apiKey: process.env.ETHERSCAN_API_KEY,
  },
};
</code></pre><h2 id="h-implementing-the-rwa-nft-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Implementing the RWA NFT Contract</h2><p>Let&apos;s create a smart contract that represents real estate properties as NFTs. We&apos;ll use the ERC721 standard with additional metadata for property details.</p><p>Create a new file <code>contracts/RealEstateNFT.sol</code>:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import &quot;@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol&quot;;
import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;@openzeppelin/contracts/utils/Counters.sol&quot;;

contract RealEstateNFT is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    // Struct to store property details
    struct PropertyDetails {
        string propertyAddress;
        uint256 squareFootage;
        uint256 purchasePrice;
        string propertyType; // residential, commercial, etc.
        uint256 yearBuilt;
        bool isVerified;
    }

    // Mapping from token ID to property details
    mapping(uint256 =&gt; PropertyDetails) public properties;

    // Events
    event PropertyMinted(uint256 tokenId, address owner, string propertyAddress);
    event PropertyVerified(uint256 tokenId, bool isVerified);

    constructor() ERC721(&quot;Real Estate NFT&quot;, &quot;RENFT&quot;) Ownable(msg.sender) {}

    function mintProperty(
        address to,
        string memory tokenURI,
        string memory propertyAddress,
        uint256 squareFootage,
        uint256 purchasePrice,
        string memory propertyType,
        uint256 yearBuilt
    ) public onlyOwner returns (uint256) {
        _tokenIds.increment();
        uint256 newTokenId = _tokenIds.current();

        _mint(to, newTokenId);
        _setTokenURI(newTokenId, tokenURI);

        properties[newTokenId] = PropertyDetails({
            propertyAddress: propertyAddress,
            squareFootage: squareFootage,
            purchasePrice: purchasePrice,
            propertyType: propertyType,
            yearBuilt: yearBuilt,
            isVerified: false
        });

        emit PropertyMinted(newTokenId, to, propertyAddress);
        return newTokenId;
    }

    function verifyProperty(uint256 tokenId) public onlyOwner {
        require(_exists(tokenId), &quot;Property does not exist&quot;);
        properties[tokenId].isVerified = true;
        emit PropertyVerified(tokenId, true);
    }

    function getPropertyDetails(uint256 tokenId)
        public
        view
        returns (
            string memory propertyAddress,
            uint256 squareFootage,
            uint256 purchasePrice,
            string memory propertyType,
            uint256 yearBuilt,
            bool isVerified
        )
    {
        require(_exists(tokenId), &quot;Property does not exist&quot;);
        PropertyDetails memory property = properties[tokenId];
        return (
            property.propertyAddress,
            property.squareFootage,
            property.purchasePrice,
            property.propertyType,
            property.yearBuilt,
            property.isVerified
        );
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/utils/Counters.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">RealEstateNFT</span> <span class="hljs-keyword">is</span> <span class="hljs-title">ERC721URIStorage</span>, <span class="hljs-title">Ownable</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">Counters</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">Counters</span>.<span class="hljs-title">Counter</span>;
    Counters.Counter <span class="hljs-keyword">private</span> _tokenIds;

    <span class="hljs-comment">// Struct to store property details</span>
    <span class="hljs-keyword">struct</span> <span class="hljs-title">PropertyDetails</span> {
        <span class="hljs-keyword">string</span> propertyAddress;
        <span class="hljs-keyword">uint256</span> squareFootage;
        <span class="hljs-keyword">uint256</span> purchasePrice;
        <span class="hljs-keyword">string</span> propertyType; <span class="hljs-comment">// residential, commercial, etc.</span>
        <span class="hljs-keyword">uint256</span> yearBuilt;
        <span class="hljs-keyword">bool</span> isVerified;
    }

    <span class="hljs-comment">// Mapping from token ID to property details</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> PropertyDetails) <span class="hljs-keyword">public</span> properties;

    <span class="hljs-comment">// Events</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">PropertyMinted</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">address</span> owner, <span class="hljs-keyword">string</span> propertyAddress</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">PropertyVerified</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">bool</span> isVerified</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) <span class="hljs-title">ERC721</span>(<span class="hljs-params"><span class="hljs-string">"Real Estate NFT"</span>, <span class="hljs-string">"RENFT"</span></span>) <span class="hljs-title">Ownable</span>(<span class="hljs-params"><span class="hljs-built_in">msg</span>.sender</span>) </span>{}

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintProperty</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> to,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> tokenURI,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> propertyAddress,
        <span class="hljs-keyword">uint256</span> squareFootage,
        <span class="hljs-keyword">uint256</span> purchasePrice,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> propertyType,
        <span class="hljs-keyword">uint256</span> yearBuilt
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        _tokenIds.increment();
        <span class="hljs-keyword">uint256</span> newTokenId <span class="hljs-operator">=</span> _tokenIds.current();

        _mint(to, newTokenId);
        _setTokenURI(newTokenId, tokenURI);

        properties[newTokenId] <span class="hljs-operator">=</span> PropertyDetails({
            propertyAddress: propertyAddress,
            squareFootage: squareFootage,
            purchasePrice: purchasePrice,
            propertyType: propertyType,
            yearBuilt: yearBuilt,
            isVerified: <span class="hljs-literal">false</span>
        });

        <span class="hljs-keyword">emit</span> PropertyMinted(newTokenId, to, propertyAddress);
        <span class="hljs-keyword">return</span> newTokenId;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyProperty</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-built_in">require</span>(_exists(tokenId), <span class="hljs-string">"Property does not exist"</span>);
        properties[tokenId].isVerified <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
        <span class="hljs-keyword">emit</span> PropertyVerified(tokenId, <span class="hljs-literal">true</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPropertyDetails</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId</span>)
        <span class="hljs-title"><span class="hljs-keyword">public</span></span>
        <span class="hljs-title"><span class="hljs-keyword">view</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">
            <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> propertyAddress,
            <span class="hljs-keyword">uint256</span> squareFootage,
            <span class="hljs-keyword">uint256</span> purchasePrice,
            <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> propertyType,
            <span class="hljs-keyword">uint256</span> yearBuilt,
            <span class="hljs-keyword">bool</span> isVerified
        </span>)
    </span>{
        <span class="hljs-built_in">require</span>(_exists(tokenId), <span class="hljs-string">"Property does not exist"</span>);
        PropertyDetails <span class="hljs-keyword">memory</span> property <span class="hljs-operator">=</span> properties[tokenId];
        <span class="hljs-keyword">return</span> (
            property.propertyAddress,
            property.squareFootage,
            property.purchasePrice,
            property.propertyType,
            property.yearBuilt,
            property.isVerified
        );
    }
}
</code></pre><h2 id="h-deploying-the-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Deploying the Contract</h2><p>Create a deployment script at <code>scripts/deploy.js</code>:</p><pre data-type="codeBlock" text="const hre = require(&quot;hardhat&quot;);

async function main() {
  const RealEstateNFT = await hre.ethers.getContractFactory(&quot;RealEstateNFT&quot;);
  const realEstateNFT = await RealEstateNFT.deploy();

  await realEstateNFT.waitForDeployment();

  console.log(&quot;RealEstateNFT deployed to:&quot;, await realEstateNFT.getAddress());
}

main().catch((error) =&gt; {
  console.error(error);
  process.exitCode = 1;
});
"><code>const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  const RealEstateNFT <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"RealEstateNFT"</span>);
  const realEstateNFT <span class="hljs-operator">=</span> await RealEstateNFT.deploy();

  await realEstateNFT.waitForDeployment();

  console.log(<span class="hljs-string">"RealEstateNFT deployed to:"</span>, await realEstateNFT.getAddress());
}

main().catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
  console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
  process.exitCode <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
});
</code></pre><p>Deploy to Sei testnet:</p><pre data-type="codeBlock" text="npx hardhat run scripts/deploy.js --network seitestnet
"><code>npx hardhat run scripts<span class="hljs-operator">/</span>deploy.js <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network seitestnet
</code></pre><h2 id="h-interacting-with-the-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Interacting with the Contract</h2><p>Here&apos;s an example script to mint a new property NFT (<code>scripts/mint-property.js</code>):</p><pre data-type="codeBlock" text="const hre = require(&quot;hardhat&quot;);

async function main() {
  const contractAddress = &quot;YOUR_DEPLOYED_CONTRACT_ADDRESS&quot;;
  const RealEstateNFT = await hre.ethers.getContractFactory(&quot;RealEstateNFT&quot;);
  const realEstateNFT = await RealEstateNFT.attach(contractAddress);

  // Mint a new property
  const tx = await realEstateNFT.mintProperty(
    &quot;RECIPIENT_ADDRESS&quot;,
    &quot;ipfs://YOUR_METADATA_URI&quot;,
    &quot;123 Blockchain Street, Crypto City&quot;,
    2000, // square footage
    ethers.parseEther(&quot;100&quot;), // purchase price in SEI
    &quot;residential&quot;,
    2023
  );

  await tx.wait();
  console.log(&quot;Property NFT minted successfully!&quot;);
}

main().catch((error) =&gt; {
  console.error(error);
  process.exitCode = 1;
});
"><code>const hre <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  const contractAddress <span class="hljs-operator">=</span> <span class="hljs-string">"YOUR_DEPLOYED_CONTRACT_ADDRESS"</span>;
  const RealEstateNFT <span class="hljs-operator">=</span> await hre.ethers.getContractFactory(<span class="hljs-string">"RealEstateNFT"</span>);
  const realEstateNFT <span class="hljs-operator">=</span> await RealEstateNFT.attach(contractAddress);

  <span class="hljs-comment">// Mint a new property</span>
  const <span class="hljs-built_in">tx</span> <span class="hljs-operator">=</span> await realEstateNFT.mintProperty(
    <span class="hljs-string">"RECIPIENT_ADDRESS"</span>,
    <span class="hljs-string">"ipfs://YOUR_METADATA_URI"</span>,
    <span class="hljs-string">"123 Blockchain Street, Crypto City"</span>,
    <span class="hljs-number">2000</span>, <span class="hljs-comment">// square footage</span>
    ethers.parseEther(<span class="hljs-string">"100"</span>), <span class="hljs-comment">// purchase price in SEI</span>
    <span class="hljs-string">"residential"</span>,
    <span class="hljs-number">2023</span>
  );

  await <span class="hljs-built_in">tx</span>.wait();
  console.log(<span class="hljs-string">"Property NFT minted successfully!"</span>);
}

main().catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
  console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
  process.exitCode <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
});
</code></pre><h2 id="h-metadata-structure" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Metadata Structure</h2><p>For each property NFT, you&apos;ll need to create a metadata JSON file following the NFT metadata standard. Here&apos;s an example:</p><pre data-type="codeBlock" text="{
  &quot;name&quot;: &quot;Blockchain Villa&quot;,
  &quot;description&quot;: &quot;A luxury residential property in Crypto City&quot;,
  &quot;image&quot;: &quot;ipfs://YOUR_IMAGE_HASH&quot;,
  &quot;attributes&quot;: [
    {
      &quot;trait_type&quot;: &quot;Property Type&quot;,
      &quot;value&quot;: &quot;Residential&quot;
    },
    {
      &quot;trait_type&quot;: &quot;Square Footage&quot;,
      &quot;value&quot;: &quot;2000&quot;
    },
    {
      &quot;trait_type&quot;: &quot;Year Built&quot;,
      &quot;value&quot;: &quot;2023&quot;
    },
    {
      &quot;trait_type&quot;: &quot;Location&quot;,
      &quot;value&quot;: &quot;Crypto City&quot;
    }
  ]
}
"><code><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Blockchain Villa"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"description"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A luxury residential property in Crypto City"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"image"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"ipfs://YOUR_IMAGE_HASH"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"attributes"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Property Type"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Residential"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Square Footage"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2000"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Year Built"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2023"</span>
    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
    <span class="hljs-punctuation">{</span>
      <span class="hljs-attr">"trait_type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Location"</span><span class="hljs-punctuation">,</span>
      <span class="hljs-attr">"value"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Crypto City"</span>
    <span class="hljs-punctuation">}</span>
  <span class="hljs-punctuation">]</span>
<span class="hljs-punctuation">}</span>
</code></pre><h2 id="h-best-practices-for-rwa-nfts" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Best Practices for RWA NFTs</h2><h3 id="h-1-verification-process" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Verification Process</h3><ul><li><p>Implement a robust verification system for property details</p></li><li><p>Consider using oracles for real-world data validation</p></li><li><p>Maintain a registry of verified properties</p></li></ul><h3 id="h-2-legal-compliance" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Legal Compliance</h3><ul><li><p>Ensure compliance with local real estate regulations</p></li><li><p>Include necessary legal disclaimers in the smart contract</p></li><li><p>Consider implementing KYC/AML requirements</p></li></ul><h3 id="h-3-security-measures" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. Security Measures</h3><ul><li><p>Use multi-signature wallets for important operations</p></li><li><p>Implement access controls for property verification</p></li><li><p>Regular security audits of the smart contract</p></li></ul><h3 id="h-4-metadata-management" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4. Metadata Management</h3><p>Metadata management is a critical component of RWA tokenization that requires careful consideration and implementation. Here&apos;s a detailed breakdown of best practices:</p><h4 id="h-decentralized-storage-architecture" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Decentralized Storage Architecture</h4><p>The foundation of effective metadata management lies in the proper storage and accessibility of asset information. Using decentralized storage solutions like IPFS (InterPlanetary File System) ensures that metadata remains immutable, accessible, and resistant to censorship. This approach provides several key benefits:</p><ul><li><p><strong>Content Addressing</strong>: Each piece of metadata receives a unique content identifier (CID) based on its content, ensuring that:</p><ul><li><p>Data integrity is maintained</p></li><li><p>Duplicate content is automatically detected</p></li><li><p>Content can be verified cryptographically</p></li><li><p>Updates are tracked through version history</p></li></ul></li><li><p><strong>Distributed Storage</strong>: By distributing metadata across the IPFS network:</p><ul><li><p>Data redundancy is ensured</p></li><li><p>Access speed is optimized</p></li><li><p>Storage costs are reduced</p></li><li><p>Network resilience is improved</p></li></ul></li></ul><h4 id="h-dynamic-update-system" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Dynamic Update System</h4><p>RWA metadata often requires updates to reflect changes in the underlying asset. Implementing a robust update system is crucial:</p><ul><li><p><strong>Version Control</strong>: Maintain a comprehensive version history of metadata changes:</p><ul><li><p>Track all modifications with timestamps</p></li><li><p>Record the identity of updaters</p></li><li><p>Maintain change logs</p></li><li><p>Enable rollback capabilities</p></li></ul></li><li><p><strong>Update Authorization</strong>: Implement a secure update mechanism:</p><ul><li><p>Require authorized signatures for updates</p></li><li><p>Implement multi-signature requirements for critical changes</p></li><li><p>Maintain an audit trail of all modifications</p></li><li><p>Notify relevant stakeholders of changes</p></li></ul></li></ul><h4 id="h-data-integrity-and-verification" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Data Integrity and Verification</h4><p>Ensuring the integrity of metadata is paramount for maintaining trust in the system:</p><ul><li><p><strong>Cryptographic Verification</strong>: Implement cryptographic proofs to verify metadata:</p><ul><li><p>Use merkle trees for efficient verification</p></li><li><p>Implement digital signatures for updates</p></li><li><p>Maintain proof of existence</p></li><li><p>Enable zero-knowledge verification where appropriate</p></li></ul></li><li><p><strong>Consistency Checks</strong>: Regular verification of metadata consistency:</p><ul><li><p>Cross-reference with on-chain data</p></li><li><p>Verify against external sources</p></li><li><p>Maintain data consistency across systems</p></li><li><p>Implement automated validation checks</p></li></ul></li></ul><h4 id="h-metadata-structure-and-standards" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Metadata Structure and Standards</h4><p>Adopting standardized metadata structures ensures interoperability and ease of use:</p><ul><li><p><strong>Schema Definition</strong>: Define clear metadata schemas for different asset types:</p><ul><li><p>Required fields for each asset class</p></li><li><p>Optional attributes</p></li><li><p>Validation rules</p></li><li><p>Format specifications</p></li></ul></li><li><p><strong>Interoperability</strong>: Ensure metadata can be used across different platforms:</p><ul><li><p>Follow industry standards</p></li><li><p>Support multiple metadata formats</p></li><li><p>Enable cross-platform compatibility</p></li><li><p>Maintain backward compatibility</p></li></ul></li></ul><h4 id="h-implementation-example" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Implementation Example</h4><p>Here&apos;s a practical example of how to implement metadata management in your smart contract:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract MetadataManager {
    struct Metadata {
        string ipfsHash;
        uint256 version;
        address lastUpdater;
        uint256 lastUpdateTime;
        bool isVerified;
    }

    mapping(uint256 =&gt; Metadata) public assetMetadata;
    mapping(uint256 =&gt; mapping(uint256 =&gt; string)) public versionHistory;

    event MetadataUpdated(uint256 indexed assetId, uint256 version, string ipfsHash);
    event MetadataVerified(uint256 indexed assetId, bool isVerified);

    function updateMetadata(
        uint256 assetId,
        string memory newIpfsHash
    ) external onlyAuthorized {
        require(bytes(newIpfsHash).length &gt; 0, &quot;Invalid IPFS hash&quot;);

        Metadata storage metadata = assetMetadata[assetId];
        metadata.version += 1;
        metadata.ipfsHash = newIpfsHash;
        metadata.lastUpdater = msg.sender;
        metadata.lastUpdateTime = block.timestamp;

        versionHistory[assetId][metadata.version] = newIpfsHash;

        emit MetadataUpdated(assetId, metadata.version, newIpfsHash);
    }

    function verifyMetadata(uint256 assetId) external onlyVerifier {
        assetMetadata[assetId].isVerified = true;
        emit MetadataVerified(assetId, true);
    }

    function getMetadataHistory(
        uint256 assetId,
        uint256 version
    ) external view returns (string memory) {
        return versionHistory[assetId][version];
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.20;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MetadataManager</span> </span>{
    <span class="hljs-keyword">struct</span> <span class="hljs-title">Metadata</span> {
        <span class="hljs-keyword">string</span> ipfsHash;
        <span class="hljs-keyword">uint256</span> version;
        <span class="hljs-keyword">address</span> lastUpdater;
        <span class="hljs-keyword">uint256</span> lastUpdateTime;
        <span class="hljs-keyword">bool</span> isVerified;
    }

    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> Metadata) <span class="hljs-keyword">public</span> assetMetadata;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">string</span>)) <span class="hljs-keyword">public</span> versionHistory;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">MetadataUpdated</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> assetId, <span class="hljs-keyword">uint256</span> version, <span class="hljs-keyword">string</span> ipfsHash</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">MetadataVerified</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> assetId, <span class="hljs-keyword">bool</span> isVerified</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateMetadata</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> assetId,
        <span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span> newIpfsHash
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyAuthorized</span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-keyword">bytes</span>(newIpfsHash).<span class="hljs-built_in">length</span> <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">"Invalid IPFS hash"</span>);

        Metadata <span class="hljs-keyword">storage</span> metadata <span class="hljs-operator">=</span> assetMetadata[assetId];
        metadata.version <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
        metadata.ipfsHash <span class="hljs-operator">=</span> newIpfsHash;
        metadata.lastUpdater <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
        metadata.lastUpdateTime <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;

        versionHistory[assetId][metadata.version] <span class="hljs-operator">=</span> newIpfsHash;

        <span class="hljs-keyword">emit</span> MetadataUpdated(assetId, metadata.version, newIpfsHash);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyMetadata</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> assetId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyVerifier</span> </span>{
        assetMetadata[assetId].isVerified <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
        <span class="hljs-keyword">emit</span> MetadataVerified(assetId, <span class="hljs-literal">true</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMetadataHistory</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> assetId,
        <span class="hljs-keyword">uint256</span> version
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">string</span> <span class="hljs-keyword">memory</span></span>) </span>{
        <span class="hljs-keyword">return</span> versionHistory[assetId][version];
    }
}
</code></pre><p>This implementation provides:</p><ul><li><p>Version control for metadata updates</p></li><li><p>Historical tracking of changes</p></li><li><p>Verification status tracking</p></li><li><p>Access control for updates</p></li><li><p>Event emission for tracking changes</p></li></ul><p>Remember to:</p><ul><li><p>Regularly backup metadata</p></li><li><p>Implement proper access controls</p></li><li><p>Monitor storage costs</p></li><li><p>Update metadata standards as needed</p></li><li><p>Maintain documentation of metadata structures</p></li></ul><h2 id="h-testing-the-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Testing the Contract</h2><p>Create a test file <code>test/RealEstateNFT.test.js</code>:</p><pre data-type="codeBlock" text="const { expect } = require(&quot;chai&quot;);
const { ethers } = require(&quot;hardhat&quot;);

describe(&quot;RealEstateNFT&quot;, function () {
  let RealEstateNFT;
  let realEstateNFT;
  let owner;
  let addr1;
  let addr2;

  beforeEach(async function () {
    [owner, addr1, addr2] = await ethers.getSigners();
    RealEstateNFT = await ethers.getContractFactory(&quot;RealEstateNFT&quot;);
    realEstateNFT = await RealEstateNFT.deploy();
  });

  describe(&quot;Deployment&quot;, function () {
    it(&quot;Should set the right owner&quot;, async function () {
      expect(await realEstateNFT.owner()).to.equal(owner.address);
    });
  });

  describe(&quot;Minting&quot;, function () {
    it(&quot;Should mint a new property NFT&quot;, async function () {
      const tx = await realEstateNFT.mintProperty(
        addr1.address,
        &quot;ipfs://test&quot;,
        &quot;123 Test St&quot;,
        2000,
        ethers.parseEther(&quot;100&quot;),
        &quot;residential&quot;,
        2023
      );

      await tx.wait();
      expect(await realEstateNFT.ownerOf(1)).to.equal(addr1.address);
    });
  });

  describe(&quot;Property Details&quot;, function () {
    it(&quot;Should return correct property details&quot;, async function () {
      await realEstateNFT.mintProperty(
        addr1.address,
        &quot;ipfs://test&quot;,
        &quot;123 Test St&quot;,
        2000,
        ethers.parseEther(&quot;100&quot;),
        &quot;residential&quot;,
        2023
      );

      const details = await realEstateNFT.getPropertyDetails(1);
      expect(details.propertyAddress).to.equal(&quot;123 Test St&quot;);
      expect(details.squareFootage).to.equal(2000);
    });
  });
});
"><code>const { expect } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"chai"</span>);
const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);

describe(<span class="hljs-string">"RealEstateNFT"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  let RealEstateNFT;
  let realEstateNFT;
  let owner;
  let addr1;
  let addr2;

  beforeEach(async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    [owner, addr1, addr2] <span class="hljs-operator">=</span> await ethers.getSigners();
    RealEstateNFT <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"RealEstateNFT"</span>);
    realEstateNFT <span class="hljs-operator">=</span> await RealEstateNFT.deploy();
  });

  describe(<span class="hljs-string">"Deployment"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    it(<span class="hljs-string">"Should set the right owner"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      expect(await realEstateNFT.owner()).to.equal(owner.<span class="hljs-built_in">address</span>);
    });
  });

  describe(<span class="hljs-string">"Minting"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    it(<span class="hljs-string">"Should mint a new property NFT"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      const <span class="hljs-built_in">tx</span> <span class="hljs-operator">=</span> await realEstateNFT.mintProperty(
        addr1.<span class="hljs-built_in">address</span>,
        <span class="hljs-string">"ipfs://test"</span>,
        <span class="hljs-string">"123 Test St"</span>,
        <span class="hljs-number">2000</span>,
        ethers.parseEther(<span class="hljs-string">"100"</span>),
        <span class="hljs-string">"residential"</span>,
        <span class="hljs-number">2023</span>
      );

      await <span class="hljs-built_in">tx</span>.wait();
      expect(await realEstateNFT.ownerOf(<span class="hljs-number">1</span>)).to.equal(addr1.<span class="hljs-built_in">address</span>);
    });
  });

  describe(<span class="hljs-string">"Property Details"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    it(<span class="hljs-string">"Should return correct property details"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      await realEstateNFT.mintProperty(
        addr1.<span class="hljs-built_in">address</span>,
        <span class="hljs-string">"ipfs://test"</span>,
        <span class="hljs-string">"123 Test St"</span>,
        <span class="hljs-number">2000</span>,
        ethers.parseEther(<span class="hljs-string">"100"</span>),
        <span class="hljs-string">"residential"</span>,
        <span class="hljs-number">2023</span>
      );

      const details <span class="hljs-operator">=</span> await realEstateNFT.getPropertyDetails(<span class="hljs-number">1</span>);
      expect(details.propertyAddress).to.equal(<span class="hljs-string">"123 Test St"</span>);
      expect(details.squareFootage).to.equal(<span class="hljs-number">2000</span>);
    });
  });
});
</code></pre><p>Run the tests:</p><pre data-type="codeBlock" text="npx hardhat test
"><code>npx hardhat <span class="hljs-built_in">test</span>
</code></pre><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>This guide has provided a foundation for implementing Real World Assets as NFTs on the Sei blockchain. The example implementation focuses on real estate, but the same principles can be applied to other types of RWAs such as:</p><ul><li><p>Art and collectibles</p></li><li><p>Commodities</p></li><li><p>Financial instruments</p></li><li><p>Intellectual property</p></li><li><p>Vehicles and equipment</p></li></ul><p>Remember to:</p><ul><li><p>Always conduct thorough testing before deployment</p></li><li><p>Implement proper security measures</p></li><li><p>Consider legal and regulatory requirements</p></li><li><p>Keep documentation up-to-date</p></li><li><p>Monitor contract performance and gas usage</p></li></ul><h2 id="h-additional-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Additional Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.sei.io">Sei Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts">OpenZeppelin Contracts</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ipfs.io">IPFS Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hardhat.org/getting-started">Hardhat Documentation</a></p></li></ul><h2 id="h-disclaimer" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Disclaimer</h2><p>This guide is for educational purposes only. Always consult with legal and financial professionals before implementing RWA tokenization solutions in production.</p>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 6 - Implementing zk proof in a sei blockchain dapp]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-6-implementing-zk-proof-in-a-sei-blockchain-dapp</link>
            <guid>Mq7dQT2cHRr89ukcqrcF</guid>
            <pubDate>Tue, 17 Jun 2025 12:30:04 GMT</pubDate>
            <description><![CDATA[Implementing Zero-Knowledge Proofs in a dApp on SeiIntroductionThis tutorial will guide you through implementing a zero-knowledge proof system in a dApp on the Sei blockchain. We&apos;ll use Hardhat for development and deployment, leveraging Sei&apos;s EVM compatibility to create a privacy-preserving age verification system.PrerequisitesBefore starting, ensure you have:Node.js (v14 or higher)npm or yarnBasic understanding of:React.jsSolidityWeb3.js or ethers.jsBasic cryptography conceptsA Sei...]]></description>
            <content:encoded><![CDATA[<h1 id="h-implementing-zero-knowledge-proofs-in-a-dapp-on-sei" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Implementing Zero-Knowledge Proofs in a dApp on Sei</h1><h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>This tutorial will guide you through implementing a zero-knowledge proof system in a dApp on the Sei blockchain. We&apos;ll use Hardhat for development and deployment, leveraging Sei&apos;s EVM compatibility to create a privacy-preserving age verification system.</p><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before starting, ensure you have:</p><ul><li><p>Node.js (v14 or higher)</p></li><li><p>npm or yarn</p></li><li><p>Basic understanding of:</p><ul><li><p>React.js</p></li><li><p>Solidity</p></li><li><p>Web3.js or ethers.js</p></li><li><p>Basic cryptography concepts</p></li></ul></li><li><p>A Sei wallet with testnet SEI tokens (for testing)</p></li></ul><h2 id="h-project-setup" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Project Setup</h2><ol><li><p>First, let&apos;s create a new project:</p></li></ol><pre data-type="codeBlock" text="mkdir sei-zk-dapp
cd sei-zk-dapp
npm init -y
"><code>mkdir sei<span class="hljs-operator">-</span>zk<span class="hljs-operator">-</span>dapp
cd sei<span class="hljs-operator">-</span>zk<span class="hljs-operator">-</span>dapp
npm init <span class="hljs-operator">-</span>y
</code></pre><ol><li><p>Install necessary dependencies:</p></li></ol><pre data-type="codeBlock" text="npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @openzeppelin/hardhat-upgrades dotenv
npm install ethers@5.7.2 @openzeppelin/contracts circomlib snarkjs @metamask/detect-provider
npm install --save-dev @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle ethereum-waffle chai
"><code>npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save<span class="hljs-operator">-</span>dev hardhat @nomicfoundation<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>toolbox @openzeppelin<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>upgrades dotenv
npm install ethers@<span class="hljs-number">5.7</span><span class="hljs-number">.2</span> @openzeppelin<span class="hljs-operator">/</span>contracts circomlib snarkjs @metamask<span class="hljs-operator">/</span>detect<span class="hljs-operator">-</span>provider
npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save<span class="hljs-operator">-</span>dev @nomiclabs<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>ethers @nomiclabs<span class="hljs-operator">/</span>hardhat<span class="hljs-operator">-</span>waffle ethereum<span class="hljs-operator">-</span>waffle chai
</code></pre><ol><li><p>Initialize Hardhat with Sei configuration:</p></li></ol><pre data-type="codeBlock" text="npx hardhat init
"><code>npx hardhat <span class="hljs-keyword">init</span>
</code></pre><ol><li><p>Create a <code>hardhat.config.js</code> file:</p></li></ol><pre data-type="codeBlock" text="require(&quot;@nomicfoundation/hardhat-toolbox&quot;);
require(&quot;@openzeppelin/hardhat-upgrades&quot;);
require(&quot;dotenv&quot;).config();

module.exports = {
  solidity: {
    version: &quot;0.8.22&quot;,
    settings: {
      optimizer: {
        enabled: true,
        runs: 200,
      },
    },
  },
  networks: {
    seitestnet: {
      url: &quot;https://evm-rpc-testnet.sei.io&quot;,
      chainId: 713715,
      accounts: [process.env.PRIVATE_KEY],
      gasPrice: 20000000000,
    },
    seimainnet: {
      url: &quot;https://evm-rpc.sei.io&quot;,
      chainId: 713715,
      accounts: [process.env.PRIVATE_KEY],
      gasPrice: 20000000000,
    },
  },
  paths: {
    sources: &quot;./contracts&quot;,
    tests: &quot;./test&quot;,
    cache: &quot;./cache&quot;,
    artifacts: &quot;./artifacts&quot;,
  },
};
"><code><span class="hljs-string">require("@nomicfoundation/hardhat-toolbox");</span>
<span class="hljs-string">require("@openzeppelin/hardhat-upgrades");</span>
<span class="hljs-string">require("dotenv").config();</span>

<span class="hljs-string">module.exports</span> <span class="hljs-string">=</span> {
  <span class="hljs-attr">solidity:</span> {
    <span class="hljs-attr">version:</span> <span class="hljs-string">"0.8.22"</span>,
    <span class="hljs-attr">settings:</span> {
      <span class="hljs-attr">optimizer:</span> {
        <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>,
        <span class="hljs-attr">runs:</span> <span class="hljs-number">200</span>,
      },
    },
  },
  <span class="hljs-attr">networks:</span> {
    <span class="hljs-attr">seitestnet:</span> {
      <span class="hljs-attr">url:</span> <span class="hljs-string">"https://evm-rpc-testnet.sei.io"</span>,
      <span class="hljs-attr">chainId:</span> <span class="hljs-number">713715</span>,
      <span class="hljs-attr">accounts:</span> [<span class="hljs-string">process.env.PRIVATE_KEY</span>],
      <span class="hljs-attr">gasPrice:</span> <span class="hljs-number">20000000000</span>,
    },
    <span class="hljs-attr">seimainnet:</span> {
      <span class="hljs-attr">url:</span> <span class="hljs-string">"https://evm-rpc.sei.io"</span>,
      <span class="hljs-attr">chainId:</span> <span class="hljs-number">713715</span>,
      <span class="hljs-attr">accounts:</span> [<span class="hljs-string">process.env.PRIVATE_KEY</span>],
      <span class="hljs-attr">gasPrice:</span> <span class="hljs-number">20000000000</span>,
    },
  },
  <span class="hljs-attr">paths:</span> {
    <span class="hljs-attr">sources:</span> <span class="hljs-string">"./contracts"</span>,
    <span class="hljs-attr">tests:</span> <span class="hljs-string">"./test"</span>,
    <span class="hljs-attr">cache:</span> <span class="hljs-string">"./cache"</span>,
    <span class="hljs-attr">artifacts:</span> <span class="hljs-string">"./artifacts"</span>,
  },
}<span class="hljs-string">;</span>
</code></pre><ol><li><p>Create a <code>.env</code> file:</p></li></ol><pre data-type="codeBlock" text="PRIVATE_KEY=your_private_key_here
SEI_RPC_URL=https://evm-rpc-testnet.sei.io
"><code><span class="hljs-attr">PRIVATE_KEY</span>=your_private_key_here
<span class="hljs-attr">SEI_RPC_URL</span>=https://evm-rpc-testnet.sei.io
</code></pre><h2 id="h-project-structure" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Project Structure</h2><pre data-type="codeBlock" text="sei-zk-dapp/
├── contracts/
│   ├── AgeVerifier.sol
│   └── Verifier.sol
├── circuits/
│   └── ageProof.circom
├── scripts/
│   ├── deploy.ts
│   └── verify.ts
├── src/
│   ├── components/
│   │   ├── AgeVerification.tsx
│   │   └── WalletConnect.tsx
│   ├── utils/
│   │   ├── zkProof.ts
│   │   └── web3.ts
│   └── App.tsx
└── test/
    └── AgeVerifier.test.ts
"><code>sei<span class="hljs-operator">-</span>zk<span class="hljs-operator">-</span>dapp<span class="hljs-operator">/</span>
├── contracts<span class="hljs-operator">/</span>
│   ├── AgeVerifier.sol
│   └── Verifier.sol
├── circuits<span class="hljs-operator">/</span>
│   └── ageProof.circom
├── scripts<span class="hljs-operator">/</span>
│   ├── deploy.ts
│   └── verify.ts
├── src<span class="hljs-operator">/</span>
│   ├── components<span class="hljs-operator">/</span>
│   │   ├── AgeVerification.tsx
│   │   └── WalletConnect.tsx
│   ├── utils<span class="hljs-operator">/</span>
│   │   ├── zkProof.ts
│   │   └── web3.ts
│   └── App.tsx
└── test<span class="hljs-operator">/</span>
    └── AgeVerifier.test.ts
</code></pre><h2 id="h-step-1-creating-the-smart-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 1: Creating the Smart Contract</h2><p>Create <code>contracts/AgeVerifier.sol</code>:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;
import &quot;@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol&quot;;
import &quot;@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol&quot;;
import &quot;./Verifier.sol&quot;;

contract AgeVerifier is Initializable, Ownable, UUPSUpgradeable {
    Verifier public verifier;

    // Mapping to store verified addresses
    mapping(address =&gt; bool) public verifiedAddresses;

    // Event emitted when verification is successful
    event AgeVerified(address indexed user);

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(address _verifier) public initializer {
        __Ownable_init();
        __UUPSUpgradeable_init();
        verifier = Verifier(_verifier);
    }

    function verifyAge(
        uint[2] memory a,
        uint[2][2] memory b,
        uint[2] memory c,
        uint[3] memory input
    ) public returns (bool) {
        // Verify the proof
        require(verifier.verifyProof(a, b, c, input), &quot;Invalid proof&quot;);

        // Mark address as verified
        verifiedAddresses[msg.sender] = true;

        // Emit event
        emit AgeVerified(msg.sender);

        return true;
    }

    function isVerified(address user) public view returns (bool) {
        return verifiedAddresses[user];
    }

    function _authorizeUpgrade(address newImplementation)
        internal
        onlyOwner
        override
    {}
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.22;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./Verifier.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">AgeVerifier</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Initializable</span>, <span class="hljs-title">Ownable</span>, <span class="hljs-title">UUPSUpgradeable</span> </span>{
    Verifier <span class="hljs-keyword">public</span> verifier;

    <span class="hljs-comment">// Mapping to store verified addresses</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>) <span class="hljs-keyword">public</span> verifiedAddresses;

    <span class="hljs-comment">// Event emitted when verification is successful</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">AgeVerified</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> user</span>)</span>;

    <span class="hljs-comment">/// @custom:oz-upgrades-unsafe-allow constructor</span>
    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        _disableInitializers();
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialize</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _verifier</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">initializer</span> </span>{
        __Ownable_init();
        __UUPSUpgradeable_init();
        verifier <span class="hljs-operator">=</span> Verifier(_verifier);
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyAge</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint</span>[<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> a,
        <span class="hljs-keyword">uint</span>[<span class="hljs-number">2</span>][<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> b,
        <span class="hljs-keyword">uint</span>[<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> c,
        <span class="hljs-keyword">uint</span>[<span class="hljs-number">3</span>] <span class="hljs-keyword">memory</span> input
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-comment">// Verify the proof</span>
        <span class="hljs-built_in">require</span>(verifier.verifyProof(a, b, c, input), <span class="hljs-string">"Invalid proof"</span>);

        <span class="hljs-comment">// Mark address as verified</span>
        verifiedAddresses[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;

        <span class="hljs-comment">// Emit event</span>
        <span class="hljs-keyword">emit</span> AgeVerified(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isVerified</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> user</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">return</span> verifiedAddresses[user];
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_authorizeUpgrade</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newImplementation</span>)
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span>
        <span class="hljs-title">onlyOwner</span>
        <span class="hljs-title"><span class="hljs-keyword">override</span></span>
    </span>{}
}
</code></pre><h2 id="h-step-2-creating-the-deployment-script" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 2: Creating the Deployment Script</h2><p>Create <code>scripts/deploy.ts</code>:</p><pre data-type="codeBlock" text="import { ethers, upgrades } from &quot;hardhat&quot;;

async function main() {
  const [deployer] = await ethers.getSigners();
  console.log(&quot;Deploying contracts with the account:&quot;, deployer.address);

  // Deploy Verifier contract first
  const Verifier = await ethers.getContractFactory(&quot;Verifier&quot;);
  const verifier = await Verifier.deploy();
  await verifier.deployed();
  console.log(&quot;Verifier deployed to:&quot;, verifier.address);

  // Deploy AgeVerifier as upgradeable contract
  const AgeVerifier = await ethers.getContractFactory(&quot;AgeVerifier&quot;);
  const ageVerifier = await upgrades.deployProxy(
    AgeVerifier,
    [verifier.address],
    {
      initializer: &quot;initialize&quot;,
      kind: &quot;uups&quot;,
    }
  );
  await ageVerifier.deployed();

  console.log(&quot;AgeVerifier proxy deployed to:&quot;, await ageVerifier.getAddress());
  console.log(
    &quot;Implementation address:&quot;,
    await upgrades.erc1967.getImplementationAddress(
      await ageVerifier.getAddress()
    )
  );
}

main()
  .then(() =&gt; process.exit(0))
  .catch((error) =&gt; {
    console.error(error);
    process.exit(1);
  });
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span>, <span class="hljs-title">upgrades</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"hardhat"</span>;

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  const [deployer] <span class="hljs-operator">=</span> await ethers.getSigners();
  console.log(<span class="hljs-string">"Deploying contracts with the account:"</span>, deployer.<span class="hljs-built_in">address</span>);

  <span class="hljs-comment">// Deploy Verifier contract first</span>
  const Verifier <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"Verifier"</span>);
  const verifier <span class="hljs-operator">=</span> await Verifier.deploy();
  await verifier.deployed();
  console.log(<span class="hljs-string">"Verifier deployed to:"</span>, verifier.<span class="hljs-built_in">address</span>);

  <span class="hljs-comment">// Deploy AgeVerifier as upgradeable contract</span>
  const AgeVerifier <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"AgeVerifier"</span>);
  const ageVerifier <span class="hljs-operator">=</span> await upgrades.deployProxy(
    AgeVerifier,
    [verifier.<span class="hljs-built_in">address</span>],
    {
      initializer: <span class="hljs-string">"initialize"</span>,
      kind: <span class="hljs-string">"uups"</span>,
    }
  );
  await ageVerifier.deployed();

  console.log(<span class="hljs-string">"AgeVerifier proxy deployed to:"</span>, await ageVerifier.getAddress());
  console.log(
    <span class="hljs-string">"Implementation address:"</span>,
    await upgrades.erc1967.getImplementationAddress(
      await ageVerifier.getAddress()
    )
  );
}

main()
  .then(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> process.exit(<span class="hljs-number">0</span>))
  .catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
    console.error(<span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    process.exit(<span class="hljs-number">1</span>);
  });
</code></pre><h2 id="h-step-3-zk-proof-generation-implementation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 3: ZK Proof Generation Implementation</h2><p>Create the proof generation utility file (<code>frontend/src/utils/zkProof.ts</code>):</p><pre data-type="codeBlock" text="import { groth16 } from &quot;snarkjs&quot;;
import { ethers } from &quot;ethers&quot;;

// Define the input interface for proof generation
interface ProofInput {
  birthYear: number;
  birthMonth: number;
  birthDay: number;
  currentYear: number;
  currentMonth: number;
  currentDay: number;
}

// Define the proof output interface
interface ProofOutput {
  proof: {
    pi_a: [string, string];
    pi_b: [[string, string], [string, string]];
    pi_c: [string, string];
  };
  publicSignals: string[];
}

// Cache for circuit and proving key
let circuitCache: ArrayBuffer | null = null;
let provingKeyCache: ArrayBuffer | null = null;

/**
 * Loads the circuit and proving key, with caching
 */
async function loadCircuitAndKey() {
  if (!circuitCache) {
    const circuitResponse = await fetch(&quot;/circuits/ageProof.wasm&quot;);
    circuitCache = await circuitResponse.arrayBuffer();
  }

  if (!provingKeyCache) {
    const provingKeyResponse = await fetch(&quot;/circuits/ageProof.zkey&quot;);
    provingKeyCache = await provingKeyResponse.arrayBuffer();
  }

  return {
    circuit: circuitCache,
    provingKey: provingKeyCache,
  };
}

/**
 * Validates the input data for the proof
 */
function validateInput(input: ProofInput): string | null {
  const now = new Date();
  const birthDate = new Date(
    input.birthYear,
    input.birthMonth - 1,
    input.birthDay
  );

  // Check if birth date is valid
  if (birthDate &gt; now) {
    return &quot;Birth date cannot be in the future&quot;;
  }

  // Check if birth date is reasonable (e.g., not more than 150 years ago)
  const minYear = now.getFullYear() - 150;
  if (input.birthYear &lt; minYear) {
    return &quot;Birth year seems invalid&quot;;
  }

  // Validate month and day
  if (input.birthMonth &lt; 1 || input.birthMonth &gt; 12) {
    return &quot;Invalid month&quot;;
  }
  if (input.birthDay &lt; 1 || input.birthDay &gt; 31) {
    return &quot;Invalid day&quot;;
  }

  return null;
}

/**
 * Converts the proof to the format expected by the smart contract
 */
function formatProofForContract(proof: any): {
  a: [string, string];
  b: [[string, string], [string, string]];
  c: [string, string];
} {
  return {
    a: [proof.pi_a[0], proof.pi_a[1]],
    b: [
      [proof.pi_b[0][1], proof.pi_b[0][2]],
      [proof.pi_b[1][1], proof.pi_b[1][2]],
    ],
    c: [proof.pi_c[0], proof.pi_c[1]],
  };
}

/**
 * Generates a zero-knowledge proof for age verification
 */
export async function generateProof(input: ProofInput): Promise&lt;ProofOutput&gt; {
  try {
    // Validate input
    const validationError = validateInput(input);
    if (validationError) {
      throw new Error(validationError);
    }

    // Load circuit and proving key
    const { circuit, provingKey } = await loadCircuitAndKey();

    // Prepare the input for the circuit
    const circuitInput = {
      birthYear: input.birthYear,
      birthMonth: input.birthMonth,
      birthDay: input.birthDay,
      currentYear: input.currentYear,
      currentMonth: input.currentMonth,
      currentDay: input.currentDay,
    };

    // Generate the proof
    const { proof, publicSignals } = await groth16.fullProve(
      circuitInput,
      circuit,
      provingKey
    );

    // Format the proof for the smart contract
    const formattedProof = formatProofForContract(proof);

    return {
      proof: formattedProof,
      publicSignals: publicSignals.map((signal: any) =&gt;
        ethers.BigNumber.from(signal).toString()
      ),
    };
  } catch (error: any) {
    console.error(&quot;Proof generation failed:&quot;, error);
    throw new Error(
      `Failed to generate proof: ${error.message || &quot;Unknown error&quot;}`
    );
  }
}

/**
 * Verifies a proof locally (useful for testing)
 */
export async function verifyProofLocally(
  proof: ProofOutput[&quot;proof&quot;],
  publicSignals: string[]
): Promise&lt;boolean&gt; {
  try {
    const verificationKey = await fetch(&quot;/circuits/verification_key.json&quot;);
    const vKey = await verificationKey.json();

    return await groth16.verify(vKey, publicSignals, proof);
  } catch (error) {
    console.error(&quot;Local verification failed:&quot;, error);
    return false;
  }
}

/**
 * Utility function to check if a proof is valid for the current date
 */
export function isProofValidForCurrentDate(proof: ProofOutput): boolean {
  const now = new Date();
  const proofDate = new Date(
    parseInt(proof.publicSignals[0]), // currentYear
    parseInt(proof.publicSignals[1]) - 1, // currentMonth
    parseInt(proof.publicSignals[2]) // currentDay
  );

  // Check if the proof is from today
  return (
    proofDate.getFullYear() === now.getFullYear() &amp;&amp;
    proofDate.getMonth() === now.getMonth() &amp;&amp;
    proofDate.getDate() === now.getDate()
  );
}

/**
 * Utility function to estimate gas cost for proof verification
 */
export async function estimateVerificationGas(
  contract: ethers.Contract,
  proof: ProofOutput[&quot;proof&quot;],
  publicSignals: string[]
): Promise&lt;ethers.BigNumber&gt; {
  try {
    const gasEstimate = await contract.estimateGas.verifyAge(
      proof.a,
      proof.b,
      proof.c,
      publicSignals
    );
    return gasEstimate;
  } catch (error) {
    console.error(&quot;Gas estimation failed:&quot;, error);
    throw new Error(&quot;Failed to estimate gas cost&quot;);
  }
}
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">groth16</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"snarkjs"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;

<span class="hljs-comment">// Define the input interface for proof generation</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ProofInput</span> </span>{
  birthYear: number;
  birthMonth: number;
  birthDay: number;
  currentYear: number;
  currentMonth: number;
  currentDay: number;
}

<span class="hljs-comment">// Define the proof output interface</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ProofOutput</span> </span>{
  proof: {
    pi_a: [<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>];
    pi_b: [[<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>], [<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>]];
    pi_c: [<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>];
  };
  publicSignals: <span class="hljs-keyword">string</span>[];
}

<span class="hljs-comment">// Cache for circuit and proving key</span>
let circuitCache: ArrayBuffer <span class="hljs-operator">|</span> null <span class="hljs-operator">=</span> null;
let provingKeyCache: ArrayBuffer <span class="hljs-operator">|</span> null <span class="hljs-operator">=</span> null;

<span class="hljs-comment">/**
 * Loads the circuit and proving key, with caching
 */</span>
async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadCircuitAndKey</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>circuitCache) {
    const circuitResponse <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"/circuits/ageProof.wasm"</span>);
    circuitCache <span class="hljs-operator">=</span> await circuitResponse.arrayBuffer();
  }

  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>provingKeyCache) {
    const provingKeyResponse <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"/circuits/ageProof.zkey"</span>);
    provingKeyCache <span class="hljs-operator">=</span> await provingKeyResponse.arrayBuffer();
  }

  <span class="hljs-keyword">return</span> {
    circuit: circuitCache,
    provingKey: provingKeyCache,
  };
}

<span class="hljs-comment">/**
 * Validates the input data for the proof
 */</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validateInput</span>(<span class="hljs-params">input: ProofInput</span>): <span class="hljs-title"><span class="hljs-keyword">string</span></span> | <span class="hljs-title">null</span> </span>{
  const <span class="hljs-built_in">now</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Date();
  const birthDate <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Date(
    input.birthYear,
    input.birthMonth <span class="hljs-operator">-</span> <span class="hljs-number">1</span>,
    input.birthDay
  );

  <span class="hljs-comment">// Check if birth date is valid</span>
  <span class="hljs-keyword">if</span> (birthDate <span class="hljs-operator">></span> <span class="hljs-built_in">now</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Birth date cannot be in the future"</span>;
  }

  <span class="hljs-comment">// Check if birth date is reasonable (e.g., not more than 150 years ago)</span>
  const minYear <span class="hljs-operator">=</span> <span class="hljs-built_in">now</span>.getFullYear() <span class="hljs-operator">-</span> <span class="hljs-number">150</span>;
  <span class="hljs-keyword">if</span> (input.birthYear <span class="hljs-operator">&#x3C;</span> minYear) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Birth year seems invalid"</span>;
  }

  <span class="hljs-comment">// Validate month and day</span>
  <span class="hljs-keyword">if</span> (input.birthMonth <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">1</span> <span class="hljs-operator">|</span><span class="hljs-operator">|</span> input.birthMonth <span class="hljs-operator">></span> <span class="hljs-number">12</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Invalid month"</span>;
  }
  <span class="hljs-keyword">if</span> (input.birthDay <span class="hljs-operator">&#x3C;</span> <span class="hljs-number">1</span> <span class="hljs-operator">|</span><span class="hljs-operator">|</span> input.birthDay <span class="hljs-operator">></span> <span class="hljs-number">31</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Invalid day"</span>;
  }

  <span class="hljs-keyword">return</span> null;
}

<span class="hljs-comment">/**
 * Converts the proof to the format expected by the smart contract
 */</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">formatProofForContract</span>(<span class="hljs-params">proof: any</span>): </span>{
  a: [<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>];
  b: [[<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>], [<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>]];
  c: [<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>];
} {
  <span class="hljs-keyword">return</span> {
    a: [proof.pi_a[<span class="hljs-number">0</span>], proof.pi_a[<span class="hljs-number">1</span>]],
    b: [
      [proof.pi_b[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>], proof.pi_b[<span class="hljs-number">0</span>][<span class="hljs-number">2</span>]],
      [proof.pi_b[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>], proof.pi_b[<span class="hljs-number">1</span>][<span class="hljs-number">2</span>]],
    ],
    c: [proof.pi_c[<span class="hljs-number">0</span>], proof.pi_c[<span class="hljs-number">1</span>]],
  };
}

<span class="hljs-comment">/**
 * Generates a zero-knowledge proof for age verification
 */</span>
export async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateProof</span>(<span class="hljs-params">input: ProofInput</span>): <span class="hljs-title">Promise</span>&#x3C;<span class="hljs-title">ProofOutput</span>> </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// Validate input</span>
    const validationError <span class="hljs-operator">=</span> validateInput(input);
    <span class="hljs-keyword">if</span> (validationError) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(validationError);
    }

    <span class="hljs-comment">// Load circuit and proving key</span>
    const { circuit, provingKey } <span class="hljs-operator">=</span> await loadCircuitAndKey();

    <span class="hljs-comment">// Prepare the input for the circuit</span>
    const circuitInput <span class="hljs-operator">=</span> {
      birthYear: input.birthYear,
      birthMonth: input.birthMonth,
      birthDay: input.birthDay,
      currentYear: input.currentYear,
      currentMonth: input.currentMonth,
      currentDay: input.currentDay,
    };

    <span class="hljs-comment">// Generate the proof</span>
    const { proof, publicSignals } <span class="hljs-operator">=</span> await groth16.fullProve(
      circuitInput,
      circuit,
      provingKey
    );

    <span class="hljs-comment">// Format the proof for the smart contract</span>
    const formattedProof <span class="hljs-operator">=</span> formatProofForContract(proof);

    <span class="hljs-keyword">return</span> {
      proof: formattedProof,
      publicSignals: publicSignals.map((signal: any) <span class="hljs-operator">=</span><span class="hljs-operator">></span>
        ethers.BigNumber.from(signal).toString()
      ),
    };
  } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>: <span class="hljs-title">any</span>) </span>{
    console.error(<span class="hljs-string">"Proof generation failed:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
      `Failed to generate proof: ${<span class="hljs-keyword">error</span>.message <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-string">"Unknown error"</span>}`
    );
  }
}

<span class="hljs-comment">/**
 * Verifies a proof locally (useful for testing)
 */</span>
export async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyProofLocally</span>(<span class="hljs-params">
  proof: ProofOutput[<span class="hljs-string">"proof"</span>],
  publicSignals: <span class="hljs-keyword">string</span>[]
</span>): <span class="hljs-title">Promise</span>&#x3C;<span class="hljs-title">boolean</span>> </span>{
  <span class="hljs-keyword">try</span> {
    const verificationKey <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"/circuits/verification_key.json"</span>);
    const vKey <span class="hljs-operator">=</span> await verificationKey.json();

    <span class="hljs-keyword">return</span> await groth16.verify(vKey, publicSignals, proof);
  } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    console.error(<span class="hljs-string">"Local verification failed:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }
}

<span class="hljs-comment">/**
 * Utility function to check if a proof is valid for the current date
 */</span>
export <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isProofValidForCurrentDate</span>(<span class="hljs-params">proof: ProofOutput</span>): <span class="hljs-title">boolean</span> </span>{
  const <span class="hljs-built_in">now</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Date();
  const proofDate <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Date(
    parseInt(proof.publicSignals[<span class="hljs-number">0</span>]), <span class="hljs-comment">// currentYear</span>
    parseInt(proof.publicSignals[<span class="hljs-number">1</span>]) <span class="hljs-operator">-</span> <span class="hljs-number">1</span>, <span class="hljs-comment">// currentMonth</span>
    parseInt(proof.publicSignals[<span class="hljs-number">2</span>]) <span class="hljs-comment">// currentDay</span>
  );

  <span class="hljs-comment">// Check if the proof is from today</span>
  <span class="hljs-keyword">return</span> (
    proofDate.getFullYear() <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">now</span>.getFullYear() <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
    proofDate.getMonth() <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">now</span>.getMonth() <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
    proofDate.getDate() <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">now</span>.getDate()
  );
}

<span class="hljs-comment">/**
 * Utility function to estimate gas cost for proof verification
 */</span>
export async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">estimateVerificationGas</span>(<span class="hljs-params">
  <span class="hljs-keyword">contract</span>: ethers.Contract,
  proof: ProofOutput[<span class="hljs-string">"proof"</span>],
  publicSignals: <span class="hljs-keyword">string</span>[]
</span>): <span class="hljs-title">Promise</span>&#x3C;<span class="hljs-title">ethers</span>.<span class="hljs-title">BigNumber</span>> </span>{
  <span class="hljs-keyword">try</span> {
    const gasEstimate <span class="hljs-operator">=</span> await <span class="hljs-keyword">contract</span>.estimateGas.verifyAge(
      proof.a,
      proof.b,
      proof.c,
      publicSignals
    );
    <span class="hljs-keyword">return</span> gasEstimate;
  } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
    console.error(<span class="hljs-string">"Gas estimation failed:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Failed to estimate gas cost"</span>);
  }
}
</code></pre><h2 id="h-step-4-creating-the-frontend-components" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 4: Creating the Frontend Components</h2><p>Update <code>src/utils/web3.ts</code> to work with Sei:</p><pre data-type="codeBlock" text="import { ethers } from &quot;ethers&quot;;
import detectEthereumProvider from &quot;@metamask/detect-provider&quot;;
import AgeVerifier from &quot;../artifacts/contracts/AgeVerifier.sol/AgeVerifier.json&quot;;

const SEI_CHAIN_ID = &quot;713715&quot;; // Sei testnet chain ID
const SEI_RPC_URL = &quot;https://evm-rpc-testnet.sei.io&quot;;

export async function setupWeb3() {
  const provider = await detectEthereumProvider();

  if (!provider) {
    throw new Error(&quot;Please install MetaMask!&quot;);
  }

  // Request account access
  await (window as any).ethereum.request({ method: &quot;eth_requestAccounts&quot; });

  // Check if we&apos;re on Sei network
  const chainId = await (window as any).ethereum.request({
    method: &quot;eth_chainId&quot;,
  });

  if (chainId !== SEI_CHAIN_ID) {
    try {
      await (window as any).ethereum.request({
        method: &quot;wallet_switchEthereumChain&quot;,
        params: [{ chainId: `0x${parseInt(SEI_CHAIN_ID).toString(16)}` }],
      });
    } catch (switchError: any) {
      // If Sei network is not added to MetaMask
      if (switchError.code === 4902) {
        await (window as any).ethereum.request({
          method: &quot;wallet_addEthereumChain&quot;,
          params: [
            {
              chainId: `0x${parseInt(SEI_CHAIN_ID).toString(16)}`,
              chainName: &quot;Sei Testnet&quot;,
              nativeCurrency: {
                name: &quot;SEI&quot;,
                symbol: &quot;SEI&quot;,
                decimals: 18,
              },
              rpcUrls: [SEI_RPC_URL],
              blockExplorerUrls: [&quot;https://testnet.sei.io&quot;],
            },
          ],
        });
      }
    }
  }

  const ethersProvider = new ethers.providers.Web3Provider(provider);
  const signer = ethersProvider.getSigner();

  const contractAddress = process.env.REACT_APP_CONTRACT_ADDRESS;
  const contract = new ethers.Contract(
    contractAddress!,
    AgeVerifier.abi,
    signer
  );

  return {
    provider: ethersProvider,
    signer,
    contract,
    account: await signer.getAddress(),
  };
}
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">detectEthereumProvider</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@metamask/detect-provider"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">AgeVerifier</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../artifacts/contracts/AgeVerifier.sol/AgeVerifier.json"</span>;

const SEI_CHAIN_ID <span class="hljs-operator">=</span> <span class="hljs-string">"713715"</span>; <span class="hljs-comment">// Sei testnet chain ID</span>
const SEI_RPC_URL <span class="hljs-operator">=</span> <span class="hljs-string">"https://evm-rpc-testnet.sei.io"</span>;

export async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setupWeb3</span>(<span class="hljs-params"></span>) </span>{
  const provider <span class="hljs-operator">=</span> await detectEthereumProvider();

  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>provider) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Please install MetaMask!"</span>);
  }

  <span class="hljs-comment">// Request account access</span>
  await (window <span class="hljs-keyword">as</span> any).ethereum.request({ method: <span class="hljs-string">"eth_requestAccounts"</span> });

  <span class="hljs-comment">// Check if we're on Sei network</span>
  const chainId <span class="hljs-operator">=</span> await (window <span class="hljs-keyword">as</span> any).ethereum.request({
    method: <span class="hljs-string">"eth_chainId"</span>,
  });

  <span class="hljs-keyword">if</span> (chainId <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> SEI_CHAIN_ID) {
    <span class="hljs-keyword">try</span> {
      await (window <span class="hljs-keyword">as</span> any).ethereum.request({
        method: <span class="hljs-string">"wallet_switchEthereumChain"</span>,
        params: [{ chainId: `0x${parseInt(SEI_CHAIN_ID).toString(<span class="hljs-number">16</span>)}` }],
      });
    } <span class="hljs-keyword">catch</span> (switchError: any) {
      <span class="hljs-comment">// If Sei network is not added to MetaMask</span>
      <span class="hljs-keyword">if</span> (switchError.<span class="hljs-built_in">code</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">4902</span>) {
        await (window <span class="hljs-keyword">as</span> any).ethereum.request({
          method: <span class="hljs-string">"wallet_addEthereumChain"</span>,
          params: [
            {
              chainId: `0x${parseInt(SEI_CHAIN_ID).toString(<span class="hljs-number">16</span>)}`,
              chainName: <span class="hljs-string">"Sei Testnet"</span>,
              nativeCurrency: {
                name: <span class="hljs-string">"SEI"</span>,
                symbol: <span class="hljs-string">"SEI"</span>,
                decimals: <span class="hljs-number">18</span>,
              },
              rpcUrls: [SEI_RPC_URL],
              blockExplorerUrls: [<span class="hljs-string">"https://testnet.sei.io"</span>],
            },
          ],
        });
      }
    }
  }

  const ethersProvider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(provider);
  const signer <span class="hljs-operator">=</span> ethersProvider.getSigner();

  const contractAddress <span class="hljs-operator">=</span> process.env.REACT_APP_CONTRACT_ADDRESS;
  const <span class="hljs-class"><span class="hljs-keyword">contract</span> = <span class="hljs-title"><span class="hljs-keyword">new</span></span> <span class="hljs-title">ethers</span>.<span class="hljs-title">Contract</span>(<span class="hljs-params">
    contractAddress!,
    AgeVerifier.<span class="hljs-built_in">abi</span>,
    signer
  </span>);

  <span class="hljs-title"><span class="hljs-keyword">return</span></span> </span>{
    provider: ethersProvider,
    signer,
    <span class="hljs-class"><span class="hljs-keyword">contract</span>,
    <span class="hljs-title">account</span>: <span class="hljs-title">await</span> <span class="hljs-title">signer</span>.<span class="hljs-title">getAddress</span>(<span class="hljs-params"></span>),
  };
}
</span></code></pre><h2 id="h-step-5-deploying-to-sei-testnet" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 5: Deploying to Sei Testnet</h2><ol><li><p>Make sure you have testnet SEI tokens in your wallet</p></li><li><p>Deploy the contracts:</p></li></ol><pre data-type="codeBlock" text="npx hardhat run scripts/deploy.ts --network seitestnet
"><code>npx hardhat run scripts<span class="hljs-operator">/</span>deploy.ts <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network seitestnet
</code></pre><ol><li><p>After deployment, update your <code>.env</code> file with the deployed contract addresses:</p></li></ol><pre data-type="codeBlock" text="REACT_APP_CONTRACT_ADDRESS=your_deployed_contract_address
"><code><span class="hljs-attr">REACT_APP_CONTRACT_ADDRESS</span>=your_deployed_contract_address
</code></pre><h2 id="h-step-6-testing" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 6: Testing</h2><p>Create <code>test/AgeVerifier.test.ts</code>:</p><pre data-type="codeBlock" text="import { expect } from &quot;chai&quot;;
import { ethers, upgrades } from &quot;hardhat&quot;;
import { SignerWithAddress } from &quot;@nomiclabs/hardhat-ethers/signers&quot;;

describe(&quot;AgeVerifier&quot;, function () {
  let ageVerifier: any;
  let verifier: any;
  let owner: SignerWithAddress;
  let user: SignerWithAddress;

  beforeEach(async function () {
    [owner, user] = await ethers.getSigners();

    // Deploy Verifier
    const Verifier = await ethers.getContractFactory(&quot;Verifier&quot;);
    verifier = await Verifier.deploy();
    await verifier.deployed();

    // Deploy AgeVerifier
    const AgeVerifier = await ethers.getContractFactory(&quot;AgeVerifier&quot;);
    ageVerifier = await upgrades.deployProxy(AgeVerifier, [verifier.address], {
      initializer: &quot;initialize&quot;,
    });
    await ageVerifier.deployed();
  });

  describe(&quot;Verification&quot;, function () {
    it(&quot;Should verify age proof correctly&quot;, async function () {
      // Mock proof data (in real scenario, this would come from the circuit)
      const a = [ethers.BigNumber.from(&quot;1&quot;), ethers.BigNumber.from(&quot;2&quot;)];
      const b = [
        [ethers.BigNumber.from(&quot;3&quot;), ethers.BigNumber.from(&quot;4&quot;)],
        [ethers.BigNumber.from(&quot;5&quot;), ethers.BigNumber.from(&quot;6&quot;)],
      ];
      const c = [ethers.BigNumber.from(&quot;7&quot;), ethers.BigNumber.from(&quot;8&quot;)];
      const input = [
        ethers.BigNumber.from(&quot;9&quot;),
        ethers.BigNumber.from(&quot;10&quot;),
        ethers.BigNumber.from(&quot;11&quot;),
      ];

      // Mock verifier to return true
      await verifier.setMockVerifyResult(true);

      await expect(ageVerifier.connect(user).verifyAge(a, b, c, input))
        .to.emit(ageVerifier, &quot;AgeVerified&quot;)
        .withArgs(user.address);

      expect(await ageVerifier.isVerified(user.address)).to.be.true;
    });
  });
});
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">expect</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"chai"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span>, <span class="hljs-title">upgrades</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"hardhat"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">SignerWithAddress</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@nomiclabs/hardhat-ethers/signers"</span>;

describe(<span class="hljs-string">"AgeVerifier"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  let ageVerifier: any;
  let verifier: any;
  let owner: SignerWithAddress;
  let user: SignerWithAddress;

  beforeEach(async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    [owner, user] <span class="hljs-operator">=</span> await ethers.getSigners();

    <span class="hljs-comment">// Deploy Verifier</span>
    const Verifier <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"Verifier"</span>);
    verifier <span class="hljs-operator">=</span> await Verifier.deploy();
    await verifier.deployed();

    <span class="hljs-comment">// Deploy AgeVerifier</span>
    const AgeVerifier <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"AgeVerifier"</span>);
    ageVerifier <span class="hljs-operator">=</span> await upgrades.deployProxy(AgeVerifier, [verifier.<span class="hljs-built_in">address</span>], {
      initializer: <span class="hljs-string">"initialize"</span>,
    });
    await ageVerifier.deployed();
  });

  describe(<span class="hljs-string">"Verification"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    it(<span class="hljs-string">"Should verify age proof correctly"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-comment">// Mock proof data (in real scenario, this would come from the circuit)</span>
      const a <span class="hljs-operator">=</span> [ethers.BigNumber.from(<span class="hljs-string">"1"</span>), ethers.BigNumber.from(<span class="hljs-string">"2"</span>)];
      const b <span class="hljs-operator">=</span> [
        [ethers.BigNumber.from(<span class="hljs-string">"3"</span>), ethers.BigNumber.from(<span class="hljs-string">"4"</span>)],
        [ethers.BigNumber.from(<span class="hljs-string">"5"</span>), ethers.BigNumber.from(<span class="hljs-string">"6"</span>)],
      ];
      const c <span class="hljs-operator">=</span> [ethers.BigNumber.from(<span class="hljs-string">"7"</span>), ethers.BigNumber.from(<span class="hljs-string">"8"</span>)];
      const input <span class="hljs-operator">=</span> [
        ethers.BigNumber.from(<span class="hljs-string">"9"</span>),
        ethers.BigNumber.from(<span class="hljs-string">"10"</span>),
        ethers.BigNumber.from(<span class="hljs-string">"11"</span>),
      ];

      <span class="hljs-comment">// Mock verifier to return true</span>
      await verifier.setMockVerifyResult(<span class="hljs-literal">true</span>);

      await expect(ageVerifier.connect(user).verifyAge(a, b, c, input))
        .to.emit(ageVerifier, <span class="hljs-string">"AgeVerified"</span>)
        .withArgs(user.<span class="hljs-built_in">address</span>);

      expect(await ageVerifier.isVerified(user.<span class="hljs-built_in">address</span>)).to.be.true;
    });
  });
});
</code></pre><p>Run the tests:</p><pre data-type="codeBlock" text="npx hardhat test
"><code>npx hardhat <span class="hljs-built_in">test</span>
</code></pre><h2 id="h-frontend-implementation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Frontend Implementation</h2><p>Let&apos;s create a complete React frontend for our ZK proof dApp. We&apos;ll use TypeScript and modern React practices.</p><ol><li><p>First, create a new React application in the project:</p></li></ol><pre data-type="codeBlock" text="npx create-react-app frontend --template typescript
cd frontend
npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion
npm install ethers@5.7.2 @metamask/detect-provider
"><code>npx create<span class="hljs-operator">-</span>react<span class="hljs-operator">-</span>app frontend <span class="hljs-operator">-</span><span class="hljs-operator">-</span>template typescript
cd frontend
npm install @chakra<span class="hljs-operator">-</span>ui<span class="hljs-operator">/</span>react @emotion<span class="hljs-operator">/</span>react @emotion<span class="hljs-operator">/</span>styled framer<span class="hljs-operator">-</span>motion
npm install ethers@<span class="hljs-number">5.7</span><span class="hljs-number">.2</span> @metamask<span class="hljs-operator">/</span>detect<span class="hljs-operator">-</span>provider
</code></pre><ol><li><p>Create the main App component (<code>frontend/src/App.tsx</code>):</p></li></ol><pre data-type="codeBlock" text="import React, { useState, useEffect } from &quot;react&quot;;
import {
  ChakraProvider,
  Box,
  Container,
  VStack,
  Heading,
} from &quot;@chakra-ui/react&quot;;
import { ethers } from &quot;ethers&quot;;
import WalletConnect from &quot;./components/WalletConnect&quot;;
import AgeVerification from &quot;./components/AgeVerification&quot;;
import { setupWeb3 } from &quot;./utils/web3&quot;;

function App() {
  const [account, setAccount] = useState&lt;string&gt;(&quot;&quot;);
  const [contract, setContract] = useState&lt;ethers.Contract | null&gt;(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() =&gt; {
    const init = async () =&gt; {
      try {
        const { contract, account } = await setupWeb3();
        setContract(contract);
        setAccount(account);
      } catch (error) {
        console.error(&quot;Failed to initialize web3:&quot;, error);
      } finally {
        setIsLoading(false);
      }
    };

    init();
  }, []);

  return (
    &lt;ChakraProvider&gt;
      &lt;Box minH=&quot;100vh&quot; bg=&quot;gray.50&quot; py={10}&gt;
        &lt;Container maxW=&quot;container.md&quot;&gt;
          &lt;VStack spacing={8} align=&quot;stretch&quot;&gt;
            &lt;Heading textAlign=&quot;center&quot; mb={8}&gt;
              Sei ZK Age Verification
            &lt;/Heading&gt;

            &lt;WalletConnect
              account={account}
              onConnect={async () =&gt; {
                const { contract, account } = await setupWeb3();
                setContract(contract);
                setAccount(account);
              }}
            /&gt;

            {contract &amp;&amp; account &amp;&amp; (
              &lt;AgeVerification contract={contract} account={account} /&gt;
            )}
          &lt;/VStack&gt;
        &lt;/Container&gt;
      &lt;/Box&gt;
    &lt;/ChakraProvider&gt;
  );
}

export default App;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span>, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {
  <span class="hljs-title class_">ChakraProvider</span>,
  <span class="hljs-title class_">Box</span>,
  <span class="hljs-title class_">Container</span>,
  <span class="hljs-title class_">VStack</span>,
  <span class="hljs-title class_">Heading</span>,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@chakra-ui/react"</span>;
<span class="hljs-keyword">import</span> { ethers } <span class="hljs-keyword">from</span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">WalletConnect</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/WalletConnect"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title class_">AgeVerification</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/AgeVerification"</span>;
<span class="hljs-keyword">import</span> { setupWeb3 } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/web3"</span>;

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
  <span class="hljs-keyword">const</span> [account, setAccount] = useState&#x3C;string>(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [contract, setContract] = useState&#x3C;ethers.<span class="hljs-property">Contract</span> | <span class="hljs-literal">null</span>>(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">true</span>);

  <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {
    <span class="hljs-keyword">const</span> <span class="hljs-title function_">init</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) => {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> { contract, account } = <span class="hljs-keyword">await</span> <span class="hljs-title function_">setupWeb3</span>();
        <span class="hljs-title function_">setContract</span>(contract);
        <span class="hljs-title function_">setAccount</span>(account);
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"Failed to initialize web3:"</span>, error);
      } <span class="hljs-keyword">finally</span> {
        <span class="hljs-title function_">setIsLoading</span>(<span class="hljs-literal">false</span>);
      }
    };

    <span class="hljs-title function_">init</span>();
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&#x3C;<span class="hljs-name">ChakraProvider</span>></span>
      <span class="hljs-tag">&#x3C;<span class="hljs-name">Box</span> <span class="hljs-attr">minH</span>=<span class="hljs-string">"100vh"</span> <span class="hljs-attr">bg</span>=<span class="hljs-string">"gray.50"</span> <span class="hljs-attr">py</span>=<span class="hljs-string">{10}</span>></span>
        <span class="hljs-tag">&#x3C;<span class="hljs-name">Container</span> <span class="hljs-attr">maxW</span>=<span class="hljs-string">"container.md"</span>></span>
          <span class="hljs-tag">&#x3C;<span class="hljs-name">VStack</span> <span class="hljs-attr">spacing</span>=<span class="hljs-string">{8}</span> <span class="hljs-attr">align</span>=<span class="hljs-string">"stretch"</span>></span>
            <span class="hljs-tag">&#x3C;<span class="hljs-name">Heading</span> <span class="hljs-attr">textAlign</span>=<span class="hljs-string">"center"</span> <span class="hljs-attr">mb</span>=<span class="hljs-string">{8}</span>></span>
              Sei ZK Age Verification
            <span class="hljs-tag">&#x3C;/<span class="hljs-name">Heading</span>></span>

            <span class="hljs-tag">&#x3C;<span class="hljs-name">WalletConnect</span>
              <span class="hljs-attr">account</span>=<span class="hljs-string">{account}</span>
              <span class="hljs-attr">onConnect</span>=<span class="hljs-string">{async</span> () =></span> {
                const { contract, account } = await setupWeb3();
                setContract(contract);
                setAccount(account);
              }}
            />

            {contract &#x26;&#x26; account &#x26;&#x26; (
              <span class="hljs-tag">&#x3C;<span class="hljs-name">AgeVerification</span> <span class="hljs-attr">contract</span>=<span class="hljs-string">{contract}</span> <span class="hljs-attr">account</span>=<span class="hljs-string">{account}</span> /></span>
            )}
          <span class="hljs-tag">&#x3C;/<span class="hljs-name">VStack</span>></span>
        <span class="hljs-tag">&#x3C;/<span class="hljs-name">Container</span>></span>
      <span class="hljs-tag">&#x3C;/<span class="hljs-name">Box</span>></span>
    <span class="hljs-tag">&#x3C;/<span class="hljs-name">ChakraProvider</span>></span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">App</span>;
</code></pre><ol><li><p>Create the WalletConnect component (<code>frontend/src/components/WalletConnect.tsx</code>):</p></li></ol><pre data-type="codeBlock" text="import React from &quot;react&quot;;
import { Box, Button, Text, useToast } from &quot;@chakra-ui/react&quot;;

interface WalletConnectProps {
  account: string;
  onConnect: () =&gt; Promise&lt;void&gt;;
}

const WalletConnect: React.FC&lt;WalletConnectProps&gt; = ({
  account,
  onConnect,
}) =&gt; {
  const toast = useToast();

  const handleConnect = async () =&gt; {
    try {
      await onConnect();
      toast({
        title: &quot;Wallet Connected&quot;,
        status: &quot;success&quot;,
        duration: 3000,
      });
    } catch (error) {
      toast({
        title: &quot;Connection Failed&quot;,
        description:
          &quot;Please make sure MetaMask is installed and you are on the Sei network&quot;,
        status: &quot;error&quot;,
        duration: 5000,
      });
    }
  };

  return (
    &lt;Box p={5} shadow=&quot;md&quot; borderWidth=&quot;1px&quot; borderRadius=&quot;lg&quot; bg=&quot;white&quot;&gt;
      {account ? (
        &lt;Text&gt;
          Connected: {account.slice(0, 6)}...{account.slice(-4)}
        &lt;/Text&gt;
      ) : (
        &lt;Button colorScheme=&quot;blue&quot; onClick={handleConnect} isLoading={false}&gt;
          Connect Wallet
        &lt;/Button&gt;
      )}
    &lt;/Box&gt;
  );
};

export default WalletConnect;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Box</span>, <span class="hljs-title">Button</span>, <span class="hljs-title">Text</span>, <span class="hljs-title">useToast</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@chakra-ui/react"</span>;

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">WalletConnectProps</span> </span>{
  account: <span class="hljs-keyword">string</span>;
  onConnect: () <span class="hljs-operator">=</span><span class="hljs-operator">></span> Promise<span class="hljs-operator">&#x3C;</span>void<span class="hljs-operator">></span>;
}

const WalletConnect: React.FC&#x3C;WalletConnectProps<span class="hljs-operator">></span> <span class="hljs-operator">=</span> ({
  account,
  onConnect,
}) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  const toast <span class="hljs-operator">=</span> useToast();

  const handleConnect <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">try</span> {
      await onConnect();
      toast({
        title: <span class="hljs-string">"Wallet Connected"</span>,
        status: <span class="hljs-string">"success"</span>,
        duration: <span class="hljs-number">3000</span>,
      });
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
      toast({
        title: <span class="hljs-string">"Connection Failed"</span>,
        description:
          <span class="hljs-string">"Please make sure MetaMask is installed and you are on the Sei network"</span>,
        status: <span class="hljs-string">"error"</span>,
        duration: <span class="hljs-number">5000</span>,
      });
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>Box p<span class="hljs-operator">=</span>{<span class="hljs-number">5</span>} shadow<span class="hljs-operator">=</span><span class="hljs-string">"md"</span> borderWidth<span class="hljs-operator">=</span><span class="hljs-string">"1px"</span> borderRadius<span class="hljs-operator">=</span><span class="hljs-string">"lg"</span> bg<span class="hljs-operator">=</span><span class="hljs-string">"white"</span><span class="hljs-operator">></span>
      {account ? (
        <span class="hljs-operator">&#x3C;</span>Text<span class="hljs-operator">></span>
          Connected: {account.slice(<span class="hljs-number">0</span>, <span class="hljs-number">6</span>)}...{account.slice(<span class="hljs-number">-4</span>)}
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Text<span class="hljs-operator">></span>
      ) : (
        <span class="hljs-operator">&#x3C;</span>Button colorScheme<span class="hljs-operator">=</span><span class="hljs-string">"blue"</span> onClick<span class="hljs-operator">=</span>{handleConnect} isLoading<span class="hljs-operator">=</span>{<span class="hljs-literal">false</span>}<span class="hljs-operator">></span>
          Connect Wallet
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>
      )}
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Box<span class="hljs-operator">></span>
  );
};

export default WalletConnect;
</code></pre><ol><li><p>Create the AgeVerification component (<code>frontend/src/components/AgeVerification.tsx</code>):</p></li></ol><pre data-type="codeBlock" text="import React, { useState, useEffect } from &quot;react&quot;;
import {
  Box,
  Button,
  FormControl,
  FormLabel,
  Input,
  VStack,
  Text,
  useToast,
  Progress,
  Alert,
  AlertIcon,
} from &quot;@chakra-ui/react&quot;;
import { ethers } from &quot;ethers&quot;;
import { generateProof } from &quot;../utils/zkProof&quot;;

interface AgeVerificationProps {
  contract: ethers.Contract;
  account: string;
}

const AgeVerification: React.FC&lt;AgeVerificationProps&gt; = ({
  contract,
  account,
}) =&gt; {
  const [birthDate, setBirthDate] = useState(&quot;&quot;);
  const [isVerifying, setIsVerifying] = useState(false);
  const [isVerified, setIsVerified] = useState(false);
  const [verificationStatus, setVerificationStatus] = useState&lt;
    &quot;idle&quot; | &quot;verifying&quot; | &quot;success&quot; | &quot;error&quot;
  &gt;(&quot;idle&quot;);
  const toast = useToast();

  useEffect(() =&gt; {
    checkVerificationStatus();
  }, [contract, account]);

  const checkVerificationStatus = async () =&gt; {
    try {
      const verified = await contract.isVerified(account);
      setIsVerified(verified);
    } catch (error) {
      console.error(&quot;Failed to check verification status:&quot;, error);
    }
  };

  const handleVerification = async () =&gt; {
    if (!birthDate) {
      toast({
        title: &quot;Error&quot;,
        description: &quot;Please enter your birth date&quot;,
        status: &quot;error&quot;,
        duration: 3000,
      });
      return;
    }

    try {
      setIsVerifying(true);
      setVerificationStatus(&quot;verifying&quot;);

      // Get current date
      const now = new Date();
      const currentYear = now.getFullYear();
      const currentMonth = now.getMonth() + 1;
      const currentDay = now.getDate();

      // Parse birth date
      const birth = new Date(birthDate);
      const birthYear = birth.getFullYear();
      const birthMonth = birth.getMonth() + 1;
      const birthDay = birth.getDate();

      // Generate proof
      const { proof, publicSignals } = await generateProof({
        birthYear,
        birthMonth,
        birthDay,
        currentYear,
        currentMonth,
        currentDay,
      });

      // Verify on-chain
      const tx = await contract.verifyAge(
        [proof.pi_a[0], proof.pi_a[1]],
        [
          [proof.pi_b[0][0], proof.pi_b[0][1]],
          [proof.pi_b[1][0], proof.pi_b[1][1]],
        ],
        [proof.pi_c[0], proof.pi_c[1]],
        publicSignals
      );

      // Wait for transaction confirmation
      await tx.wait();

      // Update verification status
      await checkVerificationStatus();
      setVerificationStatus(&quot;success&quot;);

      toast({
        title: &quot;Verification Successful&quot;,
        description: &quot;Your age has been verified on the blockchain&quot;,
        status: &quot;success&quot;,
        duration: 5000,
      });
    } catch (error: any) {
      console.error(&quot;Verification failed:&quot;, error);
      setVerificationStatus(&quot;error&quot;);

      toast({
        title: &quot;Verification Failed&quot;,
        description: error.message || &quot;Please try again&quot;,
        status: &quot;error&quot;,
        duration: 5000,
      });
    } finally {
      setIsVerifying(false);
    }
  };

  return (
    &lt;Box p={5} shadow=&quot;md&quot; borderWidth=&quot;1px&quot; borderRadius=&quot;lg&quot; bg=&quot;white&quot;&gt;
      &lt;VStack spacing={4} align=&quot;stretch&quot;&gt;
        &lt;Text fontSize=&quot;xl&quot; fontWeight=&quot;bold&quot;&gt;
          Age Verification
        &lt;/Text&gt;

        {isVerified ? (
          &lt;Alert status=&quot;success&quot;&gt;
            &lt;AlertIcon /&gt;
            Your age has been verified on the Sei blockchain
          &lt;/Alert&gt;
        ) : (
          &lt;&gt;
            &lt;FormControl isRequired&gt;
              &lt;FormLabel&gt;Birth Date&lt;/FormLabel&gt;
              &lt;Input
                type=&quot;date&quot;
                value={birthDate}
                onChange={(e) =&gt; setBirthDate(e.target.value)}
                disabled={isVerifying}
              /&gt;
            &lt;/FormControl&gt;

            {verificationStatus === &quot;verifying&quot; &amp;&amp; (
              &lt;Progress size=&quot;xs&quot; isIndeterminate /&gt;
            )}

            &lt;Button
              colorScheme=&quot;blue&quot;
              onClick={handleVerification}
              isLoading={isVerifying}
              loadingText=&quot;Verifying...&quot;
              disabled={!birthDate || isVerifying}
            &gt;
              Verify Age
            &lt;/Button&gt;

            {verificationStatus === &quot;error&quot; &amp;&amp; (
              &lt;Alert status=&quot;error&quot;&gt;
                &lt;AlertIcon /&gt;
                Verification failed. Please try again.
              &lt;/Alert&gt;
            )}
          &lt;/&gt;
        )}
      &lt;/VStack&gt;
    &lt;/Box&gt;
  );
};

export default AgeVerification;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span>, { <span class="hljs-title">useState</span>, <span class="hljs-title">useEffect</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> {
  <span class="hljs-title">Box</span>,
  <span class="hljs-title">Button</span>,
  <span class="hljs-title">FormControl</span>,
  <span class="hljs-title">FormLabel</span>,
  <span class="hljs-title">Input</span>,
  <span class="hljs-title">VStack</span>,
  <span class="hljs-title">Text</span>,
  <span class="hljs-title">useToast</span>,
  <span class="hljs-title">Progress</span>,
  <span class="hljs-title">Alert</span>,
  <span class="hljs-title">AlertIcon</span>,
} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@chakra-ui/react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ethers</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">generateProof</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../utils/zkProof"</span>;

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">AgeVerificationProps</span> </span>{
  <span class="hljs-class"><span class="hljs-keyword">contract</span>: <span class="hljs-title">ethers</span>.<span class="hljs-title">Contract</span>;
  <span class="hljs-title">account</span>: <span class="hljs-title"><span class="hljs-keyword">string</span></span>;
}

<span class="hljs-title">const</span> <span class="hljs-title">AgeVerification</span>: <span class="hljs-title">React</span>.<span class="hljs-title">FC</span>&#x3C;<span class="hljs-title">AgeVerificationProps</span>> = (<span class="hljs-params">{
  <span class="hljs-keyword">contract</span>,
  account,
}</span>) => </span>{
  const [birthDate, setBirthDate] <span class="hljs-operator">=</span> useState(<span class="hljs-string">""</span>);
  const [isVerifying, setIsVerifying] <span class="hljs-operator">=</span> useState(<span class="hljs-literal">false</span>);
  const [isVerified, setIsVerified] <span class="hljs-operator">=</span> useState(<span class="hljs-literal">false</span>);
  const [verificationStatus, setVerificationStatus] <span class="hljs-operator">=</span> useState<span class="hljs-operator">&#x3C;</span>
    <span class="hljs-string">"idle"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"verifying"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"success"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"error"</span>
  <span class="hljs-operator">></span>(<span class="hljs-string">"idle"</span>);
  const toast <span class="hljs-operator">=</span> useToast();

  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    checkVerificationStatus();
  }, [<span class="hljs-class"><span class="hljs-keyword">contract</span>, <span class="hljs-title">account</span>]);

  <span class="hljs-title">const</span> <span class="hljs-title">checkVerificationStatus</span> = <span class="hljs-title">async</span> (<span class="hljs-params"></span>) => </span>{
    <span class="hljs-keyword">try</span> {
      const verified <span class="hljs-operator">=</span> await <span class="hljs-keyword">contract</span>.isVerified(account);
      setIsVerified(verified);
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
      console.error(<span class="hljs-string">"Failed to check verification status:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
    }
  };

  const handleVerification <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>birthDate) {
      toast({
        title: <span class="hljs-string">"Error"</span>,
        description: <span class="hljs-string">"Please enter your birth date"</span>,
        status: <span class="hljs-string">"error"</span>,
        duration: <span class="hljs-number">3000</span>,
      });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
      setIsVerifying(<span class="hljs-literal">true</span>);
      setVerificationStatus(<span class="hljs-string">"verifying"</span>);

      <span class="hljs-comment">// Get current date</span>
      const <span class="hljs-built_in">now</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Date();
      const currentYear <span class="hljs-operator">=</span> <span class="hljs-built_in">now</span>.getFullYear();
      const currentMonth <span class="hljs-operator">=</span> <span class="hljs-built_in">now</span>.getMonth() <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
      const currentDay <span class="hljs-operator">=</span> <span class="hljs-built_in">now</span>.getDate();

      <span class="hljs-comment">// Parse birth date</span>
      const birth <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Date(birthDate);
      const birthYear <span class="hljs-operator">=</span> birth.getFullYear();
      const birthMonth <span class="hljs-operator">=</span> birth.getMonth() <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
      const birthDay <span class="hljs-operator">=</span> birth.getDate();

      <span class="hljs-comment">// Generate proof</span>
      const { proof, publicSignals } <span class="hljs-operator">=</span> await generateProof({
        birthYear,
        birthMonth,
        birthDay,
        currentYear,
        currentMonth,
        currentDay,
      });

      <span class="hljs-comment">// Verify on-chain</span>
      const <span class="hljs-built_in">tx</span> <span class="hljs-operator">=</span> await <span class="hljs-keyword">contract</span>.verifyAge(
        [proof.pi_a[<span class="hljs-number">0</span>], proof.pi_a[<span class="hljs-number">1</span>]],
        [
          [proof.pi_b[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>], proof.pi_b[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]],
          [proof.pi_b[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>], proof.pi_b[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>]],
        ],
        [proof.pi_c[<span class="hljs-number">0</span>], proof.pi_c[<span class="hljs-number">1</span>]],
        publicSignals
      );

      <span class="hljs-comment">// Wait for transaction confirmation</span>
      await <span class="hljs-built_in">tx</span>.wait();

      <span class="hljs-comment">// Update verification status</span>
      await checkVerificationStatus();
      setVerificationStatus(<span class="hljs-string">"success"</span>);

      toast({
        title: <span class="hljs-string">"Verification Successful"</span>,
        description: <span class="hljs-string">"Your age has been verified on the blockchain"</span>,
        status: <span class="hljs-string">"success"</span>,
        duration: <span class="hljs-number">5000</span>,
      });
    } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>: <span class="hljs-title">any</span>) </span>{
      console.error(<span class="hljs-string">"Verification failed:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
      setVerificationStatus(<span class="hljs-string">"error"</span>);

      toast({
        title: <span class="hljs-string">"Verification Failed"</span>,
        description: <span class="hljs-keyword">error</span>.message <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-string">"Please try again"</span>,
        status: <span class="hljs-string">"error"</span>,
        duration: <span class="hljs-number">5000</span>,
      });
    } finally {
      setIsVerifying(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>Box p<span class="hljs-operator">=</span>{<span class="hljs-number">5</span>} shadow<span class="hljs-operator">=</span><span class="hljs-string">"md"</span> borderWidth<span class="hljs-operator">=</span><span class="hljs-string">"1px"</span> borderRadius<span class="hljs-operator">=</span><span class="hljs-string">"lg"</span> bg<span class="hljs-operator">=</span><span class="hljs-string">"white"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>VStack spacing<span class="hljs-operator">=</span>{<span class="hljs-number">4</span>} align<span class="hljs-operator">=</span><span class="hljs-string">"stretch"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Text fontSize<span class="hljs-operator">=</span><span class="hljs-string">"xl"</span> fontWeight<span class="hljs-operator">=</span><span class="hljs-string">"bold"</span><span class="hljs-operator">></span>
          Age Verification
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Text<span class="hljs-operator">></span>

        {isVerified ? (
          <span class="hljs-operator">&#x3C;</span>Alert status<span class="hljs-operator">=</span><span class="hljs-string">"success"</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>AlertIcon <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            Your age has been verified on the Sei blockchain
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Alert<span class="hljs-operator">></span>
        ) : (
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span>FormControl isRequired<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>FormLabel<span class="hljs-operator">></span>Birth Date<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>FormLabel<span class="hljs-operator">></span>
              <span class="hljs-operator">&#x3C;</span>Input
                <span class="hljs-keyword">type</span><span class="hljs-operator">=</span><span class="hljs-string">"date"</span>
                value<span class="hljs-operator">=</span>{birthDate}
                onChange<span class="hljs-operator">=</span>{(e) <span class="hljs-operator">=</span><span class="hljs-operator">></span> setBirthDate(e.target.<span class="hljs-built_in">value</span>)}
                disabled<span class="hljs-operator">=</span>{isVerifying}
              <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>FormControl<span class="hljs-operator">></span>

            {verificationStatus <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"verifying"</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (
              <span class="hljs-operator">&#x3C;</span>Progress size<span class="hljs-operator">=</span><span class="hljs-string">"xs"</span> isIndeterminate <span class="hljs-operator">/</span><span class="hljs-operator">></span>
            )}

            <span class="hljs-operator">&#x3C;</span>Button
              colorScheme<span class="hljs-operator">=</span><span class="hljs-string">"blue"</span>
              onClick<span class="hljs-operator">=</span>{handleVerification}
              isLoading<span class="hljs-operator">=</span>{isVerifying}
              loadingText<span class="hljs-operator">=</span><span class="hljs-string">"Verifying..."</span>
              disabled<span class="hljs-operator">=</span>{<span class="hljs-operator">!</span>birthDate <span class="hljs-operator">|</span><span class="hljs-operator">|</span> isVerifying}
            <span class="hljs-operator">></span>
              Verify Age
            <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Button<span class="hljs-operator">></span>

            {verificationStatus <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"error"</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (
              <span class="hljs-operator">&#x3C;</span>Alert status<span class="hljs-operator">=</span><span class="hljs-string">"error"</span><span class="hljs-operator">></span>
                <span class="hljs-operator">&#x3C;</span>AlertIcon <span class="hljs-operator">/</span><span class="hljs-operator">></span>
                Verification failed. Please <span class="hljs-keyword">try</span> again.
              &#x3C;<span class="hljs-operator">/</span>Alert<span class="hljs-operator">></span>
            )}
          <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
        )}
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>VStack<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>Box<span class="hljs-operator">></span>
  );
};

export default AgeVerification;
</code></pre><ol><li><p>Create a styles file (<code>frontend/src/styles/theme.ts</code>):</p></li></ol><pre data-type="codeBlock" text="import { extendTheme } from &quot;@chakra-ui/react&quot;;

const theme = extendTheme({
  styles: {
    global: {
      body: {
        bg: &quot;gray.50&quot;,
      },
    },
  },
  components: {
    Button: {
      defaultProps: {
        colorScheme: &quot;blue&quot;,
      },
    },
  },
});

export default theme;
"><code>import { extendTheme } <span class="hljs-selector-tag">from</span> "<span class="hljs-keyword">@chakra-ui</span>/react";

const theme = extendTheme({
  styles: {
    global: {
      <span class="hljs-selector-tag">body</span>: {
        bg: <span class="hljs-string">"gray.50"</span>,
      },
    },
  },
  components: {
    <span class="hljs-selector-tag">Button</span>: {
      defaultProps: {
        colorScheme: <span class="hljs-string">"blue"</span>,
      },
    },
  },
});

export default theme;
</code></pre><ol><li><p>Update the index file (<code>frontend/src/index.tsx</code>):</p></li></ol><pre data-type="codeBlock" text="import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import App from &quot;./App&quot;;
import theme from &quot;./styles/theme&quot;;
import { ChakraProvider } from &quot;@chakra-ui/react&quot;;

ReactDOM.render(
  &lt;React.StrictMode&gt;
    &lt;ChakraProvider theme={theme}&gt;
      &lt;App /&gt;
    &lt;/ChakraProvider&gt;
  &lt;/React.StrictMode&gt;,
  document.getElementById(&quot;root&quot;)
);
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">ReactDOM</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react-dom"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">App</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./App"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">theme</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./styles/theme"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">ChakraProvider</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@chakra-ui/react"</span>;

ReactDOM.render(
  <span class="hljs-operator">&#x3C;</span>React.StrictMode>
    <span class="hljs-operator">&#x3C;</span>ChakraProvider theme<span class="hljs-operator">=</span>{theme}<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>App <span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>ChakraProvider<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>React.StrictMode>,
  document.getElementById(<span class="hljs-string">"root"</span>)
);
</code></pre><ol><li><p>Add environment variables (<code>frontend/.env</code>):</p></li></ol><pre data-type="codeBlock" text="REACT_APP_CONTRACT_ADDRESS=your_deployed_contract_address
REACT_APP_SEI_RPC_URL=https://evm-rpc-testnet.sei.io
REACT_APP_SEI_CHAIN_ID=713715
"><code><span class="hljs-attr">REACT_APP_CONTRACT_ADDRESS</span>=your_deployed_contract_address
<span class="hljs-attr">REACT_APP_SEI_RPC_URL</span>=https://evm-rpc-testnet.sei.io
<span class="hljs-attr">REACT_APP_SEI_CHAIN_ID</span>=<span class="hljs-number">713715</span>
</code></pre><ol><li><p>Start the frontend development server:</p></li></ol><pre data-type="codeBlock" text="cd frontend
npm start
"><code><span class="hljs-built_in">cd</span> frontend
npm start
</code></pre><p>To use the frontend:</p><ol><li><p>Deploy the contracts to Sei testnet</p></li><li><p>Update the <code>.env</code> file with contract addresses</p></li><li><p>Start the development server</p></li><li><p>Connect your MetaMask wallet</p></li><li><p>Switch to Sei testnet</p></li><li><p>Enter your birth date</p></li><li><p>Submit for verification</p></li></ol><h2 id="h-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.sei.io">Sei Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.sei.io/evm/evm-hardhat">Sei EVM Hardhat Guide</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.circom.io/">Circom Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/iden3/snarkjs">SnarkJS Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts">OpenZeppelin Contracts</a></p></li></ul><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>This tutorial has walked you through implementing a zero-knowledge proof system on a Sei blockchain dapp. You&apos;ve learned how to:</p><ul><li><p>Set up a Hardhat project for Sei</p></li><li><p>Deploy upgradeable contracts</p></li><li><p>Implement ZK proofs</p></li><li><p>Create a frontend structure</p></li><li><p>Test and verify your implementation</p></li></ul><p>Happy building on Sei!</p>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 5 - Creating an onchain verifier on the sei blockchain]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-5-creating-an-onchain-verifier-on-the-sei-blockchain</link>
            <guid>FR1u6gXgAtMdTWJwJuhh</guid>
            <pubDate>Tue, 17 Jun 2025 12:29:24 GMT</pubDate>
            <description><![CDATA[Creating a Zero-Knowledge Onchain Verifier: A Beginner&apos;s GuideIntroductionZero-knowledge proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. When implemented on a blockchain, these verifiers enable powerful privacy-preserving applications. This tutorial will guide you through creating a simple ZK onchain verifier...]]></description>
            <content:encoded><![CDATA[<h1 id="h-creating-a-zero-knowledge-onchain-verifier-a-beginners-guide" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Creating a Zero-Knowledge Onchain Verifier: A Beginner&apos;s Guide</h1><h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>Zero-knowledge proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. When implemented on a blockchain, these verifiers enable powerful privacy-preserving applications.</p><p>This tutorial will guide you through creating a simple ZK onchain verifier using Circom and Solidity. We&apos;ll build a basic system where a user can prove they know a secret number without revealing it.</p><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before we begin, make sure you have:</p><ul><li><p>Node.js and npm installed</p></li><li><p>Basic understanding of Solidity</p></li><li><p>Familiarity with basic cryptography concepts</p></li><li><p>A code editor (VS Code recommended)</p></li></ul><h2 id="h-setting-up-the-development-environment" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Setting Up the Development Environment</h2><ol><li><p>First, let&apos;s install the necessary tools:</p></li></ol><pre data-type="codeBlock" text="npm install -g circom
npm install -g snarkjs
"><code>npm install <span class="hljs-operator">-</span>g circom
npm install <span class="hljs-operator">-</span>g snarkjs
</code></pre><ol><li><p>Create a new project directory and initialize it:</p></li></ol><pre data-type="codeBlock" text="mkdir zk-verifier-tutorial
cd zk-verifier-tutorial
npm init -y
"><code>mkdir zk<span class="hljs-operator">-</span>verifier<span class="hljs-operator">-</span>tutorial
cd zk<span class="hljs-operator">-</span>verifier<span class="hljs-operator">-</span>tutorial
npm init <span class="hljs-operator">-</span>y
</code></pre><h2 id="h-understanding-the-components" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding the Components</h2><p>Our ZK system will consist of three main parts:</p><ol><li><p>A Circom circuit (the proving system)</p></li><li><p>A Solidity verifier contract</p></li><li><p>A frontend application to interact with the system</p></li></ol><h2 id="h-step-1-creating-the-circom-circuit" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 1: Creating the Circom Circuit</h2><p>Let&apos;s create a simple circuit that proves knowledge of a secret number. Create a file named <code>circuit.circom</code>:</p><pre data-type="codeBlock" text="pragma circom 2.0.0;

template SecretNumber() {
    // Private input: the secret number
    signal private input secret;

    // Public input: the hash of the secret number
    signal public input hash;

    // Output: whether the proof is valid
    signal output out;

    // Component to compute hash
    component hash = Poseidon(1);

    // Connect the secret to the hash component
    hash.inputs[0] &lt;== secret;

    // Verify that the provided hash matches the computed hash
    hash.out === hash;

    // Set output to 1 if verification passes
    out &lt;== 1;
}

component main = SecretNumber();
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> circom 2.0.0;</span>

template SecretNumber() {
    <span class="hljs-comment">// Private input: the secret number</span>
    signal <span class="hljs-keyword">private</span> input secret;

    <span class="hljs-comment">// Public input: the hash of the secret number</span>
    signal <span class="hljs-keyword">public</span> input hash;

    <span class="hljs-comment">// Output: whether the proof is valid</span>
    signal output out;

    <span class="hljs-comment">// Component to compute hash</span>
    component hash <span class="hljs-operator">=</span> Poseidon(<span class="hljs-number">1</span>);

    <span class="hljs-comment">// Connect the secret to the hash component</span>
    hash.inputs[<span class="hljs-number">0</span>] <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> secret;

    <span class="hljs-comment">// Verify that the provided hash matches the computed hash</span>
    hash.out <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> hash;

    <span class="hljs-comment">// Set output to 1 if verification passes</span>
    out <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
}

component main <span class="hljs-operator">=</span> SecretNumber();
</code></pre><h2 id="h-step-2-compiling-the-circuit" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 2: Compiling the Circuit</h2><ol><li><p>Create a <code>build</code> directory:</p></li></ol><pre data-type="codeBlock" text="mkdir build
"><code>mkdir build
</code></pre><ol><li><p>Compile the circuit:</p></li></ol><pre data-type="codeBlock" text="circom circuit.circom --r1cs --wasm --sym -o build
"><code>circom circuit.circom <span class="hljs-operator">-</span><span class="hljs-operator">-</span>r1cs <span class="hljs-operator">-</span><span class="hljs-operator">-</span>wasm <span class="hljs-operator">-</span><span class="hljs-operator">-</span>sym <span class="hljs-operator">-</span>o build
</code></pre><ol><li><p>Generate the proving and verification keys:</p></li></ol><pre data-type="codeBlock" text="snarkjs groth16 setup build/circuit.r1cs pot12_final.ptau build/circuit_final.zkey
"><code>snarkjs groth16 setup build<span class="hljs-operator">/</span>circuit.r1cs pot12_final.ptau build<span class="hljs-operator">/</span>circuit_final.zkey
</code></pre><h2 id="h-step-3-creating-the-verifier-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 3: Creating the Verifier Contract</h2><p>Now, let&apos;s create a Solidity contract that will verify the proofs on-chain. Create a file named <code>Verifier.sol</code>:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import &quot;@openzeppelin/contracts/access/Ownable.sol&quot;;

contract SecretNumberVerifier is Ownable {
    // Event emitted when a valid proof is submitted
    event ProofVerified(address indexed prover, uint256 hash);

    // Mapping to store verified hashes
    mapping(uint256 =&gt; bool) public verifiedHashes;

    // Function to verify the proof
    function verifyProof(
        uint[2] memory a,
        uint[2][2] memory b,
        uint[2] memory c,
        uint[1] memory input
    ) public returns (bool) {
        // Verify the proof using the verifier contract
        bool isValid = verify(a, b, c, input);

        if (isValid) {
            verifiedHashes[input[0]] = true;
            emit ProofVerified(msg.sender, input[0]);
        }

        return isValid;
    }

    // Function to check if a hash has been verified
    function isHashVerified(uint256 hash) public view returns (bool) {
        return verifiedHashes[hash];
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"@openzeppelin/contracts/access/Ownable.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">SecretNumberVerifier</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Ownable</span> </span>{
    <span class="hljs-comment">// Event emitted when a valid proof is submitted</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">ProofVerified</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> prover, <span class="hljs-keyword">uint256</span> hash</span>)</span>;

    <span class="hljs-comment">// Mapping to store verified hashes</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">bool</span>) <span class="hljs-keyword">public</span> verifiedHashes;

    <span class="hljs-comment">// Function to verify the proof</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyProof</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint</span>[<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> a,
        <span class="hljs-keyword">uint</span>[<span class="hljs-number">2</span>][<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> b,
        <span class="hljs-keyword">uint</span>[<span class="hljs-number">2</span>] <span class="hljs-keyword">memory</span> c,
        <span class="hljs-keyword">uint</span>[<span class="hljs-number">1</span>] <span class="hljs-keyword">memory</span> input
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-comment">// Verify the proof using the verifier contract</span>
        <span class="hljs-keyword">bool</span> isValid <span class="hljs-operator">=</span> verify(a, b, c, input);

        <span class="hljs-keyword">if</span> (isValid) {
            verifiedHashes[input[<span class="hljs-number">0</span>]] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
            <span class="hljs-keyword">emit</span> ProofVerified(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, input[<span class="hljs-number">0</span>]);
        }

        <span class="hljs-keyword">return</span> isValid;
    }

    <span class="hljs-comment">// Function to check if a hash has been verified</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isHashVerified</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> hash</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>) </span>{
        <span class="hljs-keyword">return</span> verifiedHashes[hash];
    }
}
</code></pre><h2 id="h-step-4-testing-the-system" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 4: Testing the System</h2><p>Let&apos;s create a simple test script to verify our implementation. Create a file named <code>test.js</code>:</p><pre data-type="codeBlock" text="const { expect } = require(&quot;chai&quot;);
const { ethers } = require(&quot;hardhat&quot;);
const snarkjs = require(&quot;snarkjs&quot;);

describe(&quot;SecretNumberVerifier&quot;, function () {
  let verifier;
  let secret = 42; // Our secret number

  beforeEach(async function () {
    const Verifier = await ethers.getContractFactory(&quot;SecretNumberVerifier&quot;);
    verifier = await Verifier.deploy();
    await verifier.deployed();
  });

  it(&quot;Should verify a valid proof&quot;, async function () {
    // Generate proof
    const { proof, publicSignals } = await snarkjs.groth16.fullProve(
      { secret: secret },
      &quot;build/circuit.wasm&quot;,
      &quot;build/circuit_final.zkey&quot;
    );

    // Verify on-chain
    const result = await verifier.verifyProof(
      [proof.pi_a[0], proof.pi_a[1]],
      [
        [proof.pi_b[0][0], proof.pi_b[0][1]],
        [proof.pi_b[1][0], proof.pi_b[1][1]],
      ],
      [proof.pi_c[0], proof.pi_c[1]],
      [publicSignals[0]]
    );

    expect(result).to.be.true;
  });
});
"><code>const { expect } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"chai"</span>);
const { ethers } <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"hardhat"</span>);
const snarkjs <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"snarkjs"</span>);

describe(<span class="hljs-string">"SecretNumberVerifier"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  let verifier;
  let secret <span class="hljs-operator">=</span> <span class="hljs-number">42</span>; <span class="hljs-comment">// Our secret number</span>

  beforeEach(async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    const Verifier <span class="hljs-operator">=</span> await ethers.getContractFactory(<span class="hljs-string">"SecretNumberVerifier"</span>);
    verifier <span class="hljs-operator">=</span> await Verifier.deploy();
    await verifier.deployed();
  });

  it(<span class="hljs-string">"Should verify a valid proof"</span>, async <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// Generate proof</span>
    const { proof, publicSignals } <span class="hljs-operator">=</span> await snarkjs.groth16.fullProve(
      { secret: secret },
      <span class="hljs-string">"build/circuit.wasm"</span>,
      <span class="hljs-string">"build/circuit_final.zkey"</span>
    );

    <span class="hljs-comment">// Verify on-chain</span>
    const result <span class="hljs-operator">=</span> await verifier.verifyProof(
      [proof.pi_a[<span class="hljs-number">0</span>], proof.pi_a[<span class="hljs-number">1</span>]],
      [
        [proof.pi_b[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>], proof.pi_b[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]],
        [proof.pi_b[<span class="hljs-number">1</span>][<span class="hljs-number">0</span>], proof.pi_b[<span class="hljs-number">1</span>][<span class="hljs-number">1</span>]],
      ],
      [proof.pi_c[<span class="hljs-number">0</span>], proof.pi_c[<span class="hljs-number">1</span>]],
      [publicSignals[<span class="hljs-number">0</span>]]
    );

    expect(result).to.be.true;
  });
});
</code></pre><h2 id="h-understanding-how-it-works" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding How It Works</h2><ol><li><p><strong>The Circuit</strong>:</p><ul><li><p>Takes a private input (the secret number) that only the prover knows</p></li><li><p>Uses the Poseidon hash function, which is specifically designed for zero-knowledge proofs and is more efficient than traditional hash functions like SHA-256 in ZK contexts</p></li><li><p>Performs a cryptographic verification by comparing the computed hash of the secret number with a provided hash value</p></li><li><p>Outputs a binary signal (1) if the verification succeeds, indicating that the prover indeed knows a number that produces the claimed hash</p></li><li><p>The circuit&apos;s constraints ensure that the prover cannot cheat by providing false inputs</p></li></ul></li><li><p><strong>The Verifier Contract</strong>:</p><ul><li><p>Acts as an on-chain verifier that receives and processes the zero-knowledge proof</p></li><li><p>Takes three main components as input:</p><ul><li><p>The proof itself (containing the cryptographic evidence)</p></li><li><p>Public inputs (like the hash value that was claimed)</p></li><li><p>Verification key (pre-computed during the trusted setup)</p></li></ul></li><li><p>Implements the Groth16 verification algorithm, which is a specific type of zk-SNARK that&apos;s particularly efficient for blockchain applications</p></li><li><p>Maintains a mapping of verified hashes to prevent replay attacks and enable future reference</p></li><li><p>Emits events that can be used by frontend applications to track successful verifications</p></li><li><p>Runs entirely on-chain, ensuring the verification process is trustless and decentralized</p></li></ul></li><li><p><strong>The Proof Generation Process</strong>:</p><ul><li><p>The prover starts with their secret number and the circuit&apos;s constraints</p></li><li><p>Uses snarkjs to generate a cryptographic proof that demonstrates knowledge of the secret number</p></li><li><p>The proof generation process involves:</p><ul><li><p>Creating a witness (a set of values that satisfy the circuit&apos;s constraints)</p></li><li><p>Running the circuit with the witness to generate the proof</p></li><li><p>Formatting the proof according to the Groth16 protocol</p></li></ul></li><li><p>The generated proof is then sent to the verifier contract</p></li><li><p>The contract performs the verification without ever learning the actual secret number</p></li><li><p>This entire process ensures that the prover can demonstrate knowledge of the secret number while maintaining complete privacy</p></li></ul></li></ol><h2 id="h-security-considerations" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Security Considerations</h2><ol><li><p><strong>Circuit Security</strong>:</p><ul><li><p>Ensure your circuit is properly constrained</p></li><li><p>Use well-audited cryptographic primitives</p></li><li><p>Consider side-channel attacks</p></li></ul></li><li><p><strong>Contract Security</strong>:</p><ul><li><p>Implement proper access controls</p></li><li><p>Consider gas optimization</p></li><li><p>Add circuit-specific checks</p></li></ul></li><li><p><strong>System Security</strong>:</p><ul><li><p>Protect private keys</p></li><li><p>Implement proper frontend security</p></li><li><p>Consider replay attacks</p></li></ul></li></ol><h2 id="h-next-steps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Next Steps</h2><ol><li><p><strong>Advanced Features</strong>:</p><ul><li><p>Add support for multiple secrets</p></li><li><p>Implement more complex circuits</p></li><li><p>Add frontend integration</p></li></ul></li><li><p><strong>Optimization</strong>:</p><ul><li><p>Optimize circuit size</p></li><li><p>Reduce gas costs</p></li><li><p>Improve proof generation time</p></li></ul></li><li><p><strong>Integration</strong>:</p><ul><li><p>Connect to a frontend application</p></li><li><p>Implement a user interface</p></li><li><p>Add more complex business logic</p></li></ul></li></ol><h2 id="h-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.circom.io/">Circom Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/iden3/snarkjs">SnarkJS Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum.org/en/zero-knowledge-proofs/">Zero Knowledge Proofs: A Technical Deep-Dive</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts">OpenZeppelin Contracts</a></p></li></ul><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>This tutorial has introduced you to the basics of creating a zero-knowledge onchain verifier. You&apos;ve learned how to:</p><ul><li><p>Create a basic Circom circuit</p></li><li><p>Generate and verify proofs</p></li><li><p>Deploy a verifier contract</p></li><li><p>Test the system</p></li></ul><p>Remember that this is a simplified example. Real-world applications often require more complex circuits and additional security considerations. Always audit your code and consider professional review for production systems.</p>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 4 - Creating a zk proof]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-4-creating-a-zk-proof</link>
            <guid>G1SASlfMb0lHYRxLGCMw</guid>
            <pubDate>Tue, 17 Jun 2025 12:27:37 GMT</pubDate>
            <description><![CDATA[Creating Your First Zero-Knowledge Proof: A Beginner&apos;s GuideIntroductionZero-knowledge proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. This tutorial will guide you through the fundamental concepts and help you create your first zero-knowledge proof.PrerequisitesBefore diving in, you should have:Basic understa...]]></description>
            <content:encoded><![CDATA[<h1 id="h-creating-your-first-zero-knowledge-proof-a-beginners-guide" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Creating Your First Zero-Knowledge Proof: A Beginner&apos;s Guide</h1><h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>Zero-knowledge proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. This tutorial will guide you through the fundamental concepts and help you create your first zero-knowledge proof.</p><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before diving in, you should have:</p><ul><li><p>Basic understanding of cryptography concepts</p></li><li><p>Familiarity with programming (we&apos;ll use JavaScript/TypeScript)</p></li><li><p>Node.js installed on your system</p></li><li><p>Basic understanding of mathematical concepts</p></li></ul><h2 id="h-understanding-the-basics" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding the Basics</h2><h3 id="h-what-is-a-zero-knowledge-proof" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What is a Zero-Knowledge Proof?</h3><p>A zero-knowledge proof must satisfy three key properties:</p><ol><li><p><strong>Completeness</strong>: If the statement is true, an honest verifier will be convinced by an honest prover</p></li><li><p><strong>Soundness</strong>: If the statement is false, no cheating prover can convince an honest verifier</p></li><li><p><strong>Zero-knowledge</strong>: If the statement is true, the verifier learns nothing other than the fact that the statement is true</p></li></ol><h3 id="h-types-of-zero-knowledge-proofs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Types of Zero-Knowledge Proofs</h3><ol><li><p><strong>Interactive Zero-Knowledge Proofs (IZKPs)</strong></p><ul><li><p>Require back-and-forth communication between prover and verifier</p></li><li><p>Example: Schnorr protocol</p></li></ul></li><li><p><strong>Non-Interactive Zero-Knowledge Proofs (NIZKPs)</strong></p><ul><li><p>Single message from prover to verifier</p></li><li><p>Example: zk-SNARKs, zk-STARKs</p></li></ul></li></ol><h2 id="h-setting-up-your-development-environment" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Setting Up Your Development Environment</h2><p>Let&apos;s set up a basic project to work with zero-knowledge proofs. We&apos;ll use the <code>circom</code> library, which is a popular choice for creating zero-knowledge circuits.</p><ol><li><p>First, create a new project directory and initialize it:</p></li></ol><pre data-type="codeBlock" text="mkdir zk-proof-tutorial
cd zk-proof-tutorial
npm init -y
"><code>mkdir zk<span class="hljs-operator">-</span>proof<span class="hljs-operator">-</span>tutorial
cd zk<span class="hljs-operator">-</span>proof<span class="hljs-operator">-</span>tutorial
npm init <span class="hljs-operator">-</span>y
</code></pre><ol><li><p>Install necessary dependencies:</p></li></ol><pre data-type="codeBlock" text="npm install circomlib snarkjs
"><code></code></pre><h2 id="h-creating-your-first-zero-knowledge-proof" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Creating Your First Zero-Knowledge Proof</h2><p>Let&apos;s create a simple example: proving that you know a number that, when multiplied by itself, equals a given value, without revealing the original number.</p><h3 id="h-step-1-define-the-circuit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 1: Define the Circuit</h3><p>Create a file named <code>multiplier.circom</code>:</p><pre data-type="codeBlock" text="pragma circom 2.0.0;

template Multiplier() {
    // Private input
    signal private input a;
    // Public input
    signal input b;
    // Output
    signal output c;

    // Constraint: a * a = b
    c &lt;== a * a;
    b === c;
}

component main = Multiplier();
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> circom 2.0.0;</span>

template Multiplier() {
    <span class="hljs-comment">// Private input</span>
    signal <span class="hljs-keyword">private</span> input a;
    <span class="hljs-comment">// Public input</span>
    signal input b;
    <span class="hljs-comment">// Output</span>
    signal output c;

    <span class="hljs-comment">// Constraint: a * a = b</span>
    c <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> a <span class="hljs-operator">*</span> a;
    b <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> c;
}

component main <span class="hljs-operator">=</span> Multiplier();
</code></pre><h3 id="h-step-2-compile-the-circuit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 2: Compile the Circuit</h3><pre data-type="codeBlock" text="circom multiplier.circom --r1cs --wasm --sym
"><code>circom multiplier.circom <span class="hljs-operator">-</span><span class="hljs-operator">-</span>r1cs <span class="hljs-operator">-</span><span class="hljs-operator">-</span>wasm <span class="hljs-operator">-</span><span class="hljs-operator">-</span>sym
</code></pre><h3 id="h-step-3-generate-the-proof" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 3: Generate the Proof</h3><p>Create a file named <code>generate-proof.js</code>:</p><pre data-type="codeBlock" text="const snarkjs = require(&quot;snarkjs&quot;);
const fs = require(&quot;fs&quot;);

async function generateProof() {
  // The number we want to prove we know (private)
  const a = 5;
  // The public result (25)
  const b = 25;

  // Generate the proof
  const { proof, publicSignals } = await snarkjs.groth16.fullProve(
    { a: a },
    &quot;multiplier.wasm&quot;,
    &quot;multiplier_final.zkey&quot;
  );

  console.log(&quot;Proof:&quot;, proof);
  console.log(&quot;Public signals:&quot;, publicSignals);
}

generateProof();
"><code>const snarkjs <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"snarkjs"</span>);
const fs <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateProof</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// The number we want to prove we know (private)</span>
  const a <span class="hljs-operator">=</span> <span class="hljs-number">5</span>;
  <span class="hljs-comment">// The public result (25)</span>
  const b <span class="hljs-operator">=</span> <span class="hljs-number">25</span>;

  <span class="hljs-comment">// Generate the proof</span>
  const { proof, publicSignals } <span class="hljs-operator">=</span> await snarkjs.groth16.fullProve(
    { a: a },
    <span class="hljs-string">"multiplier.wasm"</span>,
    <span class="hljs-string">"multiplier_final.zkey"</span>
  );

  console.log(<span class="hljs-string">"Proof:"</span>, proof);
  console.log(<span class="hljs-string">"Public signals:"</span>, publicSignals);
}

generateProof();
</code></pre><h3 id="h-step-4-verify-the-proof" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 4: Verify the Proof</h3><p>Create a file named <code>verify-proof.js</code>:</p><pre data-type="codeBlock" text="const snarkjs = require(&quot;snarkjs&quot;);

async function verifyProof() {
  const verificationKey = JSON.parse(fs.readFileSync(&quot;verification_key.json&quot;));
  const proof = JSON.parse(fs.readFileSync(&quot;proof.json&quot;));
  const publicSignals = JSON.parse(fs.readFileSync(&quot;public.json&quot;));

  const res = await snarkjs.groth16.verify(
    verificationKey,
    publicSignals,
    proof
  );

  if (res === true) {
    console.log(&quot;Verification OK&quot;);
  } else {
    console.log(&quot;Invalid proof&quot;);
  }
}

verifyProof();
"><code>const snarkjs <span class="hljs-operator">=</span> <span class="hljs-built_in">require</span>(<span class="hljs-string">"snarkjs"</span>);

async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyProof</span>(<span class="hljs-params"></span>) </span>{
  const verificationKey <span class="hljs-operator">=</span> JSON.parse(fs.readFileSync(<span class="hljs-string">"verification_key.json"</span>));
  const proof <span class="hljs-operator">=</span> JSON.parse(fs.readFileSync(<span class="hljs-string">"proof.json"</span>));
  const publicSignals <span class="hljs-operator">=</span> JSON.parse(fs.readFileSync(<span class="hljs-string">"public.json"</span>));

  const res <span class="hljs-operator">=</span> await snarkjs.groth16.verify(
    verificationKey,
    publicSignals,
    proof
  );

  <span class="hljs-keyword">if</span> (res <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-literal">true</span>) {
    console.log(<span class="hljs-string">"Verification OK"</span>);
  } <span class="hljs-keyword">else</span> {
    console.log(<span class="hljs-string">"Invalid proof"</span>);
  }
}

verifyProof();
</code></pre><h2 id="h-understanding-the-components" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding the Components</h2><p>Let&apos;s break down what we just created:</p><ol><li><p><strong>Circuit Definition</strong>:</p><ul><li><p>The circuit defines the constraints of our proof</p></li><li><p>We use signals to represent inputs and outputs</p></li><li><p>The constraint <code>a * a = b</code> ensures the proof is valid</p></li></ul></li><li><p><strong>Proof Generation</strong>:</p><ul><li><p>Takes private input (the number we know)</p></li><li><p>Generates a proof that we know this number</p></li><li><p>Outputs public signals (the result)</p></li></ul></li><li><p><strong>Proof Verification</strong>:</p><ul><li><p>Verifies the proof without knowing the private input</p></li><li><p>Ensures the proof is valid and the constraints are satisfied</p></li></ul></li></ol><h2 id="h-real-world-applications" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Real-World Applications</h2><p>Zero-knowledge proofs have numerous applications:</p><ol><li><p><strong>Privacy-Preserving Transactions</strong></p><ul><li><p>Proving you have enough funds without revealing your balance</p></li><li><p>Example: Zcash cryptocurrency</p></li></ul></li><li><p><strong>Identity Verification</strong></p><ul><li><p>Proving you&apos;re over 18 without revealing your exact age</p></li><li><p>Proving you&apos;re a citizen without revealing your ID</p></li></ul></li><li><p><strong>Supply Chain</strong></p><ul><li><p>Proving a product meets certain standards without revealing proprietary information</p></li></ul></li></ol><h2 id="h-best-practices" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Best Practices</h2><ol><li><p><strong>Security</strong></p><ul><li><p>Always use cryptographically secure random numbers</p></li><li><p>Keep private inputs secure</p></li><li><p>Use well-audited libraries</p></li></ul></li><li><p><strong>Performance</strong></p><ul><li><p>Optimize circuit size</p></li><li><p>Consider gas costs in blockchain applications</p></li><li><p>Use appropriate proving systems for your use case</p></li></ul></li><li><p><strong>Testing</strong></p><ul><li><p>Test with various inputs</p></li><li><p>Include edge cases</p></li><li><p>Verify proofs thoroughly</p></li></ul></li></ol><h2 id="h-common-challenges-and-solutions" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Common Challenges and Solutions</h2><ol><li><p><strong>Circuit Complexity</strong></p><ul><li><p>Start with simple circuits</p></li><li><p>Break down complex problems into smaller components</p></li><li><p>Use existing libraries when possible</p></li></ul></li><li><p><strong>Gas Costs</strong></p><ul><li><p>Optimize circuit design</p></li><li><p>Consider using more efficient proving systems</p></li><li><p>Batch proofs when possible</p></li></ul></li><li><p><strong>Trusted Setup</strong></p><ul><li><p>Use existing trusted setups when possible</p></li><li><p>Consider transparent setups (zk-STARKs)</p></li><li><p>Participate in multi-party computation ceremonies</p></li></ul></li></ol><h2 id="h-next-steps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Next Steps</h2><p>To continue your journey with zero-knowledge proofs:</p><ol><li><p>Explore more complex circuits</p></li><li><p>Learn about different proving systems (zk-SNARKs, zk-STARKs)</p></li><li><p>Study real-world implementations</p></li><li><p>Contribute to open-source ZK projects</p></li><li><p>Join the zero-knowledge community</p></li></ol><h2 id="h-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.circom.io/">Circom Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/iden3/snarkjs">SnarkJS Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zeroknowledge.fm/">Zero Knowledge Podcast</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zkproof.org/">ZKProof Standards</a></p></li></ul><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>This tutorial has introduced you to the basics of zero-knowledge proofs and helped you create your first proof. Remember that ZKPs are a powerful tool for privacy and scalability, but they require careful implementation and understanding of the underlying concepts.</p><p>As you continue your journey, focus on understanding the mathematical foundations, practice with different types of proofs, and explore real-world applications. The field of zero-knowledge proofs is rapidly evolving, offering exciting opportunities for innovation and development.</p>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 3 - Creating a zk snark circuit]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-3-creating-a-zk-snark-circuit</link>
            <guid>R4sioO7MIhrapnuUkGp9</guid>
            <pubDate>Tue, 17 Jun 2025 12:26:08 GMT</pubDate>
            <description><![CDATA[Creating Your First Zero-Knowledge Circuit: A Beginner&apos;s GuideIntroductionZero-knowledge proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. This tutorial will guide you through creating your first ZK circuit using Circom, one of the most popular languages for writing ZK circuits.PrerequisitesBefore we begin, mak...]]></description>
            <content:encoded><![CDATA[<h1 id="h-creating-your-first-zero-knowledge-circuit-a-beginners-guide" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Creating Your First Zero-Knowledge Circuit: A Beginner&apos;s Guide</h1><h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>Zero-knowledge proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. This tutorial will guide you through creating your first ZK circuit using Circom, one of the most popular languages for writing ZK circuits.</p><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before we begin, make sure you have the following installed:</p><ul><li><p>Node.js (v14 or higher)</p></li><li><p>npm (Node Package Manager)</p></li><li><p>Git</p></li></ul><h2 id="h-setting-up-your-development-environment" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Setting Up Your Development Environment</h2><ol><li><p>First, let&apos;s install the Circom compiler and snarkjs:</p></li></ol><pre data-type="codeBlock" text="npm install -g circom snarkjs
"><code>npm install <span class="hljs-operator">-</span>g circom snarkjs
</code></pre><ol><li><p>Create a new project directory and initialize it:</p></li></ol><pre data-type="codeBlock" text="mkdir my-first-zk-circuit
cd my-first-zk-circuit
npm init -y
"><code>mkdir my<span class="hljs-operator">-</span>first<span class="hljs-operator">-</span>zk<span class="hljs-operator">-</span>circuit
cd my<span class="hljs-operator">-</span>first<span class="hljs-operator">-</span>zk<span class="hljs-operator">-</span>circuit
npm init <span class="hljs-operator">-</span>y
</code></pre><h2 id="h-understanding-the-basics" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding the Basics</h2><h3 id="h-what-is-a-zk-circuit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What is a ZK Circuit?</h3><p>A ZK circuit is a program that defines a computational problem that can be proven without revealing the inputs. Think of it as a mathematical function that:</p><ol><li><p>Takes some inputs (private and public)</p></li><li><p>Performs computations</p></li><li><p>Produces outputs that can be verified</p></li></ol><h3 id="h-key-components-of-a-zk-circuit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Key Components of a ZK Circuit</h3><ol><li><p><strong>Signals</strong>: These are the inputs and outputs of your circuit</p><ul><li><p>Private signals: Known only to the prover</p></li><li><p>Public signals: Known to both prover and verifier</p></li></ul></li><li><p><strong>Constraints</strong>: These are the mathematical relationships that must be satisfied</p><ul><li><p>They define the valid computations in your circuit</p></li><li><p>They ensure the prover is following the rules</p></li></ul></li></ol><h2 id="h-creating-your-first-circuit" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Creating Your First Circuit</h2><p>Let&apos;s create a simple circuit that proves knowledge of a number that, when multiplied by itself, equals a given public number. This is a basic example that demonstrates the core concepts.</p><ol><li><p>Create a new file called <code>multiplier.circom</code>:</p></li></ol><pre data-type="codeBlock" text="pragma circom 2.0.0;

template Multiplier() {
    // Declaration of signals
    signal private input a;
    signal public output b;

    // Constraints
    b &lt;== a * a;
}

component main = Multiplier();
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> circom 2.0.0;</span>

template Multiplier() {
    <span class="hljs-comment">// Declaration of signals</span>
    signal <span class="hljs-keyword">private</span> input a;
    signal <span class="hljs-keyword">public</span> output b;

    <span class="hljs-comment">// Constraints</span>
    b <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> a <span class="hljs-operator">*</span> a;
}

component main <span class="hljs-operator">=</span> Multiplier();
</code></pre><p>Let&apos;s break down this circuit:</p><ul><li><p><code>pragma circom 2.0.0;</code> - Specifies the Circom version</p></li><li><p><code>template Multiplier()</code> - Defines our circuit template</p></li><li><p><code>signal private input a;</code> - Declares a private input signal</p></li><li><p><code>signal public output b;</code> - Declares a public output signal</p></li><li><p><code>b &lt;== a * a;</code> - Defines the constraint (b must equal a squared)</p></li></ul><h2 id="h-compiling-and-testing-the-circuit" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Compiling and Testing the Circuit</h2><ol><li><p>Compile the circuit:</p></li></ol><pre data-type="codeBlock" text="circom multiplier.circom --r1cs --wasm --sym
"><code>circom multiplier.circom <span class="hljs-operator">-</span><span class="hljs-operator">-</span>r1cs <span class="hljs-operator">-</span><span class="hljs-operator">-</span>wasm <span class="hljs-operator">-</span><span class="hljs-operator">-</span>sym
</code></pre><ol><li><p>Generate the proving and verification keys:</p></li></ol><pre data-type="codeBlock" text="snarkjs powersoftau new bn128 12 pot12_0000.ptau -v
snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau --name=&quot;First contribution&quot; -v
snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau -v
snarkjs groth16 setup multiplier.r1cs pot12_final.ptau multiplier_0000.zkey
snarkjs zkey contribute multiplier_0000.zkey multiplier_0001.zkey --name=&quot;First contribution&quot; -v
snarkjs zkey export verificationkey multiplier_0001.zkey verification_key.json
"><code>snarkjs powersoftau <span class="hljs-keyword">new</span> bn128 <span class="hljs-number">12</span> pot12_0000.ptau <span class="hljs-operator">-</span>v
snarkjs powersoftau contribute pot12_0000.ptau pot12_0001.ptau <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name<span class="hljs-operator">=</span><span class="hljs-string">"First contribution"</span> <span class="hljs-operator">-</span>v
snarkjs powersoftau prepare phase2 pot12_0001.ptau pot12_final.ptau <span class="hljs-operator">-</span>v
snarkjs groth16 setup multiplier.r1cs pot12_final.ptau multiplier_0000.zkey
snarkjs zkey contribute multiplier_0000.zkey multiplier_0001.zkey <span class="hljs-operator">-</span><span class="hljs-operator">-</span>name<span class="hljs-operator">=</span><span class="hljs-string">"First contribution"</span> <span class="hljs-operator">-</span>v
snarkjs zkey export verificationkey multiplier_0001.zkey verification_key.json
</code></pre><ol><li><p>Create a test input file <code>input.json</code>:</p></li></ol><pre data-type="codeBlock" text="{
  &quot;a&quot;: 5
}
"><code>{
  "<span class="hljs-selector-tag">a</span>": <span class="hljs-number">5</span>
}
</code></pre><ol><li><p>Generate the proof:</p></li></ol><pre data-type="codeBlock" text="snarkjs groth16 prove multiplier_0001.zkey witness.wtns proof.json public.json
"><code>snarkjs groth16 prove multiplier_0001.zkey witness.wtns proof.json <span class="hljs-keyword">public</span>.json
</code></pre><ol><li><p>Verify the proof:</p></li></ol><pre data-type="codeBlock" text="snarkjs groth16 verify verification_key.json public.json proof.json
"><code>snarkjs groth16 verify verification_key.json <span class="hljs-keyword">public</span>.json proof.json
</code></pre><h2 id="h-understanding-what-happened" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding What Happened</h2><p>Let&apos;s break down what we just did:</p><ol><li><p><strong>Circuit Definition</strong>: We created a circuit that proves knowledge of a number that, when squared, equals a given output.</p></li><li><p><strong>Compilation</strong>: The circuit was compiled into a format that can be used to generate and verify proofs.</p></li><li><p><strong>Trusted Setup</strong>: We performed a trusted setup to generate the proving and verification keys.</p></li><li><p><strong>Proof Generation</strong>: We generated a proof that we know a number (5) that, when squared, equals 25.</p></li><li><p><strong>Verification</strong>: We verified that the proof is valid without revealing the original number.</p></li></ol><h2 id="h-best-practices" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Best Practices</h2><ol><li><p><strong>Security</strong>:</p><ul><li><p>Always keep private inputs secure</p></li><li><p>Use appropriate bit lengths for your signals</p></li><li><p>Be careful with arithmetic overflow</p></li></ul></li><li><p><strong>Performance</strong>:</p><ul><li><p>Minimize the number of constraints</p></li><li><p>Use efficient circuit designs</p></li><li><p>Consider the trade-off between circuit size and security</p></li></ul></li><li><p><strong>Testing</strong>:</p><ul><li><p>Test your circuit with various inputs</p></li><li><p>Include edge cases</p></li><li><p>Verify the constraints work as expected</p></li></ul></li></ol><h2 id="h-common-pitfalls-to-avoid" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Common Pitfalls to Avoid</h2><ol><li><p><strong>Arithmetic Overflow</strong>: Always consider the maximum values your signals can take</p></li><li><p><strong>Incorrect Constraints</strong>: Double-check your mathematical relationships</p></li><li><p><strong>Inefficient Designs</strong>: Keep your circuits as simple as possible</p></li><li><p><strong>Security Assumptions</strong>: Don&apos;t make assumptions about the security of your inputs</p></li></ol><h2 id="h-next-steps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Next Steps</h2><p>Now that you&apos;ve created your first ZK circuit, here are some suggestions for further learning:</p><ol><li><p>Try creating more complex circuits</p></li><li><p>Learn about different proving systems (Groth16, PLONK, etc.)</p></li><li><p>Explore optimization techniques</p></li><li><p>Study real-world applications of ZK proofs</p></li><li><p>Join the ZK community and contribute to open-source projects</p></li></ol><h2 id="h-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.circom.io/">Circom Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/iden3/snarkjs">SnarkJS Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zeroknowledge.fm/">Zero Knowledge Podcast</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zkstudyclub.com/">ZK Study Club</a></p></li></ul><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>Congratulations! You&apos;ve created your first ZK circuit. This is just the beginning of your journey into the world of zero-knowledge proofs. Remember that ZK technology is still evolving, and there&apos;s always more to learn. Keep experimenting, building, and contributing to the community.</p><p>Happy coding!</p>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 2 - Understanding core concepts in zk technology]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-2-understanding-core-concepts-in-zk-technology</link>
            <guid>l2GRO7OXoXhyd8JKSLSJ</guid>
            <pubDate>Tue, 17 Jun 2025 12:25:14 GMT</pubDate>
            <description><![CDATA[Understanding Core Concepts in Zero-Knowledge TechnologyMathematical FoundationsAt the heart of zero-knowledge proofs lies the concept of finite fields and arithmetic circuits. These mathematical constructs form the foundation upon which ZKPs are built, providing the mathematical structure needed for secure and efficient cryptographic operations.Finite FieldsFinite fields, also known as Galois fields (named after mathematician Évariste Galois), are fundamental algebraic structures in cryptogr...]]></description>
            <content:encoded><![CDATA[<h1 id="h-understanding-core-concepts-in-zero-knowledge-technology" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Understanding Core Concepts in Zero-Knowledge Technology</h1><h2 id="h-mathematical-foundations" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Mathematical Foundations</h2><p>At the heart of zero-knowledge proofs lies the concept of finite fields and arithmetic circuits. These mathematical constructs form the foundation upon which ZKPs are built, providing the mathematical structure needed for secure and efficient cryptographic operations.</p><h3 id="h-finite-fields" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Finite Fields</h3><p>Finite fields, also known as Galois fields (named after mathematician Évariste Galois), are fundamental algebraic structures in cryptography and zero-knowledge proofs. They are fields with a finite number of elements, where a field is a set equipped with addition, subtraction, multiplication, and division operations that satisfy certain properties.</p><p>Key characteristics of finite fields in ZKPs:</p><ul><li><p>A finite field (denoted as Fp) contains exactly p elements, where p is a prime number</p></li><li><p>In ZKPs, we typically work with large prime fields (e.g., fields of order 2^254 - 2^30 + 1)</p></li><li><p>All arithmetic operations (addition, multiplication) are performed modulo p</p></li><li><p>This modular arithmetic ensures that all computations remain within the field&apos;s bounds</p></li><li><p>The field&apos;s size determines the security and efficiency of the ZKP system</p></li></ul><p>The choice of field size is crucial:</p><ul><li><p>Too small: vulnerable to attacks</p></li><li><p>Too large: inefficient computations</p></li><li><p>Prime fields are preferred over extension fields for their simplicity and efficiency</p></li></ul><p>To learn more about finite fields and their role in cryptography:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://crypto.stanford.edu/pbc/notes/numbertheory/finite.html">Finite Fields in Cryptography</a> - Stanford&apos;s cryptography course notes</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.math.brown.edu/johsilve/frintro.html">Understanding Finite Fields</a> - Brown University&apos;s introduction to finite fields</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.math.clemson.edu/~macaule/classes/m20_math4120/slides/math4120_lecture-01-10.pdf">Finite Fields and Their Applications</a> - Clemson University&apos;s lecture notes</p></li></ul><h3 id="h-arithmetic-circuits" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Arithmetic Circuits</h3><p>Arithmetic circuits are fundamental computational models in zero-knowledge proofs that represent mathematical computations as directed acyclic graphs (DAGs). They serve as the computational backbone for expressing complex operations in a way that can be efficiently proven and verified.</p><p>Key Components:</p><ul><li><p><strong>Nodes</strong>: Represent arithmetic operations (primarily addition and multiplication)</p></li><li><p><strong>Edges</strong>: Represent the flow of values between operations</p></li><li><p><strong>Inputs</strong>: Clearly defined input variables or constants</p></li><li><p><strong>Outputs</strong>: The final computation results</p></li><li><p><strong>Properties</strong>: Must be deterministic (same inputs always produce same outputs) and side-effect free (no external state changes)</p></li></ul><p>The circuit&apos;s structure is crucial because:</p><ul><li><p>It determines the number of constraints in the zero-knowledge proof</p></li><li><p>It affects the proof generation and verification time</p></li><li><p>It impacts the overall efficiency of the system</p></li></ul><p>Example of a simple arithmetic circuit computing (a + b) * (a - b):</p><ul><li><p><strong>Binding</strong>: Once committed, the prover cannot change the polynomial</p></li><li><p><strong>Hiding</strong>: The commitment reveals nothing about the polynomial</p></li><li><p><strong>Evaluation</strong>: Can prove evaluations at specific points without revealing the polynomial</p></li></ul><h2 id="h-common-commitment-schemes" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Common Commitment Schemes</h2><p>Commitment schemes are cryptographic primitives that allow a prover to commit to a value while keeping it hidden until later. Here are the most common commitment schemes used in zero-knowledge proofs:</p><p><strong>Kate Commitments (KZG)</strong> are a powerful commitment scheme that leverages pairing-friendly elliptic curves. These curves enable efficient bilinear pairings, which are mathematical operations that map pairs of points on elliptic curves to elements in a finite field. The key advantage of KZG commitments is their ability to produce constant-sized proofs, meaning the proof size remains the same regardless of the polynomial degree being proven. This makes them highly efficient for both provers and verifiers. However, they require a trusted setup ceremony to generate a structured reference string (SRS), which is a critical security parameter. While KZG commitments are widely used in modern ZK systems like Plonk and Sonic, they are vulnerable to quantum attacks due to their reliance on pairing-based cryptography.</p><p><strong>FRI (Fast Reed-Solomon Interactive)</strong> is a commitment scheme that forms the core of zk-STARKs. It provides a way to prove polynomial evaluations through a series of interactive steps. Unlike KZG, FRI is quantum-resistant because it relies on hash functions rather than number-theoretic assumptions. This makes it more secure against future quantum computing attacks. FRI doesn&apos;t require a trusted setup, which enhances its transparency and security. However, this comes at the cost of larger proof sizes, which grow logarithmically with the polynomial degree. The scheme uses interactive oracle proofs (IOPs) to achieve its security properties, making it more complex to implement but offering better long-term security guarantees.</p><p><strong>Bulletproofs</strong> are a specialized commitment scheme designed primarily for range proofs and inner product arguments. They are particularly notable for not requiring a trusted setup, making them more transparent and easier to deploy. The proof size in Bulletproofs grows logarithmically with the size of the statement being proven. They are based on discrete logarithm assumptions, which are well-studied cryptographic primitives. Bulletproofs have found significant application in privacy-preserving cryptocurrencies, where they enable efficient range proofs for confidential transactions.</p><p><strong>DARK (Diophantine Arguments of Knowledge)</strong> is a commitment scheme used in systems like Supersonic and PLONK. It&apos;s based on class groups, which are mathematical structures that provide certain cryptographic properties. Like Bulletproofs, DARK doesn&apos;t require a trusted setup, making it more transparent and secure. It produces constant-sized proofs, similar to KZG, but with the added benefit of being quantum-resistant. This makes DARK an attractive choice for systems that need to balance efficiency with long-term security considerations.</p><p>Each of these commitment schemes offers different trade-offs between proof size, security assumptions, and implementation complexity. The choice of which to use depends on the specific requirements of the zero-knowledge proof system being built, including security needs, performance requirements, and whether a trusted setup is acceptable.</p><h2 id="h-non-interactive-proofs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Non-Interactive Proofs</h2><p>Non-interactive proofs (NIPs) are a crucial advancement in zero-knowledge technology that eliminate the need for real-time interaction between prover and verifier. Instead of multiple rounds of communication, these proofs are generated in a single step and can be verified independently.</p><h3 id="h-key-characteristics" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Key Characteristics</h3><ul><li><p><strong>Single Message</strong>: The entire proof is contained in one message from prover to verifier</p></li><li><p><strong>Public Verifiability</strong>: Anyone with the proof can verify it without interaction</p></li><li><p><strong>Common Reference String</strong>: Often requires a trusted setup phase to generate public parameters</p></li><li><p><strong>Fiat-Shamir Transform</strong>: Many NIPs are created by converting interactive proofs using this technique</p></li></ul><h3 id="h-types-of-non-interactive-proofs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Types of Non-interactive Proofs</h3><ol><li><p><strong>zk-SNARKs</strong> (Zero-Knowledge Succinct Non-interactive Arguments of Knowledge)</p><ul><li><p>Require trusted setup</p></li><li><p>Provide constant-size proofs</p></li><li><p>Used in privacy-preserving cryptocurrencies</p></li><li><p>Example: Groth16 protocol</p></li></ul></li><li><p><strong>zk-STARKs</strong> (Zero-Knowledge Scalable Transparent Arguments of Knowledge)</p><ul><li><p>No trusted setup required</p></li><li><p>Quantum-resistant</p></li><li><p>Larger proof sizes but faster verification</p></li><li><p>Example: FRI-based protocols</p></li></ul></li><li><p><strong>Bulletproofs</strong></p><ul><li><p>No trusted setup</p></li><li><p>Logarithmic proof size</p></li><li><p>Efficient for range proofs</p></li><li><p>Used in confidential transactions</p></li></ul></li></ol><h3 id="h-advantages" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Advantages</h3><ul><li><p><strong>Offline Verification</strong>: Proofs can be verified at any time</p></li><li><p><strong>Scalability</strong>: Can be broadcast to multiple verifiers</p></li><li><p><strong>Storage Efficiency</strong>: Single proof can be stored and reused</p></li><li><p><strong>Blockchain Compatibility</strong>: Well-suited for blockchain applications</p></li></ul><h3 id="h-applications" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Applications</h3><ul><li><p><strong>Privacy-Preserving Transactions</strong>: Hiding transaction amounts while proving validity</p></li><li><p><strong>Identity Verification</strong>: Proving identity attributes without revealing them</p></li><li><p><strong>Supply Chain</strong>: Proving compliance without revealing sensitive data</p></li><li><p><strong>Voting Systems</strong>: Proving vote validity without revealing the vote</p></li></ul><h2 id="h-core-zk-technologies" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Core ZK Technologies</h2><h3 id="h-r1cs-rank-1-constraint-systems" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">R1CS (Rank-1 Constraint Systems)</h3><p>R1CS is a fundamental mathematical framework used to represent arithmetic circuits as a system of equations. It serves as the foundation for many zero-knowledge proof systems, particularly zk-SNARKs.</p><h4 id="h-structure-and-mathematical-foundation" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Structure and Mathematical Foundation</h4><p>R1CS represents constraints using three matrices (A, B, C) and a vector of variables (x). The system enforces that for each row i: (A_i·x) * (B_i·x) = (C_i·x)</p><p>This structure allows us to:</p><ul><li><p>Represent any arithmetic circuit as a series of constraints</p></li><li><p>Convert complex computations into a format suitable for zero-knowledge proofs</p></li><li><p>Maintain the mathematical relationships between variables while hiding their values</p></li></ul><h4 id="h-detailed-example" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Detailed Example</h4><p>Let&apos;s break down the example of a * b = c:</p><ol><li><p>The vector x represents our variables: x = [1, a, b, c]</p><ul><li><p>First element is always 1 (constant term)</p></li><li><p>Remaining elements are our actual variables</p></li></ul></li><li><p>The matrices A, B, and C encode the constraint:</p><ul><li><p>A = [1 0 0] selects variable &apos;a&apos;</p></li><li><p>B = [0 1 0] selects variable &apos;b&apos;</p></li><li><p>C = [0 0 1] selects variable &apos;c&apos;</p></li></ul></li><li><p>When we compute (A·x) * (B·x) = (C·x), we get:</p><ul><li><p>(A·x) = a</p></li><li><p>(B·x) = b</p></li><li><p>(C·x) = c</p></li><li><p>Therefore: a * b = c</p></li></ul></li></ol><h2 id="h-some-tools-and-frameworks" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Some Tools and Frameworks</h2><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.circom.io/"><strong>Circom</strong></a></p><ul><li><p>Circuit compiler</p></li><li><p>C-like syntax</p></li><li><p>Good documentation</p></li></ul></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zokrates.github.io/"><strong>ZoKrates</strong></a></p><ul><li><p>High-level language</p></li><li><p>Ethereum integration</p></li><li><p>Standard library</p></li></ul></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.cairo-lang.org/"><strong>Cairo</strong></a></p><ul><li><p>STARK-friendly</p></li><li><p>Growing ecosystem</p></li><li><p>Good tooling</p></li></ul></li></ol><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>Understanding the core concepts of zero-knowledge technology requires a deep dive into mathematics, cryptography, and computer science. The field continues to evolve rapidly, with new developments in proof systems, optimization techniques, and applications.</p><p>As the technology matures, we can expect to see more efficient implementations, better developer tools, and wider adoption across various industries. The key to successful implementation lies in understanding these core concepts and applying them appropriately to specific use cases.</p><h2 id="h-further-reading" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Further Reading</h2><ol><li><p><strong>Technical Papers</strong></p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://arxiv.org/abs/1906.07221">Why and How zk-SNARK Works</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.zkproof.org/">ZKProof Standards</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eprint.iacr.org/2019/953">Plonk Paper</a></p></li></ul></li><li><p><strong>Implementation Guides</strong></p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.circom.io/">Circom Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zokrates.github.io/">ZoKrates Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.cairo-lang.org/docs/">Cairo Documentation</a></p></li></ul></li><li><p><strong>Community Resources</strong></p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zkproof.org/">ZKProof.org</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zkresearch.org/">ZK Research</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zeroknowledge.fm/">ZK Podcast</a></p></li></ul></li></ol>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[SEI ZK Series Part 1 - Introduction to zero knowledge proofs]]></title>
            <link>https://paragraph.com/@4undraiser/sei-zk-series-part-1-introduction-to-zero-knowledge-proofs</link>
            <guid>zTcbW093wBCqZNYniVRl</guid>
            <pubDate>Tue, 17 Jun 2025 12:24:04 GMT</pubDate>
            <description><![CDATA[Introduction to Zero-Knowledge Proofs (ZKPs)What are Zero-Knowledge Proofs?Zero-Knowledge Proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. In other words, ZKPs enable you to prove you know something without revealing what you know. For example, imagine you want to prove you&apos;re over 21 years old without reveali...]]></description>
            <content:encoded><![CDATA[<h1 id="h-introduction-to-zero-knowledge-proofs-zkps" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction to Zero-Knowledge Proofs (ZKPs)</h1><h2 id="h-what-are-zero-knowledge-proofs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What are Zero-Knowledge Proofs?</h2><p>Zero-Knowledge Proofs (ZKPs) are cryptographic methods that allow one party (the prover) to prove to another party (the verifier) that a statement is true without revealing any additional information beyond the validity of the statement itself. In other words, ZKPs enable you to prove you know something without revealing what you know.</p><p>For example, imagine you want to prove you&apos;re over 21 years old without revealing your actual birth date. With ZKPs, you could prove this statement is true while keeping your exact age private. Similarly, you could prove you have enough money in your bank account for a transaction without revealing your actual balance, or demonstrate you know a password without transmitting it.</p><p>ZKPs are particularly powerful in scenarios where privacy and security are crucial. They&apos;re used in:</p><ul><li><p>Privacy-preserving cryptocurrencies (like Zcash) to hide transaction amounts while proving they&apos;re valid</p></li><li><p>Secure voting systems where voters can prove their vote was counted without revealing who they voted for</p></li><li><p>Age verification systems where users can prove they meet age requirements without sharing their date of birth</p></li><li><p>Authentication systems where users can prove they know a password without sending it over the network</p></li></ul><p>The beauty of ZKPs lies in their ability to maintain privacy while still providing cryptographic certainty that a statement is true. This makes them invaluable in our increasingly privacy-conscious digital world.</p><h3 id="h-the-three-properties-of-zero-knowledge-proofs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Three Properties of Zero-Knowledge Proofs</h3><p>For a protocol to be considered a zero-knowledge proof, it must satisfy three key properties:</p><ol><li><p><strong>Completeness</strong>: If the statement is true, an honest verifier will be convinced by an honest prover.</p></li><li><p><strong>Soundness</strong>: If the statement is false, no cheating prover can convince an honest verifier of its validity, except with some tiny probability.</p></li><li><p><strong>Zero-Knowledge</strong>: If the statement is true, the verifier learns nothing other than the fact that the statement is true.</p></li></ol><h2 id="h-the-classic-example-the-cave-analogy" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Classic Example: The Cave Analogy</h2><p>To better understand ZKPs, let&apos;s explore the famous &quot;Cave Analogy&quot;:</p><p>Imagine a circular cave with a single entrance. Peggy (the prover) wants to prove to Victor (the verifier) that she knows a secret password to open a door on the opposite side of the cave, without revealing the password itself.</p><p>Here&apos;s how it works:</p><ol><li><p>Victor waits outside while Peggy enters the cave</p></li><li><p>Peggy goes either left or right inside the cave (Victor doesn&apos;t see which way)</p></li><li><p>Victor enters the cave and shouts which way Peggy should come out (left or right)</p></li><li><p>If Peggy knows the password, she can always exit the way Victor requests</p></li><li><p>This process is repeated multiple times</p></li></ol><p>If Peggy consistently exits the correct way, Victor becomes convinced that she knows the password, even though he never sees it.</p><h2 id="h-types-of-zero-knowledge-proofs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Types of Zero-Knowledge Proofs</h2><h3 id="h-1-interactive-zero-knowledge-proofs-izkps" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Interactive Zero-Knowledge Proofs (IZKPs)</h3><ul><li><p>Require back-and-forth communication between prover and verifier</p></li><li><p>The cave analogy is an example of an interactive proof</p></li><li><p>Typically used in academic settings and theoretical applications</p></li></ul><h3 id="h-2-non-interactive-zero-knowledge-proofs-nizkps" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Non-Interactive Zero-Knowledge Proofs (NIZKPs)</h3><ul><li><p>Require only one message from prover to verifier</p></li><li><p>More practical for real-world applications</p></li><li><p>Can be verified by anyone who has the proof</p></li><li><p>Examples include zk-SNARKs and zk-STARKs</p></li></ul><h2 id="h-key-zkp-systems" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Key ZKP Systems</h2><h3 id="h-zk-snarks-zero-knowledge-succinct-non-interactive-arguments-of-knowledge" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge)</h3><p>zk-SNARKs represent a breakthrough in zero-knowledge cryptography, being the first practical implementation of Non-Interactive Zero-Knowledge Proofs (NIZKPs). The acronym breaks down into:</p><ul><li><p><strong>Zero-Knowledge</strong>: The proof reveals nothing about the underlying data</p></li><li><p><strong>Succinct</strong>: Proofs are small in size and can be verified quickly</p></li><li><p><strong>Non-Interactive</strong>: Requires only one message from prover to verifier</p></li><li><p><strong>Arguments of Knowledge</strong>: The prover must know the witness (secret data) to generate a valid proof</p></li></ul><p>Key characteristics:</p><ul><li><p><strong>Trusted Setup</strong>: Requires a one-time &quot;ceremony&quot; to generate public parameters. This is a critical security consideration as anyone with access to the &quot;toxic waste&quot; from this setup could potentially forge proofs</p></li><li><p><strong>Efficient Verification</strong>: Proofs can be verified in constant time, regardless of the complexity of the original computation</p></li><li><p><strong>Small Proof Size</strong>: Typically just a few hundred bytes, making them ideal for blockchain applications</p></li><li><p><strong>Wide Adoption</strong>: Used in Zcash for private transactions, and in various other privacy-focused cryptocurrencies and applications</p></li></ul><p>Notable implementations include:</p><ul><li><p><strong>Zcash</strong>: Uses zk-SNARKs to enable private transactions while maintaining the security of the blockchain</p></li><li><p><strong>Tornado Cash</strong>: Implements zk-SNARKs for private token transfers</p></li><li><p><strong>Aztec Protocol</strong>: Uses zk-SNARKs for private smart contracts on Ethereum</p></li></ul><p>While powerful, zk-SNARKs do have some limitations:</p><ul><li><p>The trusted setup requirement introduces potential security risks</p></li><li><p>They are not quantum-resistant</p></li><li><p>The proving process can be computationally intensive</p></li></ul><h3 id="h-zk-starks-zero-knowledge-scalable-transparent-arguments-of-knowledge" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">zk-STARKs (Zero-Knowledge Scalable Transparent Arguments of Knowledge)</h3><p>zk-STARKs represent a newer generation of zero-knowledge proof systems that address some of the limitations of zk-SNARKs. The acronym breaks down into:</p><ul><li><p><strong>Zero-Knowledge</strong>: The proof reveals nothing about the underlying data</p></li><li><p><strong>Scalable</strong>: Proof generation and verification times scale quasi-linearly with the size of the computation</p></li><li><p><strong>Transparent</strong>: No trusted setup required, making them more trustless and secure</p></li><li><p><strong>Arguments of Knowledge</strong>: The prover must know the witness (secret data) to generate a valid proof</p></li></ul><p>Key characteristics:</p><ul><li><p><strong>Trustless Setup</strong>: Unlike zk-SNARKs, zk-STARKs don&apos;t require a trusted setup ceremony, eliminating the risk of &quot;toxic waste&quot; and making them more suitable for trustless environments</p></li><li><p><strong>Quantum Resistance</strong>: Based on hash functions and symmetric cryptography, making them resistant to quantum computing attacks</p></li><li><p><strong>Performance Trade-offs</strong>:</p><ul><li><p>Larger proof sizes (typically kilobytes instead of bytes)</p></li><li><p>Faster proving times, especially for complex computations</p></li><li><p>Verification times that scale with the size of the computation</p></li></ul></li><li><p><strong>Scalability</strong>: Particularly well-suited for scaling blockchain networks through Layer 2 solutions</p></li></ul><p>Notable implementations and use cases:</p><ul><li><p><strong>StarkNet</strong>: A Layer 2 scaling solution for Ethereum that uses zk-STARKs for off-chain computation with on-chain verification</p></li><li><p><strong>StarkEx</strong>: A scaling engine powering applications like dYdX and Immutable X</p></li><li><p><strong>Other Applications</strong>: Identity systems, voting mechanisms, and private computation platforms</p></li></ul><p>While zk-STARKs offer significant advantages, they also have some considerations:</p><ul><li><p>The larger proof sizes can increase on-chain storage costs</p></li><li><p>The technology is newer and less battle-tested than zk-SNARKs</p></li><li><p>Implementation complexity can be higher due to the lack of mature tooling</p></li></ul><h2 id="h-real-world-applications" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Real-World Applications</h2><h3 id="h-1-privacy-preserving-cryptocurrencies" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Privacy-Preserving Cryptocurrencies</h3><ul><li><p>Zcash uses zk-SNARKs to enable private transactions</p></li><li><p>Monero uses ring signatures and confidential transactions</p></li><li><p>Allows users to prove they have sufficient funds without revealing their balance</p></li></ul><h3 id="h-2-layer-2-scaling-solutions" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Layer 2 Scaling Solutions</h3><ul><li><p>StarkNet and zkSync use ZKPs for scaling Ethereum</p></li><li><p>Enables off-chain computation with on-chain verification</p></li><li><p>Reduces gas costs and increases throughput</p></li></ul><h3 id="h-3-identity-and-authentication" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. Identity and Authentication</h3><ul><li><p>Prove identity without revealing personal information</p></li><li><p>Age verification without revealing exact age</p></li><li><p>KYC/AML compliance while maintaining privacy</p></li></ul><h3 id="h-4-decentralized-voting" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4. Decentralized Voting</h3><ul><li><p>Prove vote validity without revealing who you voted for</p></li><li><p>Ensure one person, one vote without revealing identity</p></li><li><p>Maintain privacy while ensuring election integrity</p></li></ul><h2 id="h-technical-components" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Technical Components</h2><h3 id="h-1-circuit-design" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Circuit Design</h3><p>Circuit design is a fundamental aspect of zero-knowledge proof systems. At its core, ZKPs work by converting statements into arithmetic circuits, where complex computations are broken down into basic arithmetic operations like addition and multiplication. These operations must be expressible in the finite field arithmetic of the proving system.</p><p>Circuits serve as mathematical representations of computations, operating similarly to digital logic circuits but over finite fields. They can represent any deterministic computation, from simple arithmetic to complex programs, with the key requirement that they must be side-effect free. The design of these circuits is crucial, as it directly impacts the performance and usability of the entire system. Circuit size affects proof generation time and size, while different proving systems may require different optimal circuit structures. Developers often employ various optimizations, such as custom gates and lookup tables, to improve efficiency.</p><h3 id="h-2-proving-systems" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Proving Systems</h3><p>Proving systems form the bridge between circuit design and actual proof generation. They transform circuit constraints into polynomial equations and generate commitment schemes for circuit values. This transformation process is essential for creating both interactive and non-interactive protocols.</p><p>During proof generation, the system processes private inputs through the circuit, generates cryptographic commitments to intermediate values, and creates zero-knowledge proofs of correct computation. Modern proving systems often incorporate various optimizations, including batch proving and recursion, to improve performance.</p><p>Different proving systems are optimized for different use cases. Some, like zk-STARKs, prioritize proof generation speed, while others, such as Groth16, focus on minimal proof size. This leads to important trade-offs between prover time, verifier time, and proof size. Additionally, specialized systems like Bulletproofs have been developed for specific applications such as range proofs.</p><h3 id="h-3-verification" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. Verification</h3><p>Verification is the final crucial component of zero-knowledge proof systems. It involves efficiently checking cryptographic commitments and openings, verifying that polynomial equations hold, and optimizing the verification process for specific hardware, particularly for on-chain verification scenarios.</p><p>Security and correctness are paramount in verification. The system must verify soundness and zero-knowledge properties, check for common vulnerabilities and attacks, validate all cryptographic assumptions, and ensure proper parameter selection. This comprehensive security approach is essential for maintaining the integrity of the proof system.</p><p>Verification systems must handle various scenarios, including on-chain versus off-chain verification, single versus batch verification, and trusted versus trustless setups. Each scenario presents different security levels and trade-offs that must be carefully considered. The choice of verification approach can significantly impact the overall system&apos;s performance and security characteristics.</p><h2 id="h-getting-started-with-zkps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Getting Started with ZKPs</h2><h3 id="h-popular-libraries-and-tools" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Popular Libraries and Tools</h3><ol><li><p><strong>Circom</strong></p><ul><li><p>Circuit compiler for zk-SNARKs</p></li><li><p>Used in many Ethereum-based projects</p></li><li><p>Good for beginners due to its C-like syntax</p></li></ul></li><li><p><strong>ZoKrates</strong></p><ul><li><p>High-level language for ZKP development</p></li><li><p>Built on top of libsnark</p></li><li><p>Good integration with Ethereum</p></li></ul></li><li><p><strong>StarkWare&apos;s Cairo</strong></p><ul><li><p>Language for writing STARK-provable programs</p></li><li><p>Used in StarkNet</p></li><li><p>Growing ecosystem and tooling</p></li></ul></li></ol><h2 id="h-challenges-and-considerations" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Challenges and Considerations</h2><h3 id="h-1-trusted-setup" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Trusted Setup</h3><ul><li><p>Many ZKP systems require a trusted setup</p></li><li><p>If compromised, the entire system&apos;s security is at risk</p></li><li><p>Newer systems like zk-STARKs eliminate this requirement</p></li></ul><h3 id="h-2-performance" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Performance</h3><ul><li><p>Proof generation can be computationally expensive</p></li><li><p>Proof sizes can be large</p></li><li><p>Ongoing research focuses on optimization</p></li></ul><h3 id="h-3-usability" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. Usability</h3><ul><li><p>Complex mathematical concepts</p></li><li><p>Steep learning curve</p></li><li><p>Need for better developer tools and documentation</p></li></ul><h2 id="h-future-of-zero-knowledge-proofs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Future of Zero-Knowledge Proofs</h2><h3 id="h-1-improved-efficiency" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Improved Efficiency</h3><ul><li><p>Smaller proof sizes</p></li><li><p>Faster proving times</p></li><li><p>Better hardware acceleration</p></li></ul><h3 id="h-2-better-developer-experience" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Better Developer Experience</h3><ul><li><p>More intuitive programming languages</p></li><li><p>Better debugging tools</p></li><li><p>Improved documentation and tutorials</p></li></ul><h3 id="h-3-new-applications" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. New Applications</h3><ul><li><p>Decentralized identity systems</p></li><li><p>Privacy-preserving machine learning</p></li><li><p>Secure multi-party computation</p></li></ul><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>Zero-Knowledge Proofs represent a powerful cryptographic tool that enables privacy and scalability in blockchain systems. While the technology is still evolving, it has already shown tremendous potential in various applications, from private cryptocurrencies to scaling solutions.</p><p>As the ecosystem matures and tools improve, we can expect to see more developers and applications adopting ZKPs to solve real-world problems while maintaining privacy and security.</p><h2 id="h-resources-for-further-learning" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Resources for Further Learning</h2><ol><li><p><strong>Books and Papers</strong></p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf">Proofs, Arguments, and Zero-Knowledge</a> - Free online book by Justin Thaler</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://toc.cryptobook.us/">A Graduate Course in Applied Cryptography</a> - Free online book by Dan Boneh and Victor Shoup</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://arxiv.org/abs/1906.07221">Why and How zk-SNARK Works</a> - Technical paper by Maksym Petkus</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.zkproof.org/">ZKProof Standards</a> - Open standards for zero-knowledge proof systems</p></li></ul></li><li><p><strong>Online Courses and Tutorials</strong></p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.coursera.org/learn/crypto">Cryptography I</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.coursera.org/learn/crypto2">Cryptography II</a> - Stanford&apos;s free courses on Coursera</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zk-learning.org/">Zero Knowledge Proofs</a> - Comprehensive learning resources curated by the ZK community</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zkhack.dev/">ZK Hack</a> - Hands-on workshops and challenges</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.youtube.com/playlist?list=PLj80z0cJm8QHm_9BdZ1BqcGbgE-BEn-3Y">ZK Whiteboard Sessions</a> - Video series by 0xPARC</p></li></ul></li><li><p><strong>Developer Resources</strong></p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.zkdocs.com/">ZK Docs</a> - Technical documentation and guides</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zeroknowledge.fm/">ZK Podcast</a> - Regular episodes covering ZK developments</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zkresearch.org/">ZK Research</a> - Latest research papers and developments</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/0xPARC/zk-bug-tracker">ZK Security</a> - Known vulnerabilities and security considerations</p></li></ul></li></ol>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
        </item>
        <item>
            <title><![CDATA[Composability in Defi]]></title>
            <link>https://paragraph.com/@4undraiser/composability-in-defi</link>
            <guid>OzPZiQcEoBmXOYV75o7h</guid>
            <pubDate>Tue, 03 Dec 2024 02:29:42 GMT</pubDate>
            <description><![CDATA[What is composability.In Web3, composability is like digital Lego blocks for applications and services. It means that different blockchain-based apps, protocols, and smart contracts can work together seamlessly, building on each other to create new and more powerful tools. Because everything is open-source and interoperable, developers can take existing pieces and combine them to build something entirely new without starting from scratch. For example, in DeFi (Decentralized Finance), composab...]]></description>
            <content:encoded><![CDATA[<h2 id="h-what-is-composability" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>What is composability.</strong></h2><p>In Web3, composability is like digital Lego blocks for applications and services. It means that different blockchain-based apps, protocols, and smart contracts can work together seamlessly, building on each other to create new and more powerful tools. Because everything is open-source and interoperable, developers can take existing pieces and combine them to build something entirely new without starting from scratch.</p><p>For example, in DeFi (Decentralized Finance), composability lets one app handle lending, another manage trading, and yet another optimize yields all working together in harmony. Users benefit by getting more options, better efficiency, and the ability to do complex things like borrowing, trading, and earning rewards in just a few steps.</p><h2 id="h-how-composability-works" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How Composability Works.</h2><p>Open Standards and Interoperability: Most Web3 applications are built on shared standards (like ERC-20 tokens or ERC-721 NFTs on Ethereum). These standards ensure that any app adhering to them can interact with others without additional integration work.</p><p>Permissionless Architecture: Anyone can use, integrate, or build upon existing protocols because they are open-source. This eliminates gatekeeping and fosters innovation.</p><p>Interconnected Ecosystem: Protocols like Uniswap, Aave, and MakerDAO can work together because they exist on the same blockchain (e.g., Ethereum) and follow common rules. A token created in one protocol can be used seamlessly in another.</p><h2 id="h-why-composability-matters-in-defi" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Why Composability Matters in Defi.</h2><p>Composability is a cornerstone of DeFi (Decentralized Finance) because it enables innovation, efficiency, and accessibility in ways that traditional financial systems cannot replicate. Here&apos;s why composability matters so much in DeFi and some protocols using composability to drive innovation in defi:</p><p><strong>1. Unlocking Endless Innovation</strong></p><p>Composability allows developers to combine existing protocols and tools to create new and complex financial applications. This &quot;Lego block&quot; approach accelerates innovation because:</p><p>Developers don’t need to build every feature from scratch. they can leverage the functionality of other protocols.</p><p>Projects can experiment with creative integrations, leading to groundbreaking products like yield aggregators, synthetic assets, and multi-layered financial strategies. example:</p><p><strong>Yearn Finance</strong></p><p>Yearn Finance leverages composability to optimize yields for users by integrating with multiple DeFi protocols. Here&apos;s how it works:</p><ul><li><p><strong>Deposits and Vaults:</strong> Users deposit assets (e.g., ETH, USDC, DAI) into Yearn’s Vaults. smart contracts that pool funds to optimize yields efficiently and reduce costs.</p></li><li><p><strong>Integration with Lending Protocols:</strong> Deposited funds are automatically deployed into protocols like Aave or Compound to earn interest. Borrowers provide over-collateralization, ensuring security.</p></li><li><p><strong>Automated Yield Strategies:</strong> Yearn dynamically reallocates funds across protocols to chase the highest returns. For example: If Aave offers better DAI yields than Compound, the strategy shifts assets. If lending yields drop, funds may be redirected to liquidity pools on DEXs like Curve or Balancer, earning trading fees and rewards.</p></li><li><p><strong>Liquidity Mining and Incentives:</strong> Yearn captures additional rewards by participating in liquidity mining programs, earning tokens like AAVE or CRV, which enhance overall returns.</p></li><li><p><strong>Auto-Compounding:</strong> Rewards from these protocols are reinvested automatically. e.g., CRV tokens earned from Curve are sold for DAI and redeposited, creating a compounding effect.</p></li></ul><p>Composability enables Yearn to interact seamlessly with lending platforms, DEXs, and reward systems, combining them to deliver a unified and optimized financial experience. It’s a perfect example of how protocols in DeFi can work together to create greater utility and value.</p><p><strong>2. Maximizing Capital Efficiency.</strong></p><p>Composability ensures that users can do more with their assets. Instead of locking funds in one protocol, users can leverage them across multiple platforms to multiply their utility for example:</p><ul><li><p><strong>Deposit into Aave:</strong> A user deposits $10,000 worth of ETH into Aave and receives aETH, which earns ~4% annual yield.</p></li><li><p><strong>Use aETH in MakerDAO:</strong> They use aETH as collateral to mint $6,000 of DAI at a 150% collateralization ratio.</p></li><li><p><strong>Leverage DAI for Yield:</strong> The DAI is staked in Curve’s stablecoin pool, earning 8% APY and additional CRV token rewards.</p></li></ul><p>This interconnected workflow demonstrates composability by enabling the same $10,000 of ETH to generate multiple layers of income without being sold. Each protocol enhances the other&apos;s utility, showcasing how composability drives innovation, efficiency, and higher returns in DeFi.</p><p><strong>3. Seamless User Experiences.</strong></p><p>By enabling protocols to interact effortlessly, composability simplifies complex financial workflows into streamlined user experiences.</p><p>In traditional finance, interacting with different financial services often involves Multiple logins across platforms (e.g., banks, brokerage accounts, loan providers), Complex paperwork or approval processes and Long waiting times for transactions to settle.</p><p>In contrast, DeFi platforms leverage composability to make these workflows faster and easier. Because blockchain protocols are interoperable and operate on the same network, users can execute multiple actions across protocols in a single transaction. This is particularly impactful for:</p><p><strong>Yield Farming:</strong> Users can deposit funds, stake liquidity provider (LP) tokens, and claim rewards without manually interacting with each platform. Portfolio Rebalancing: Assets can be moved or swapped across multiple protocols instantly to adjust for market conditions.</p><p>Example: Zapper and InstaDapp Both Zapper and InstaDapp are excellent examples of how composability powers streamlined user experiences in DeFi.</p><p><strong>Zapper</strong></p><p>Zapper acts as a dashboard that integrates multiple DeFi protocols under one interface. With Zapper, users can:</p><p>Deposit funds into liquidity pools on platforms like Uniswap or Curve without directly interacting with the underlying protocols, Claim rewards from yield farming and reinvest them automatically, then Swap tokens across protocols seamlessly.</p><p>For example, a user could take idle ETH in their wallet, deposit it into a liquidity pool, and start earning rewards all in just a few clicks, thanks to Zapper leveraging the composability of DeFi protocols.</p><p><strong>InstaDapp</strong></p><p>InstaDapp takes this a step further by enabling complex multi-protocol transactions. It acts as a middleware that integrates lending, trading, and staking protocols into a unified interface. Users can:</p><ul><li><p>Optimize loans by switching between platforms like Aave and Compound to get the best interest rates.</p></li><li><p>Leverage positions by borrowing against collateral and using the borrowed funds to trade or earn yield in other protocols.</p></li><li><p>Bridge assets between protocols or layers with minimal effort.</p></li></ul><p>For example, a user with collateral on Aave could:</p><p>Borrow stablecoins, Swap them for another asset on Uniswap and Deposit the new asset into Curve’s liquidity pool all in one transaction via InstaDapp.</p><p>The seamless user experiences provided by tools like Zapper and InstaDapp highlight the power of composability to democratize access to advanced financial tools. By abstracting away the complexities of multi-protocol interactions, these platforms enable users whether they&apos;re DeFi veterans or beginners to engage with the ecosystem efficiently and confidently. This simplicity and accessibility are critical to driving DeFi adoption and expanding its user base.</p><p><strong>4. Lowering Barriers to Entry.</strong></p><p>Open-source composability allows new projects to launch quickly by building on existing infrastructure:</p><p>Developers can integrate with established protocols to provide value without needing to create everything themselves. This creates a permissionless environment where innovation isn’t bottlenecked by gatekeepers. For instance, Balancer integrated with other DeFi projects like Compound and Aave to enhance its liquidity pools and enable advanced strategies.</p><p><strong>5. Building Advanced Financial Products.</strong></p><p>Composability is essential for creating financial products that rival or surpass those in traditional finance. Examples include:</p><p><strong>Flash loans:</strong> Instant, uncollateralized loans enabled by combining lending and trading protocols.</p><p><strong>Options and Derivatives:</strong> Protocols like Opyn rely on price feeds from oracles and tokenized collateral to enable decentralized options trading. These products are only possible because protocols can interact in real-time, trustlessly.</p><h2 id="h-challenges-of-composability-in-defi" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Challenges of Composability in DeFi.</strong></h2><p>While composability is transformative, it also introduces unique risks:</p><ul><li><p><strong>Smart Contract Vulnerabilities:</strong> Bugs in one protocol can have cascading effects if multiple systems depend on it.</p></li><li><p><strong>Systemic Risks:</strong> Highly interconnected systems mean that a failure in one protocol could impact others (e.g., the &quot;DeFi contagion&quot; risk during market downturns).</p></li><li><p><strong>Gas Costs:</strong> On networks with high transaction fees, interacting with multiple composable protocols can become prohibitively expensive.</p></li></ul><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Conclusion</strong></h2><p>Composability matters in DeFi because it supercharges the ecosystem’s potential for innovation, efficiency, and accessibility. It’s the reason why DeFi has grown so rapidly, with protocols working together to create a decentralized financial system that rivals and often outperforms traditional finance. While it comes with challenges, the benefits of composability far outweigh the risks, making it a foundational principle of the DeFi revolution.<br><br>Thanks.</p>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/aaa2cbbe973982a118d2a918e7aa5fcc16eb25b521d2f9a0a25ebb5dba7b1175.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Solidity to Aptos Move.]]></title>
            <link>https://paragraph.com/@4undraiser/solidity-to-aptos-move</link>
            <guid>mODsXzdblmjcZr9Ixfk4</guid>
            <pubDate>Mon, 22 Jul 2024 23:44:59 GMT</pubDate>
            <description><![CDATA[IntroductionSo if you&apos;re thinking about switching from solidity to move, let’s first bridge the knowledge gap that may exist for developers who are well-versed in Solidity and are considering or beginning to explore the Aptos blockchain and its Move programming language. The aim is to smooth the learning curve by drawing parallels between the familiar concepts in Solidity and how these are reimagined in Move…..Some HistorySolidity is a high-level language designed for implementing smart ...]]></description>
            <content:encoded><![CDATA[<h3 id="h-introduction" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Introduction</strong></h3><p>So if you&apos;re thinking about switching from solidity to move, let’s first bridge the knowledge gap that may exist for developers who are well-versed in Solidity and are considering or beginning to explore the Aptos blockchain and its Move programming language. The aim is to smooth the learning curve by drawing parallels between the familiar concepts in Solidity and how these are reimagined in Move…..</p><h4 id="h-some-history" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0"><strong>Some History</strong></h4><p>Solidity is a high-level language designed for implementing smart contracts on blockchains like Ethereum and other EVM Supported blockchains and L2s alike. It&apos;s statically typed and supports inheritance, libraries, and complex user-defined types, making it a robust tool for developing decentralized applications. As one of the pioneering languages in the blockchain space, Solidity has set a standard for smart contract programming with its accessibility and comprehensive documentation, helping to grow a vast ecosystem of developers and projects.Aptos Move, on the other hand, is the new kid on the block (<em>pun intended</em>) when it comes to the blockchain development arena. Originally developed for the facebook blockchain project, move has been adopted by a handful of blockchains including Aptos. Move is unique in its design, focusing on safety and predictability. It utilizes a resource-oriented approach, where resources are a first-class type, ensuring assets are tracked and handled safely. This approach prevents common bugs and vulnerabilities right from the language level, promoting more secure applications. Move&apos;s modular structure also facilitates code reusability and upgradability, which are crucial for maintaining large-scale decentralized applications.</p><h3 id="h-why-move" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Why Move?</strong></h3><p>The move programming language adopts a resource-oriented approach which is fundamentally distinct from the contract-oriented model seen in Ethereum&apos;s Solidity. This resource-centric paradigm in Move is designed to provide a more secure and predictable framework for handling digital assets. Each resource in Move is a unique entity that cannot be copied or implicitly discarded, ensuring that the programming model itself enforces asset safety and scarcity</p><h3 id="h-key-differences-from-solidity" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Key Differences from Solidity</strong></h3><h4 id="h-syntax-and-type-system" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0"><strong>Syntax and Type System</strong></h4><p>Move and Solidity present distinctly different syntax and type systems that cater to their underlying design philosophies. Move, inspired by Rust, features a more rigorous type system, including generics and fine-grained visibility specifiers for functions and types. In contrast, Solidity&apos;s type system is more akin to JavaScript, focusing on accessibility for a wide range of programmers. Move enforces static typing, which helps catch errors at compile time, thereby enhancing the security and reliability of the code before it ever runs on the blockchain.</p><h4 id="h-resources-and-linear-types" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0"><strong>Resources and Linear Types</strong></h4><p>One of the main features of Move is its treatment of resources linear types that represent assets and must be treated with care to ensure they are neither duplicated nor accidentally destroyed. In Move, resources can only exist in one location at a time, must be explicitly moved between storage locations, and cannot be copied. This contrasts with Solidity, where tokens and other assets are typically represented by integers, which can be copied and reassigned freely, sometimes leading to bugs or security vulnerabilities. Move&apos;s resource model enforces a high degree of integrity and traceability in the management of digital assets.</p><h4 id="h-modules-vs-contracts" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0"><strong>Modules vs. Contracts</strong></h4><p>Move does not use contracts per se; instead, it organizes code into modules. A module in Move is a collection of associated functions, types, and data (resources) that are deployed together as a single unit. This structure supports better encapsulation and code reuse, as modules can import other modules and use their public functions and types. In contrast, Solidity&apos;s contracts are more self-contained, each deployed independently even if they share common functionalities with other contracts. This fundamental difference influences not only code organization and reuse but also impacts how developers approach building applications on their respective blockchains.</p><h3 id="h-example-implementations" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Example Implementations</strong></h3><p>Let’s see some code examples.</p><p><strong>1. Token Creation and ManagementSolidity</strong></p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Token {
    uint256 public totalSupply;
    mapping(address =&gt; uint256) public balances;

    event Transfer(address indexed from, address indexed to, uint256 value);

    constructor(uint256 initialSupply) {
        balances[msg.sender] = initialSupply;
        totalSupply = initialSupply;
    }

    function transfer(address to, uint256 amount) public {
        require(balances[msg.sender] &gt;= amount, &quot;Insufficient balance&quot;);
        balances[msg.sender] -= amount;
        balances[to] += amount;
        emit Transfer(msg.sender, to, amount);
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Token</span> </span>{
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> totalSupply;
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> balances;

    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">Transfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> <span class="hljs-keyword">from</span>, <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> to, <span class="hljs-keyword">uint256</span> value</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> initialSupply</span>) </span>{
        balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> initialSupply;
        totalSupply <span class="hljs-operator">=</span> initialSupply;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> to, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">></span><span class="hljs-operator">=</span> amount, <span class="hljs-string">"Insufficient balance"</span>);
        balances[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">-</span><span class="hljs-operator">=</span> amount;
        balances[to] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> amount;
        <span class="hljs-keyword">emit</span> Transfer(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, to, amount);
    }
}
</code></pre><p><strong>Implicit Trust Model:</strong> Solidity assumes that the contract code will handle state management properly. It provides mechanisms like modifiers and requires statements to enforce rules but relies heavily on the developer to implement these checks correctly. <strong>State Management:</strong> Uses a mapping for balances which is a built-in data structure ideal for key-value storage, straightforward in terms of addressing individual account balances.<strong>Events:</strong> Solidity utilizes events (like Transfer) to log transactions, which are essential for off-chain applications to track on-chain activities.</p><p><code>msg.sender</code> is used to identify the caller of a function.</p><p><strong>Move</strong></p><pre data-type="codeBlock" text="address module_address;

module module_address::Token {
    struct Token&lt;CoinType&gt; has store {
        total_supply: u64,
        balances: vector&lt;u64&gt;,  // Assume CoinType is indexed by an address vector
    }

    public fun create&lt;CoinType&gt;(initial_supply: u64): Token&lt;CoinType&gt; {
        let balances = vector::empty&lt;u64&gt;();
        vector::push_back(&amp;mut balances, initial_supply);
        Token&lt;CoinType&gt; { total_supply: initial_supply, balances }
    }

    public fun transfer&lt;CoinType&gt;(token: &amp;mut Token&lt;CoinType&gt;, from_index: u64, to_index: u64, amount: u64) {
        let from_balance = vector::borrow_mut(&amp;mut token.balances, from_index);
        let to_balance = vector::borrow_mut(&amp;mut token.balances, to_index);
        *from_balance = from_balance - amount;
        *to_balance = to_balance + amount;
    }
}
"><code><span class="hljs-keyword">address</span> module_address;

module module_address::Token {
    <span class="hljs-keyword">struct</span> <span class="hljs-title">Token</span>&#x3C;<span class="hljs-title">CoinType</span>> <span class="hljs-title">has</span> <span class="hljs-title">store</span> {
        total_supply: u64,
        balances: vector<span class="hljs-operator">&#x3C;</span>u64<span class="hljs-operator">></span>,  <span class="hljs-comment">// Assume CoinType is indexed by an address vector</span>
    }

    <span class="hljs-keyword">public</span> fun create<span class="hljs-operator">&#x3C;</span>CoinType<span class="hljs-operator">></span>(initial_supply: u64): Token<span class="hljs-operator">&#x3C;</span>CoinType<span class="hljs-operator">></span> {
        let balances <span class="hljs-operator">=</span> vector::empty<span class="hljs-operator">&#x3C;</span>u64<span class="hljs-operator">></span>();
        vector::push_back(<span class="hljs-operator">&#x26;</span>mut balances, initial_supply);
        Token<span class="hljs-operator">&#x3C;</span>CoinType<span class="hljs-operator">></span> { total_supply: initial_supply, balances }
    }

    <span class="hljs-keyword">public</span> fun transfer<span class="hljs-operator">&#x3C;</span>CoinType<span class="hljs-operator">></span>(token: <span class="hljs-operator">&#x26;</span>mut Token<span class="hljs-operator">&#x3C;</span>CoinType<span class="hljs-operator">></span>, from_index: u64, to_index: u64, amount: u64) {
        let from_balance <span class="hljs-operator">=</span> vector::borrow_mut(<span class="hljs-operator">&#x26;</span>mut token.balances, from_index);
        let to_balance <span class="hljs-operator">=</span> vector::borrow_mut(<span class="hljs-operator">&#x26;</span>mut token.balances, to_index);
        <span class="hljs-operator">*</span>from_balance <span class="hljs-operator">=</span> from_balance <span class="hljs-operator">-</span> amount;
        <span class="hljs-operator">*</span>to_balance <span class="hljs-operator">=</span> to_balance <span class="hljs-operator">+</span> amount;
    }
}
</code></pre><p><strong>Explicit Resource Management:</strong> Move’s resource-centric model means that tokens or any assets are treated as tangible resources. This ensures that tokens can&apos;t be duplicated or lost through bugs, as each token is a unique entity that must be explicitly managed.</p><p><strong>Advanced Type Safety:</strong> Move employs generics and stronger type systems, including linear types, to ensure that resources are used correctly. This can prevent many common bugs in contract development.</p><p><strong>Vector for Balances:</strong> Instead of a direct mapping, Move uses a vector to store balances, tied to indices. This introduces complexity as indices must correlate to addresses, requiring additional management overhead.<code>&amp;signer</code> reference is used to identify the caller of a function, which provides a more secure way to represent the transaction authorizer.</p><p><strong>2. Access Control.Solidity</strong></p><pre data-type="codeBlock" text="contract Owned {
    address private owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, &quot;Not the owner&quot;);
        _;
    }

    function changeOwner(address newOwner) public onlyOwner {
        owner = newOwner;
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Owned</span> </span>{
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">private</span> owner;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        owner <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">onlyOwner</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> owner, <span class="hljs-string">"Not the owner"</span>);
        <span class="hljs-keyword">_</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">changeOwner</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> newOwner</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        owner <span class="hljs-operator">=</span> newOwner;
    }
}
</code></pre><p><strong>Modifiers for Reusability:</strong> Solidity uses modifiers like onlyOwner to create reusable checks that simplify function enforcement policies, making the code more modular and readable.</p><p><strong>Direct Address Comparisons:</strong> Ownership is verified through direct address comparisons, a straightforward and efficient method but reliant on proper handling to avoid security issues.<strong>Move</strong></p><pre data-type="codeBlock" text="module NamedAddress::Owned {
    struct Owner has key {
        owner_address: address,
    }

    public fun initialize(account: &amp;signer) {
        let owner = Owner { owner_address: Signer::address_of(account) };
        move_to(account, owner);
    }

    public fun change_owner(account: &amp;signer, new_owner: address) acquires Owner {
        let owner = borrow_global_mut&lt;Owner&gt;(Signer::address_of(account));
        owner.owner_address = new_owner;
    }
}
"><code>module NamedAddress::Owned {
    struct Owner has key {
        owner_address: address,
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">initialize</span><span class="hljs-params">(account: &#x26;<span class="hljs-type">signer</span>)</span></span> {
        let owner = Owner { owner_address: Signer::address_of(account) };
        move_to(account, owner);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">change_owner</span><span class="hljs-params">(account: &#x26;<span class="hljs-type">signer</span>, new_owner: <span class="hljs-type">address</span>)</span></span> acquires Owner {
        let owner = borrow_global_mut&#x3C;Owner>(Signer::address_of(account));
        owner.owner_address = new_owner;
    }
}
</code></pre><p><strong>Resource-Based Access Control</strong>: Ownership is represented as a resource, making unauthorized access or accidental changes to ownership more difficult. This encapsulation is a core part of ensuring security in Move.</p><p><strong>Signer as a Security Feature</strong>: Move uses signer references which directly link to the transaction sender, a built-in mechanism to ensure actions are authorized by the right entity.</p><h3 id="h-conclusion" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Conclusion</strong></h3><p>Switching from Solidity to Move can significantly enhance safety and security in blockchain development. Move&apos;s language design emphasizes resource-oriented programming, where digital assets are treated as unique resources that cannot be duplicated or lost unintentionally. This approach, along with a linear type system, reduces common bugs like double spending and reentrancy, prevalent in Solidity. Move also supports built-in formal verification tools, like the Move Prover, which allow developers to mathematically prove contract reliability, providing a higher assurance level, especially critical in financial applications.</p><p>Furthermore, Move&apos;s modular system facilitates code reusability and secure encapsulation, streamlining on-chain contract upgrades—a notable challenge in Solidity. As Move is utilized in newer blockchain platforms like Aptos, it brings innovations in scalability and throughput, appealing to projects seeking cutting-edge blockchain features.</p><h3 id="h-referencesfurther-learning" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">References/Further Learning</h3><p>These resources will help your migration.</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://aptos.dev/en/build/get-started/ethereum-cheatsheet">https://aptos.dev/en/build/get-started/ethereum-cheatsheet</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/move-tutorial">https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/move-tutorial</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://aptos.dev/en/build/smart-contracts/why-move">https://aptos.dev/en/build/smart-contracts/why-move</a></p><p>THANK YOU!!!</p>]]></content:encoded>
            <author>4undraiser@newsletter.paragraph.com (4undRaiser)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/7539e197be57292a6b789abfb011228f6170d871ca762ac1e7c0c90cd998fecb.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>