
{"id":48330,"date":"2025-02-27T17:09:30","date_gmt":"2025-02-27T17:09:30","guid":{"rendered":"https:\/\/mycryptomania.com\/?p=48330"},"modified":"2025-02-27T17:09:30","modified_gmt":"2025-02-27T17:09:30","slug":"how-to-deploy-and-interact-with-an-erc-721-smart-contract-on-abstract-using-hardhat-and","status":"publish","type":"post","link":"https:\/\/mycryptomania.com\/?p=48330","title":{"rendered":"How to Deploy and Interact with an ERC-721 Smart Contract on Abstract Using Hardhat and\u2026"},"content":{"rendered":"<h3>How to Deploy and Interact with an ERC-721 Smart Contract on Abstract Using Hardhat and OpenZeppelin<\/h3>\n<p>In this article, we will cover how to compile, deploy, and interact with an ERC-721 smart contract built using Solidity and the OpenZeppelin library on the Abstract Sepolia testnet. We will be using Hardhat, zkSync-Ethers, and TypeScript to create out\u00a0project.<\/p>\n<h3>What is Abstract?<\/h3>\n<p>Abstract is a Layer 2 (L2) network built on top of Ethereum using the <strong>ZK Stack<\/strong>, a modular framework for creating ZK-powered blockchains. Like any L2, its goal is to scale Ethereum\u2019s throughput while significantly reducing gas\u00a0fees.<\/p>\n<p>Abstract is <strong>EVM-compatible<\/strong>, supporting Solidity smart contracts and popular Ethereum development frameworks like <strong>Hardhat<\/strong> and <strong>Foundry<\/strong>. It also works seamlessly with widely used libraries such as <strong>Viem<\/strong> and <strong>Ethers.js<\/strong>.<\/p>\n<p>Most existing Ethereum smart contracts will function on Abstract with minimal modifications, making it easy for developers to port applications with little friction. However Abstract smart contracts use different bytecode than the Ethereum Virtual Machine\u00a0(EVM).<\/p>\n<p>To learn more about Abstract check out\u00a0<a href=\"https:\/\/docs.abs.xyz\/what-is-abstract\">this<\/a>.<\/p>\n<h3>What is an\u00a0ERC-721?<\/h3>\n<p><strong>ERC-721<\/strong> is an Ethereum token standard for <strong>non-fungible tokens (NFTs)<\/strong>, meaning each token is <strong>unique<\/strong> and <strong>cannot be replaced<\/strong>. It enables ownership, transfer, and metadata management for digital assets like art, collectibles, and in-game\u00a0items.<\/p>\n<p>If are new to ERC-721 smart contracts check out <a href=\"https:\/\/medium.com\/coinmonks\/unlocking-nfts-a-deep-dive-into-erc-721-on-ethereum-75b5fd492741\">th<\/a>ese two articles:<\/p>\n<p><a href=\"https:\/\/medium.com\/coinmonks\/unlocking-nfts-a-deep-dive-into-erc-721-on-ethereum-75b5fd492741\">NFT Mastery: Understanding ERC-721 on Ethereum for\u00a0Starter<\/a>s<a href=\"https:\/\/medium.com\/coinmonks\/how-to-create-erc-721-nfts-on-ethereum-with-openzeppelin-a-step-by-step-tutorial-47b252843dd9\">How to Create ERC-721 NFTs on Ethereum with OpenZeppelin: A Step-by-Step Tutorial<\/a><\/p>\n<h3>Hardhat Project Set\u00a0Up<\/h3>\n<h4>Prerequisites<\/h4>\n<p>WSL2 (recommended for Windows\u00a0users)Node.js 22<\/p>\n<p>To set Node 22 in your machine you can use\u00a0nvm:<\/p>\n<p>nvm install 22<br \/>nvm use 22 <br \/>nvm alias default 22 #global<\/p>\n<p>Create a new directory and navigate into\u00a0it:<\/p>\n<p>mkdir abstract_nft &amp;&amp; cd abstract_nft<\/p>\n<p>Create a new Hardhat\u00a0project:<\/p>\n<p>npx hardhat init<\/p>\n<p>Recommended setup:<\/p>\n<p>\u2714 What do you want to do? \u00b7 Create a TypeScript project<br \/>\u2714 Hardhat project root: \u00b7 \/path\/to\/my-abstract-project<br \/>\u2714 Do you want to add a .gitignore? (Y\/n) \u00b7 y<br \/>\u2714 Do you &#8230; install &#8230; dependencies with npm &#8230; \u00b7 y<\/p>\n<p>Install the required dependencies to compile, deploy and interact with smart contracts on Abstract:<\/p>\n<p>npm install -D @matterlabs\/hardhat-zksync zksync-ethers@6 ethers@6<\/p>\n<p>Install also dotenv to handle the environment variables:<\/p>\n<p>npm install -D dotenv<\/p>\n<p>Create a\u00a0.env file in the root directory to store your account&#8217;s private key. You can use any Ethereum-compatible wallet, such as MetaMask or Coinbase. Write:<\/p>\n<p>PRIVATE_KEY=your_private_key<\/p>\n<p>Now modify the file hardhat.config.ts\u00a0:<\/p>\n<p>import { HardhatUserConfig } from &#8220;hardhat\/config&#8221;;<br \/>import &#8220;@matterlabs\/hardhat-zksync&#8221;;<br \/>import &#8216;dotenv\/config&#8217;;<\/p>\n<p>const config: HardhatUserConfig = {<br \/>  zksolc: {<br \/>    version: &#8220;latest&#8221;,<br \/>    settings: {<br \/>      \/\/ Note: This must be true to call NonceHolder &amp; ContractDeployer system contracts<br \/>      enableEraVMExtensions: false,<br \/>    },<br \/>  },<br \/>  defaultNetwork: &#8220;abstractTestnet&#8221;,<br \/>  networks: {<br \/>    abstractTestnet: {<br \/>      url: &#8220;https:\/\/api.testnet.abs.xyz&#8221;,<br \/>      ethNetwork: &#8220;sepolia&#8221;,<br \/>      zksync: true,<br \/>      chainId: 11124,<br \/>      accounts: [process.env.PRIVATE_KEY!] <br \/>    },<br \/>    abstractMainnet: {<br \/>      url: &#8220;https:\/\/api.mainnet.abs.xyz&#8221;,<br \/>      ethNetwork: &#8220;mainnet&#8221;,<br \/>      zksync: true,<br \/>      chainId: 2741,<br \/>      accounts: [process.env.PRIVATE_KEY!]<br \/>    },<br \/>  },<br \/>  etherscan: {<br \/>    apiKey: {<br \/>      abstractTestnet: &#8220;TACK2D1RGYX9U7MC31SZWWQ7FCWRYQ96AD&#8221;,<br \/>      abstractMainnet: &#8220;IEYKU3EEM5XCD76N7Y7HF9HG7M9ARZ2H4A&#8221;,<br \/>    },<br \/>    customChains: [<br \/>      {<br \/>        network: &#8220;abstractTestnet&#8221;,<br \/>        chainId: 11124,<br \/>        urls: {<br \/>          apiURL: &#8220;https:\/\/api-sepolia.abscan.org\/api&#8221;,<br \/>          browserURL: &#8220;https:\/\/sepolia.abscan.org\/&#8221;,<br \/>        },<br \/>      },<br \/>      {<br \/>        network: &#8220;abstractMainnet&#8221;,<br \/>        chainId: 2741,<br \/>        urls: {<br \/>          apiURL: &#8220;https:\/\/api.abscan.org\/api&#8221;,<br \/>          browserURL: &#8220;https:\/\/abscan.org\/&#8221;,<br \/>        },<br \/>      },<br \/>    ],<br \/>  },<br \/>  solidity: {<br \/>    version: &#8220;0.8.24&#8221;,<br \/>  },<br \/>};<\/p>\n<p>export default config;<\/p>\n<p>Let\u2019s install the OpenZeppelin contracts:<\/p>\n<p>npm install -D @openzeppelin\/contracts<\/p>\n<p>This is the ERC 721 smart contract that I have put in the filecontracts\/CarsNft.sol\u00a0:<\/p>\n<p>\/\/ SPDX-License-Identifier: MIT<br \/>\/\/ Compatible with OpenZeppelin Contracts ^5.0.0<br \/>pragma solidity ^0.8.24;<\/p>\n<p>import {ERC721} from &#8220;@openzeppelin\/contracts\/token\/ERC721\/ERC721.sol&#8221;;<br \/>import {ERC721Enumerable} from &#8220;@openzeppelin\/contracts\/token\/ERC721\/extensions\/ERC721Enumerable.sol&#8221;;<br \/>import {ERC721URIStorage} from &#8220;@openzeppelin\/contracts\/token\/ERC721\/extensions\/ERC721URIStorage.sol&#8221;;<\/p>\n<p>contract CarsNft is ERC721, ERC721Enumerable, ERC721URIStorage {<br \/>    uint256 private _nextTokenId;<\/p>\n<p>    constructor()<br \/>        ERC721(&#8220;CarsNft&#8221;, &#8220;CARS&#8221;)<br \/>    {}<\/p>\n<p>    function safeMint(address to, string memory uri) public {<br \/>        uint256 tokenId = _nextTokenId++;<br \/>        _safeMint(to, tokenId);<br \/>        _setTokenURI(tokenId, uri);<br \/>    }<\/p>\n<p>    \/\/ The following functions are overrides required by Solidity.<\/p>\n<p>    function _update(address to, uint256 tokenId, address auth)<br \/>        internal<br \/>        override(ERC721, ERC721Enumerable)<br \/>        returns (address)<br \/>    {<br \/>        return super._update(to, tokenId, auth);<br \/>    }<\/p>\n<p>    function _increaseBalance(address account, uint128 value)<br \/>        internal<br \/>        override(ERC721, ERC721Enumerable)<br \/>    {<br \/>        super._increaseBalance(account, value);<br \/>    }<\/p>\n<p>    function tokenURI(uint256 tokenId)<br \/>        public<br \/>        view<br \/>        override(ERC721, ERC721URIStorage)<br \/>        returns (string memory)<br \/>    {<br \/>        return super.tokenURI(tokenId);<br \/>    }<\/p>\n<p>    function supportsInterface(bytes4 interfaceId)<br \/>        public<br \/>        view<br \/>        override(ERC721, ERC721Enumerable, ERC721URIStorage)<br \/>        returns (bool)<br \/>    {<br \/>        return super.supportsInterface(interfaceId);<br \/>    }<br \/>}<\/p>\n<p>As you can see it is a very simple ERC 721 created with the <a href=\"https:\/\/wizard.openzeppelin.com\/#erc721\">OpenZeppelin Wizard<\/a>.<\/p>\n<p>Becore compiling it, let\u2019s clear any existing artifacts:<\/p>\n<p>npx hardhat clean<\/p>\n<p>Now we can compile our smart contract with zksolc compiler:<\/p>\n<p>npx hardhat compile &#8211;network abstractTestnet<\/p>\n<p>Before deploying our smart contracts, you need some test Ether in your wallet. You can either get <strong>Sepolia ETH<\/strong> from a faucet and bridge it using the <strong>Arbitrum bridge<\/strong>, or use an <strong>Arbitrum faucet<\/strong> to obtain Arbitrum test Ether directly.<\/p>\n<p><a href=\"https:\/\/docs.abs.xyz\/tooling\/faucets\">Here <\/a>you can find the Arbitrum faucets\u00a0page.<\/p>\n<p><a href=\"https:\/\/native-bridge.abs.xyz\/bridge\/?network=abstract-testnet\">Here<\/a> you can find the testnet Arbitrum\u00a0bridge.<\/p>\n<p>Before doing that make sure to add the Arbitrum testnet to your wallet by using the informations provided at <a href=\"https:\/\/docs.abs.xyz\/connect-to-abstract\">this\u00a0page<\/a>.<\/p>\n<p>In order to deploy our smart contract we need to create a deployment script at \/deploy\/deploy.ts:<\/p>\n<p>import { Wallet } from &#8220;zksync-ethers&#8221;;<br \/>import { HardhatRuntimeEnvironment } from &#8220;hardhat\/types&#8221;;<br \/>import { Deployer } from &#8220;@matterlabs\/hardhat-zksync&#8221;;<br \/>import &#8220;dotenv\/config&#8221;;<\/p>\n<p>\/\/ An example of a deploy script that will deploy and call a simple contract.<br \/>export default async function (hre: HardhatRuntimeEnvironment) {<br \/>  console.log(`Running deploy script`);<\/p>\n<p>  \/\/ Initialize the wallet using your private key.<br \/>  const wallet = new Wallet(process.env.PRIVATE_KEY!);<\/p>\n<p>  \/\/ Create deployer object and load the artifact of the contract we want to deploy.<br \/>  const deployer = new Deployer(hre, wallet);<br \/>  \/\/ Load contract<br \/>  const artifact = await deployer.loadArtifact(&#8220;CarsNft&#8221;);<\/p>\n<p>  \/\/ Deploy this contract. The returned object will be of a `Contract` type,<br \/>  \/\/ similar to the ones in `ethers`.<br \/>  const tokenContract = await deployer.deploy(artifact);<\/p>\n<p>  console.log(<br \/>    `${<br \/>      artifact.contractName<br \/>    } was deployed to ${await tokenContract.getAddress()}`<br \/>  );<br \/>}<\/p>\n<p>To execute this file you can\u00a0run:<\/p>\n<p>npx hardhat deploy-zksync &#8211;script deploy.ts &#8211;network abstractTestnet<\/p>\n<p>It will log something like:<\/p>\n<p>Testnet<br \/>Running deploy script<br \/>CarsNft was deployed to 0x32d5014AF5387002331d2AaC10fA29a6B5f3E943<\/p>\n<p>And you can check the transaction on <a href=\"https:\/\/explorer.testnet.abs.xyz\/address\/0x32d5014AF5387002331d2AaC10fA29a6B5f3E943\">Arbitrum\u00a0scan<\/a>.<\/p>\n<p>To deploy the code we are using zksync-ethers and you can learn more about it\u00a0<a href=\"https:\/\/docs.zksync.io\/zksync-era\/tooling\/hardhat\/guides\/getting-started\">here<\/a>.<\/p>\n<p>We can also verify our smart contract on the block explorer by\u00a0running:<\/p>\n<p>npx hardhat verify &#8211;network abstractTestnet 0x32d5014AF5387002331d2AaC10fA29a6B5f3E943<\/p>\n<p>You can check the verified code of the previous smart contract\u00a0<a href=\"https:\/\/sepolia.abscan.org\/address\/0x32d5014AF5387002331d2AaC10fA29a6B5f3E943#code\">here<\/a>.<\/p>\n<p>To interact with the smart contract I have created the file scripts\/interact.ts:<\/p>\n<p>import hre from &#8220;hardhat&#8221;;<\/p>\n<p>const CONTRACT_ADDRESS = &#8220;0x32d5014AF5387002331d2AaC10fA29a6B5f3E943&#8221;;<br \/>const MY_ADDRESS = &#8220;0x20c6F9006d563240031A1388f4f25726029a6368&#8221;;<\/p>\n<p>if (!CONTRACT_ADDRESS)<br \/>  throw &#8220;\u26d4\ufe0f Provide address of the contract to interact with!&#8221;;<\/p>\n<p>async function main() {<br \/>  console.log(`Running script to interact with contract ${CONTRACT_ADDRESS}`);<\/p>\n<p>  \/\/ Get the first signer<br \/>  const [signer] = await hre.ethers.getSigners();<\/p>\n<p>  \/\/ Get the contract factory and deploy<br \/>  const CarsNft = await hre.ethers.getContractFactory(&#8220;CarsNft&#8221;);<br \/>  const carsNft = await CarsNft.connect(signer).attach(CONTRACT_ADDRESS);<\/p>\n<p>  \/\/ Run contract read function name<br \/>  const name = await carsNft.name();<br \/>  console.log(`Current name is: ${name}`);<\/p>\n<p>  \/\/ Run contract write function safeMint<br \/>  const transaction = await carsNft.safeMint(MY_ADDRESS, &#8220;URI of car n.1&#8221;);<br \/>  console.log(`Transaction hash of safeMint : ${transaction.hash}`);<br \/>  await transaction.wait();<\/p>\n<p>  \/\/ Run contract read function balanceOf<br \/>  const balance = await carsNft.balanceOf(MY_ADDRESS);<br \/>  console.log(`Balance of the address ${MY_ADDRESS} is: ${balance.toString()}`);<\/p>\n<p>  \/\/ Run contract read function tokenURI<br \/>  const tokenURI = await carsNft.tokenURI(0);<br \/>  console.log(`Token URI of token 0 is: ${tokenURI}`);<br \/>}<\/p>\n<p>main()<br \/>  .then(() =&gt; process.exit(0))<br \/>  .catch((error) =&gt; {<br \/>    console.error(error);<br \/>    process.exit(1);<br \/>  });<\/p>\n<p>To run the previous script you can\u00a0execute:<\/p>\n<p>npx hardhat run scripts\/interact.ts <\/p>\n<p>This should be the\u00a0result:<\/p>\n<p>Running script to interact with contract 0x32d5014AF5387002331d2AaC10fA29a6B5f3E943<br \/>Current name is: CarsNft<br \/>Transaction hash of safeMint : 0x766938205ed335c769124d99ae917f577b3c259815a629b246589e08ded7eb83<br \/>Balance of the address 0x20c6F9006d563240031A1388f4f25726029a6368 is: 1<br \/>Token URI of token 0 is: URI of car n.1<\/p>\n<p>In this two pages you can learn\u00a0more:<\/p>\n<p><a href=\"https:\/\/docs.zksync.io\/zksync-era\/tooling\/hardhat\/guides\/getting-started\">https:\/\/docs.zksync.io\/zksync-era\/tooling\/hardhat\/guides\/getting-started<\/a><a href=\"https:\/\/docs.abs.xyz\/build-on-abstract\/smart-contracts\/hardhat#recommended-hardhat-setup\">https:\/\/docs.abs.xyz\/build-on-abstract\/smart-contracts\/hardhat#recommended-hardhat-setup<\/a><\/p>\n<h3>Github Repository<\/h3>\n<p>The Github repository of this project can be found\u00a0<a href=\"https:\/\/github.com\/RosarioB\/abstract_erc721\">here<\/a>.<\/p>\n<h3>Sources<\/h3>\n<p><a href=\"https:\/\/docs.zksync.io\/zk-stack\">https:\/\/docs.zksync.io\/zk-stack<\/a><a href=\"https:\/\/docs.abs.xyz\/what-is-abstract\">https:\/\/docs.abs.xyz\/what-is-abstract<\/a><a href=\"https:\/\/docs.abs.xyz\/build-on-abstract\/smart-contracts\/hardhat#recommended-hardhat-setup\">https:\/\/docs.abs.xyz\/build-on-abstract\/smart-contracts\/hardhat#recommended-hardhat-setup<\/a><a href=\"https:\/\/docs.zksync.io\/zksync-era\/tooling\/hardhat\/guides\/getting-started\">https:\/\/docs.zksync.io\/zksync-era\/tooling\/hardhat\/guides\/getting-started<\/a><\/p>\n<p><a href=\"https:\/\/medium.com\/coinmonks\/how-to-deploy-and-interact-with-an-erc-721-smart-contract-on-abstract-using-hardhat-and-0e3d14c7cd6a\">How to Deploy and Interact with an ERC-721 Smart Contract on Abstract Using Hardhat and\u2026<\/a> was originally published in <a href=\"https:\/\/medium.com\/coinmonks\">Coinmonks<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>","protected":false},"excerpt":{"rendered":"<p>How to Deploy and Interact with an ERC-721 Smart Contract on Abstract Using Hardhat and OpenZeppelin In this article, we will cover how to compile, deploy, and interact with an ERC-721 smart contract built using Solidity and the OpenZeppelin library on the Abstract Sepolia testnet. We will be using Hardhat, zkSync-Ethers, and TypeScript to create [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-48330","post","type-post","status-publish","format-standard","hentry","category-interesting"],"_links":{"self":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/48330"}],"collection":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=48330"}],"version-history":[{"count":0,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/48330\/revisions"}],"wp:attachment":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=48330"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=48330"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=48330"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}