
{"id":46268,"date":"2025-02-20T13:23:15","date_gmt":"2025-02-20T13:23:15","guid":{"rendered":"https:\/\/mycryptomania.com\/?p=46268"},"modified":"2025-02-20T13:23:15","modified_gmt":"2025-02-20T13:23:15","slug":"how-to-create-and-deploy-an-erc721-smart-contract-on-starknet-with-starknet-foundry-and-starknet-js","status":"publish","type":"post","link":"https:\/\/mycryptomania.com\/?p=46268","title":{"rendered":"How to Create and Deploy an ERC721 Smart Contract on Starknet with Starknet Foundry and Starknet.js"},"content":{"rendered":"<p>This guide covers how to create, deploy, and interact with an ERC721 smart contract on Starknet using OpenZeppelin, Starknet Foundry, and Starknet.js. You\u2019ll learn how to compile, declare, and deploy the contract, then mint NFTs and manage them efficiently on Layer\u00a02.<\/p>\n<h3>Table Of\u00a0Contents<\/h3>\n<p>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#5404\">Table Of Contents<\/a><br \/>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#fb50\">Introduction to StarkNet and Cairo<\/a><br \/>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#66b6\">Starknet Foundry Set Up<\/a><br \/> \u2218 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#2e6f\">Importing the Braavos Account<\/a><br \/> \u2218 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#bf0e\">Defining the Profiles<\/a><br \/> \u2218 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#6e9c\">Install The OpenZeppelin Library<\/a><br \/>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#ccbf\">StarkNet Faucet<\/a><br \/>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#31d7\">Building The Contract<\/a><br \/> \u2218 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#e02a\">Declaring The Contract<\/a><br \/>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#40ce\">Deploying the Contract<\/a><br \/>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#f937\">Starknet.js<\/a><br \/>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#084c\">Github Repository<\/a><br \/>\u00b7 <a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#a626\">Conclusion<\/a><br \/>\u00b7\u00a0<a href=\"https:\/\/medium.com\/coinmonks?source=rss----721b17443fd5---4#b7af\">Sources<\/a><\/p>\n<h3>Introduction to StarkNet and\u00a0Cairo<\/h3>\n<p>Starknet is a<strong> <\/strong>Layer 2 (L2) scaling solution for Ethereum that uses<strong> <\/strong>STARK (Scalable Transparent Argument of Knowledge) proofs to batch multiple transactions off-chain and submit a single proof to Ethereum. This drastically reduces gas fees and increases throughput, while maintaining Ethereum\u2019s security and decentralization.<\/p>\n<p>Unlike Solidity, which is used for Ethereum smart contracts, Cairo is the native smart-contract language for Starknet. <a href=\"https:\/\/www.cairo-lang.org\/\">Cairo<\/a> allows to write provable program with STARK validity proofs, without a deep knowledge of the complex cryptographic concepts underneath.<\/p>\n<p>Check out <a href=\"https:\/\/www.starknet.io\/blog\/validity-rollups\/\">this guide<\/a> for further exploration on Starknet.<\/p>\n<h3>Starknet Foundry Set\u00a0Up<\/h3>\n<p>To compile and deploy our smart contract, we will use <strong>Starknet Foundry<\/strong> in this article. For installation instructions, I recommend checking out <a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/getting-started\/installation.html\">this\u00a0guide<\/a>.<\/p>\n<p>To start a new project called label_nft we will\u00a0execute:<\/p>\n<p>snforge new label_nft<\/p>\n<h4>Importing the Braavos\u00a0Account<\/h4>\n<p>For this project I have imported my <a href=\"https:\/\/braavos.app\/\">Braavos <\/a>account using sncast account importboth for Sepolia testnet and\u00a0mainnet:<\/p>\n<p>sncast <br \/>   account import <br \/>   &#8211;url https:\/\/starknet-sepolia.infura.io\/v3\/**** <br \/>   &#8211;name account_braavos <br \/>   &#8211;address 0xyjozq3nlq7f1fma6d2oz9rd8xa3np961igz7xpg3roxw37c8fl7nzfsz4avkeag4 <br \/>   &#8211;private-key 0x7a90aE1b2904F6F6787b0bcB6E4c8D08aF1e4fca5a33b96c8d8e7b8a6f78b60c <br \/>   &#8211;type braavos<\/p>\n<p>These accounts are saved in the file starknet_open_zeppelin_accounts.json at the path: ~\/.starknet_accounts\/starknet_open_zeppelin_accounts.json<\/p>\n<p>The file starknet_open_zeppelin_accounts.json should be similar to\u00a0this:<\/p>\n<p>{<br \/>  &#8220;alpha-mainnet&#8221;: {<br \/>    &#8220;account_braavos_mainnet&#8221;: {<br \/>      &#8220;address&#8221;: &#8220;0xyjozq3nlq7f1fma6d2oz9rd8xa3np961igz7xpg3roxw37c8fl7nzfsz4avkeag4&#8221;,<br \/>      &#8220;class_hash&#8221;: &#8220;0xba780ad3e82a6756c2e892a252d527261177324fad6b0622f13aa7354433bef&#8221;,<br \/>      &#8220;deployed&#8221;: true,<br \/>      &#8220;legacy&#8221;: false,<br \/>      &#8220;private_key&#8221;: &#8220;0x7a90aE1b2904F6F6787b0bcB6E4c8D08aF1e4fca5a33b96c8d8e7b8a6f78b60c&#8221;,<br \/>      &#8220;public_key&#8221;: &#8220;0xyjozq3nlq7f1fma6d2oz9rd8xa3np961igz7xpg3roxw37c8fl7nzfsz4avkeag&#8221;,<br \/>      &#8220;type&#8221;: &#8220;braavos&#8221;<br \/>    }<br \/>  },<br \/>  &#8220;alpha-sepolia&#8221;: {<br \/>    &#8220;account_braavos&#8221;: {<br \/>      &#8220;address&#8221;: &#8220;0xyjozq3nlq7f1fma6d2oz9rd8xa3np961igz7xpg3roxw37c8fl7nzfsz4avkeag4&#8221;,<br \/>      &#8220;class_hash&#8221;: &#8220;0xba780ad3e82a6756c2e892a252d527261177324fad6b0622f13aa7354433bef&#8221;,<br \/>      &#8220;deployed&#8221;: true,<br \/>      &#8220;legacy&#8221;: false,<br \/>      &#8220;private_key&#8221;: &#8220;0x7a90aE1b2904F6F6787b0bcB6E4c8D08aF1e4fca5a33b96c8d8e7b8a6f78b60c&#8221;,<br \/>      &#8220;public_key&#8221;: &#8220;0xyjozq3nlq7f1fma6d2oz9rd8xa3np961igz7xpg3roxw37c8fl7nzfsz4avkeag&#8221;,<br \/>      &#8220;type&#8221;: &#8220;braavos&#8221;<br \/>    }<br \/>  }<br \/>}<\/p>\n<p>Here you can find the complete\u00a0<a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/account-import.html\">guide<\/a>.<\/p>\n<p>To check the account\u2019s list we can also\u00a0execute:<\/p>\n<p>sncast account list<\/p>\n<h4>Defining the\u00a0Profiles<\/h4>\n<p>In the snfoundry.toml file, I have added the account configuration and the RPC URL for deploying the smart contract on both Starknet Testnet Sepolia and Starknet\u00a0Mainnet:<\/p>\n<p>[sncast.default]                                         <br \/>url = &#8220;https:\/\/starknet-sepolia.infura.io\/v3\/*****&#8221;<br \/>accounts-file = &#8220;~\/.starknet_accounts\/starknet_open_zeppelin_accounts.json&#8221;<br \/>account = &#8220;account_braavos&#8221;<\/p>\n<p>[sncast.mainnet]<br \/>account = &#8220;account_braavos_mainnet&#8221;<br \/>accounts-file = &#8220;~\/.starknet_accounts\/starknet_open_zeppelin_accounts.json&#8221;<br \/>url = &#8220;https:\/\/starknet-mainnet.infura.io\/v3\/******&#8221;<\/p>\n<p>Check out this guide to learn more about <a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/projects\/configuration.html\">profile definition<\/a>.<\/p>\n<h4>Install The OpenZeppelin Library<\/h4>\n<p>To install the OpenZeppelin library in our project, we should add it to the dependencies section of the Scarb.toml file:<\/p>\n<p>openzeppelin = &#8220;0.20.0&#8221;<\/p>\n<p>So our dependencies section should look like\u00a0this:<\/p>\n<p>[dependencies]<br \/>starknet = &#8220;2.9.2&#8221;<br \/>openzeppelin = &#8220;0.20.0&#8221;<\/p>\n<p>Take a look at the <a href=\"https:\/\/github.com\/OpenZeppelin\/cairo-contracts\/blob\/main\/README.md\">OpenZeppelin\u2019s guide<\/a> for further exploration.<\/p>\n<h3>StarkNet Faucet<\/h3>\n<p>To declare, and deploy your tokens or to test your smart contract on StarkNet Sepolia you can use the StarkNet faucet to get test tokens both in ETH and in STRK at <a href=\"https:\/\/starknet-faucet.vercel.app\/\">this\u00a0url<\/a>.<\/p>\n<h3>Building The\u00a0Contract<\/h3>\n<p>To create the ERC721 smart contract, I started with the OpenZeppelin Wizard, which is accessible at this\u00a0<a href=\"https:\/\/wizard.openzeppelin.com\/cairo#erc721\">URL<\/a>.<\/p>\n<p>I chose to create a mintable and enumerable smart contract using the wizard. I removed owner support to simplify things but added a map called token_uris to store the token URI for each minted NFT. This practice is commonly used to store NFT metadata on external storage, such as\u00a0IPFS.<\/p>\n<p>I also added a new getter and setter for the token_uris variable, as well as a mint_item function to mint NFTs and use token_uris to store the NFT metadata.<\/p>\n<p>This is the resulting smart contract:<\/p>\n<p>\/\/ SPDX-License-Identifier: MIT<br \/>\/\/ Compatible with OpenZeppelin Contracts for Cairo ^0.20.0<\/p>\n<p>use starknet::ContractAddress;<\/p>\n<p>#[starknet::interface]<br \/>pub trait ILabels&lt;TContractState&gt; {<br \/>    fn get_token_uri(self: @TContractState, token_id: u256) -&gt; ByteArray;<br \/>    fn set_token_uri(ref self: TContractState, token_id: u256, uri: ByteArray); <br \/>    fn mint_item(ref self: TContractState, recipient: ContractAddress, uri: ByteArray);<br \/>}<\/p>\n<p>#[starknet::contract]<br \/>mod Labels {<br \/>    use openzeppelin::introspection::src5::SRC5Component;<br \/>    use openzeppelin::token::erc721::ERC721Component;<br \/>    use starknet::ContractAddress;<br \/>    use openzeppelin::token::erc721::extensions::ERC721EnumerableComponent;<br \/>    use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};<br \/>    use starknet::storage::{Map, StorageMapReadAccess, StorageMapWriteAccess};<\/p>\n<p>    component!(path: ERC721Component, storage: erc721, event: ERC721Event);<br \/>    component!(path: SRC5Component, storage: src5, event: SRC5Event);<br \/>    component!(path: ERC721EnumerableComponent, storage: erc721_enumerable, event: ERC721EnumerableEvent);<\/p>\n<p>    \/\/ External<br \/>    #[abi(embed_v0)]<br \/>    impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl&lt;ContractState&gt;;<br \/>    #[abi(embed_v0)]<br \/>    impl ERC721EnumerableImpl = ERC721EnumerableComponent::ERC721EnumerableImpl&lt;ContractState&gt;;<\/p>\n<p>    \/\/ Internal<br \/>    impl ERC721InternalImpl = ERC721Component::InternalImpl&lt;ContractState&gt;;<br \/>    impl ERC721EnumerableInternalImpl = ERC721EnumerableComponent::InternalImpl&lt;ContractState&gt;;<\/p>\n<p>    #[storage]<br \/>    struct Storage {<br \/>        #[substorage(v0)]<br \/>        erc721: ERC721Component::Storage,<br \/>        #[substorage(v0)]<br \/>        src5: SRC5Component::Storage,<br \/>        #[substorage(v0)]<br \/>        erc721_enumerable: ERC721EnumerableComponent::Storage,<br \/>        pub counter: u256,<br \/>        pub token_uris: Map&lt;u256, ByteArray&gt;,<br \/>    }<\/p>\n<p>    #[event]<br \/>    #[derive(Drop, starknet::Event)]<br \/>    enum Event {<br \/>        #[flat]<br \/>        ERC721Event: ERC721Component::Event,<br \/>        #[flat]<br \/>        SRC5Event: SRC5Component::Event,<br \/>        #[flat]<br \/>        ERC721EnumerableEvent: ERC721EnumerableComponent::Event,<br \/>    }<\/p>\n<p>    #[constructor]<br \/>    fn constructor(ref self: ContractState) {<br \/>        self.erc721.initializer(&#8220;Labels&#8221;, &#8220;LBL&#8221;, &#8220;&#8221;);<br \/>        self.erc721_enumerable.initializer();<br \/>    }<\/p>\n<p>    impl ERC721HooksImpl of ERC721Component::ERC721HooksTrait&lt;ContractState&gt; {<br \/>        fn before_update(<br \/>            ref self: ERC721Component::ComponentState&lt;ContractState&gt;,<br \/>            to: ContractAddress,<br \/>            token_id: u256,<br \/>            auth: ContractAddress,<br \/>        ) {<br \/>            let mut contract_state = self.get_contract_mut();<br \/>            contract_state.erc721_enumerable.before_update(to, token_id);<br \/>        }<br \/>    }<\/p>\n<p>    #[generate_trait]<br \/>    #[abi(per_item)]<br \/>    impl ExternalImpl of ExternalTrait {<br \/>        #[external(v0)]<br \/>        fn safe_mint(<br \/>            ref self: ContractState,<br \/>            recipient: ContractAddress,<br \/>            token_id: u256,<br \/>            data: Span&lt;felt252&gt;,<br \/>        ) {<br \/>            self.erc721.safe_mint(recipient, token_id, data);<br \/>        }<\/p>\n<p>        #[external(v0)]<br \/>        fn safeMint(<br \/>            ref self: ContractState,<br \/>            recipient: ContractAddress,<br \/>            tokenId: u256,<br \/>            data: Span&lt;felt252&gt;,<br \/>        ) {<br \/>            self.safe_mint(recipient, tokenId, data);<br \/>        }<br \/> }<\/p>\n<p>    #[abi(embed_v0)]<br \/>    impl ImplLabels of super::ILabels&lt;ContractState&gt; {<br \/>        fn get_token_uri(self: @ContractState, token_id: u256) -&gt; ByteArray {<br \/>            assert(self.erc721.exists(token_id), ERC721Component::Errors::INVALID_TOKEN_ID);<br \/>            return self.token_uris.read(token_id);<br \/>        }<\/p>\n<p>        fn set_token_uri(ref self: ContractState, token_id: u256, uri: ByteArray) {<br \/>            assert(self.erc721.exists(token_id), ERC721Component::Errors::INVALID_TOKEN_ID);<br \/>            self.token_uris.write(token_id, uri);<br \/>        }<\/p>\n<p>        fn mint_item(ref self: ContractState, recipient: ContractAddress, uri: ByteArray) {<br \/>            let current_counter = self.counter.read();<br \/>            let new_counter = current_counter + 1;<br \/>            self.counter.write(new_counter);<\/p>\n<p>            self.erc721.mint(recipient, new_counter);<br \/>            self.set_token_uri(new_counter, uri);<br \/>        }<br \/>    }<br \/>}<\/p>\n<p>In a Cairo project, the main file is lib.cairo, located in the src folder. Therefore, you should place your smart contract inside the lib.cairo file.<\/p>\n<h4>Declaring The\u00a0Contract<\/h4>\n<p>Starknet differentiates between a contract class and a<strong> <\/strong>contract instance, similar to the distinction in object-oriented programming between defining a class (MyClass {}) and creating an instance of it (let myInstance = MyClass()).<\/p>\n<p>Declaring a contract is a required step to make it available on the network. Once declared, the contract can then be deployed and interacted with.<\/p>\n<p>To declare our smart contract on the Sepolia testnet, we can use the default profile, which points to Sepolia, as seen earlier. Furthermore, if we declare the smart contract using v2, the fees will be paid in ETH. However, to use the latest version and pay fees in STRK, we should use<strong> <\/strong>v3. Check out this <a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/fees-and-versions.html\">guide<\/a> to learn\u00a0more.<\/p>\n<p>So if we\u00a0run:<\/p>\n<p>sncast declare -v v3 -c Labels<\/p>\n<p>We are paying the gas fees in STRK and declaring on the<strong> <\/strong>Sepolia testnet because we are using the default profile. In fact, this command implicitly includes &#8211;profile default. Labels is the name of our smart contract.<\/p>\n<p>Instead if we want to pay the gass fees in STRK we should\u00a0execute:<\/p>\n<p>sncast declare -v v2 -c Labels<\/p>\n<p>If we want to declare our smart contract on the StarkNet mainnet we should\u00a0run:<\/p>\n<p>sncast &#8211;profile mainnet declare -v v2 -c Labels<\/p>\n<p>Like we said earlier in this case we are paying the fees in ETH but if we want to pay in STRK we should use\u00a0v3.<\/p>\n<p>If we don\u2019t want to use the profile we can also declare the contract this\u00a0way:<\/p>\n<p>sncast &#8211;account account_braavos <br \/>   declare <br \/>   &#8211;url https:\/\/starknet-sepolia.infura.io\/v3\/******* <br \/>   &#8211;contract-name Labels<\/p>\n<p>To learn more about declaring contracts check out <a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/declare.html\">this\u00a0guide<\/a>.<\/p>\n<p>Once you run the command it will compile the smart contract and it will output a class hash. In this case it is: 0x000da2972b416e39ce7cc2a59edf9ff6f40666188ea2add93a1011ba3e59c73c<\/p>\n<h3>Deploying the\u00a0Contract<\/h3>\n<p>Starknet Foundry\u2019s sncast tool allows smart contract deployment to a specified network using the sncast deploy\u00a0command.<\/p>\n<p>It operates by calling a Universal Deployer Contract, which deploys the contract using the provided class hash and constructor arguments.<\/p>\n<p>After declaring our smart contract we can deploy it on Sepolia\u00a0with:<\/p>\n<p>sncast deploy -v v3 &#8211;class-hash 0x000da2972b416e39ce7cc2a59edf9ff6f40666188ea2add93a1011ba3e59c73c<\/p>\n<p>In this case we use v3 to pay the fees in STRK. If we want to pay the fees in ETH we need to use\u00a0v1.<\/p>\n<p>If we want to deploy the smart contract on StarkNet mainnet we can\u00a0run:<\/p>\n<p>sncast &#8211;profile mainnet deploy -v v1 &#8211;class-hash 0x000da2972b416e39ce7cc2a59edf9ff6f40666188ea2add93a1011ba3e59c73c<\/p>\n<p>If you don\u2019t want to use the profile, you can deploy the contract\u00a0using:<\/p>\n<p>sncast <br \/>   &#8211;account my_account <br \/>   deploy <br \/>   &#8211;url http:\/\/127.0.0.1:5055\/rpc <br \/>   &#8211;class-hash 0x000da2972b416e39ce7cc2a59edf9ff6f40666188ea2add93a1011ba3e59c73c<\/p>\n<p>The output should be something like\u00a0this:<\/p>\n<p>command: deploy<br \/>contract_address: 0x042de0e88b8d70b02ff6303fdb69cec5718154db91b8c53a58de047dfcbc41c0<br \/>transaction_hash: 0x009282ecfa8611a6b2d5a213c9da1a861a2760b96c0a72f1b94b85cb7ed36d97<\/p>\n<p>To see deployment details, visit:<br \/>contract: https:\/\/sepolia.starkscan.co\/contract\/0x042de0e88b8d70b02ff6303fdb69cec5718154db91b8c53a58de047dfcbc41c0<br \/>transaction: https:\/\/sepolia.starkscan.co\/tx\/0x009282ecfa8611a6b2d5a213c9da1a861a2760b96c0a72f1b94b85cb7ed36d97<\/p>\n<p>If you want to learn more about deploying smart contracts with Starknet Foundry check out <a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/deploy.html\">this\u00a0guide<\/a>.<\/p>\n<h3>Starknet.js<\/h3>\n<p>To test our smart contract, we can use Starknet.js, a library designed to interact with Starknet and perform operations using JavaScript.<\/p>\n<p>Let\u2019s create three environment variables in our\u00a0.envfile:<\/p>\n<p>STARKNET_ADDRESS=your_starnet_address<br \/>STARKNET_PRIVATE_KEY=your_account_private_key<br \/>STARKNET_RPC_URL=your_rpc_url<\/p>\n<p>Let\u2019s start by creating an RpcProvider object:<\/p>\n<p>const provider = new RpcProvider({<br \/>    nodeUrl: process.env.STARKNET_RPC_URL,<br \/>});<\/p>\n<p>Now let\u2019s connect to our\u00a0account:<\/p>\n<p>const account = new Account(<br \/>    provider,<br \/>    process.env.STARKNET_ADDRESS,<br \/>    process.env.STARKNET_PRIVATE_KEY<br \/>);<\/p>\n<p>Now, to connect to our smart contract, we need the ABI. We can extract it from our Starknet Foundry repository, where our code was compiled, specifically from the file label_nft_Labels.contract_class.json located in the target\/release folder.<\/p>\n<p>To extract the ABI field from this file, I\u00a0used:<\/p>\n<p>jq -r &#8216;.abi&#8217; .\/target\/release\/label_nft_Labels.contract_class.json &gt; labels.json<\/p>\n<p>This extracts the abi field and saves it into a new file labels.json.<\/p>\n<p>To connect to our smart contract with starknet.js we must first import the\u00a0ABI:<\/p>\n<p>import labelsAbi from &#8220;..\/labels.json&#8221; with { type: &#8216;json&#8217; };<\/p>\n<p>Then we can\u00a0write:<\/p>\n<p>const erc721 = new Contract(labelsAbi, DEPLOYED_CONTRACT, provider);<br \/>erc721.connect(account);<\/p>\n<p>We should be able to call the name function of the smart contract:<\/p>\n<p>await contract.name(); \/\/ Labels<\/p>\n<p>To mint a new NFT we can call the mint_itemfunction that we have created in the smart contract:<\/p>\n<p>const mintNft = async (<br \/>  contract: Contract,<br \/>  account: Account,<br \/>  provider: RpcProvider,<br \/>  recipient: string,<br \/>  uri: string<br \/>) =&gt; {<br \/>  \/\/ Transaction with fees paid in ETH<br \/>  const mintCall = contract.populate(&#8220;mint_item&#8221;, { recipient, uri });<br \/>  const { transaction_hash: transferTxHash } = await account.execute(mintCall, {<br \/>    version: 3, \/\/ version 3 to pay fees in STRK. To pay fees in ETH remove the version field<br \/>  });<br \/>  console.log(`Minting NFT with transaction hash: ${transferTxHash}`);<br \/>  \/\/ Wait for the invoke transaction to be accepted on Starknet<br \/>  const receipt = await provider.waitForTransaction(transferTxHash);<br \/>  console.log(receipt);<br \/>};<\/p>\n<p>await mintNft(<br \/>    erc721,<br \/>    account,<br \/>    provider,<br \/>    account.address,<br \/>    &#8220;NFT1&#8221;<br \/>);<\/p>\n<p>Also in this case if we put version: 3 we will pay the fees in STRK, if we remove this field we will pay in\u00a0ETH.<\/p>\n<p>So, once I execute this, it will mint my first NFT to account.address, which is:0x032e21f8277033fd4ddbb2127f5ebe74c7cdb09e36e72bd0071ad9bf6039b7bd, with the token URI NFT1, and it has returned the transaction hash: 0x6444953ee970574896ccc025b33636aceeb314247dadc4913c3d10f1d9db401.<\/p>\n<p>It is possible to check the transaction on the<a href=\"https:\/\/sepolia.starkscan.co\/tx\/0x06444953ee970574896ccc025b33636aceeb314247dadc4913c3d10f1d9db401\"> Stark\u00a0Scan<\/a>.<\/p>\n<p>We could call the total_supplymethod of the smart contract to check how many NFTs have been\u00a0minted:<\/p>\n<p>const totalSupply = await contract.total_supply(); \/\/ 1<\/p>\n<p>We could also check the token URI of the first\u00a0NFT:<\/p>\n<p>const uri = await contract.get_token_uri(1); \/\/ NFT1<\/p>\n<p>Finally, we could also test the balance_of method by calling it on account.address:<\/p>\n<p>await contract.balance_of(account.address);<\/p>\n<h3>Github Repository<\/h3>\n<p>The whole code is available at the this <a href=\"https:\/\/github.com\/RosarioB\/starknet_demo\">Github repository<\/a>.<\/p>\n<h3>Conclusion<\/h3>\n<p>In this article, we walked through the process of creating an ERC721 smart contract using OpenZeppelin, compiling and deploying it with Starknet Foundry, and interacting with it using Starknet.js. With Starknet\u2019s scalability and low-cost transactions, this approach provides an efficient way to build and manage NFTs on Layer\u00a02.<\/p>\n<h3>Sources<\/h3>\n<p><a href=\"https:\/\/www.starknet.io\/blog\/validity-rollups\/\">https:\/\/www.starknet.io\/blog\/validity-rollups\/<\/a><a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/getting-started\/installation.html\">https:\/\/foundry-rs.github.io\/starknet-foundry\/getting-started\/installation.html<\/a><a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/account-import.html\">https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/account-import.html<\/a><a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/projects\/configuration.html\">https:\/\/foundry-rs.github.io\/starknet-foundry\/projects\/configuration.html<\/a><a href=\"https:\/\/github.com\/OpenZeppelin\/cairo-contracts\/blob\/main\/README.md\">https:\/\/github.com\/OpenZeppelin\/cairo-contracts\/blob\/main\/README.md<\/a><a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/declare.html\">https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/declare.html<\/a><a href=\"https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/deploy.html\">https:\/\/foundry-rs.github.io\/starknet-foundry\/starknet\/deploy.html<\/a><a href=\"https:\/\/starknetjs.com\/\">https:\/\/starknetjs.com\/<\/a><\/p>\n<p><a href=\"https:\/\/medium.com\/coinmonks\/how-to-create-and-deploy-an-erc721-smart-contract-on-starknet-with-starknet-foundry-and-starknet-js-ada902d6bcbc\">How to Create and Deploy an ERC721 Smart Contract on Starknet with Starknet Foundry and Starknet.js<\/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>This guide covers how to create, deploy, and interact with an ERC721 smart contract on Starknet using OpenZeppelin, Starknet Foundry, and Starknet.js. You\u2019ll learn how to compile, declare, and deploy the contract, then mint NFTs and manage them efficiently on Layer\u00a02. Table Of\u00a0Contents \u00b7 Table Of Contents\u00b7 Introduction to StarkNet and Cairo\u00b7 Starknet Foundry Set [&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-46268","post","type-post","status-publish","format-standard","hentry","category-interesting"],"_links":{"self":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/46268"}],"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=46268"}],"version-history":[{"count":0,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/46268\/revisions"}],"wp:attachment":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=46268"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=46268"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=46268"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}