Botón de "Mint" NFTs en tu página web
Tags: blockchain, solidity, nft, nfts
Published: 2021-10-04 18:26:35

---
title: Botón de "Mint" NFTs en tu página web
published: true
description: 
tags: blockchain, solidity, nft, nfts
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2647ln8sj66n32mnamlq.png
---

Los proyectos de NFTs coleccionables que atraen mas compradores cuentan con un sitio web para mintear. En este video lanzamos un smart contract de una colección de NFTs en Rinkeby y luego la interfaz web necesaria con el botón de "Mint".

{% youtube hwKo-DKIxjk %}

## Dependencias

Para este tutorial ocuparás [NodeJs](https://nodejs.org/es/) que les recomendará descargarlo el Linux via [NVM](https://github.com/nvm-sh/nvm#installing-and-updating) , un URL de RPC te recomiendo usar [INFURA](https://infura.io/), [Metamask](https://metamask.io/) con fondos de Rinkeby Testnet que puedes conseguir desde el [Faucet](https://faucet.rinkeby.io/).

## 1. Configuración inicial

```bash
mkdir MyNFTCollection
cd MyNFTCollection
npm install --save-dev truffle dotenv @truffle/hdwallet-provider @openzeppelin/contracts
npx truffle init
```

`truffle-config.js`
```js
require('dotenv').config()
const HDWalletProvider = require('@truffle/hdwallet-provider');

const fs = require('fs');

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*",
    },
    rinkeby: {
      provider: function () {
        return new HDWalletProvider(process.env.PRIVATE_KEY, process.env.RINKEBY_RPC_URL);
      },
      network_id: 4,
      gas: 4000000
    }
  },
  mocha: {
  },
  compilers: {
    solc: {
      version: "0.8.8",
    }
  },
  db: {
    enabled: false
  }
};
```

## 2. Lanzamos el contrato

Crea y edita el contrato a tu conveniencia.

`contracts/MyNFTCollection.sol`

```solidity
// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.8;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyNFTCollection is ERC721Enumerable {
    uint256 public MAX_ELEMENTS = 5;
    uint256 public PRICE = 0.01 ether;
    address public CREATOR = 0x0000000000000000000000000000000000000000;
    uint256 public token_count;
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdTracker;

    constructor() ERC721("My NFT", "MNFT") {}

    function _baseURI() internal view virtual override returns (string memory) {
        return "MIURL";
    }

    function _totalSupply() internal view returns (uint) {
        return _tokenIdTracker.current();
    }

    function totalMint() public view returns (uint256) {
        return _totalSupply();
    }

    function mint(address _to, uint256 _count) public payable {
        uint256 total = _totalSupply();
        require(total + _count <= MAX_ELEMENTS, "Max limit");
        require(total <= MAX_ELEMENTS, "Sale end");
        require(msg.value >= PRICE*_count, "Value below price");

        for (uint256 i = 0; i < _count; i++) {
            _mintAnElement(_to);
        }
    }

    function _mintAnElement(address _to) private {
        uint id = _totalSupply();
        _tokenIdTracker.increment();
        _safeMint(_to, id);
    }

    function withdrawAll() public {
        (bool success, ) = CREATOR.call{value:address(this).balance}("");
        require(success, "Transfer failed.");
    }
}
```

`migrations/2_my_deploy.js`
```js
const MyNFTCollection = artifacts.require("MyNFTCollection")

module.exports = async function (deployer) {
  await deployer.deploy(MyNFTCollection)
}
```

`.env`
```js
RINKEBY_RPC_URL=TULLAVERPC
PRIVATE_KEY=TULLAVEPRIVADA
```

```bash
npx truffle migrate --network rinkeby
```

## 3. El frontend

Crea la carpeta `client/contracts` y copypastea dentro `build/MyNFTCollection.json`. También agrega el html y js necesario y edítalos a tu conveniencia.

`client/index.html`
```html
<p><span id="web3_message">Loading web3...</span></p>
<select id="mint_amount">
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
  <option value="4">4</option>
</select>
<input type="button" value="mint!" onclick="mint()">
<p>You have <span id="nft_balance"></span> NFTs</p>

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.3.5/web3.min.js"></script>
<script type="text/javascript" src="src/contract_interaction.js"></script>
```

`client/src/contract_interaction.js`

```js
const NETWORK_ID = 4
var contract
var accounts
var web3
var balance
var price

function metamaskReloadCallback()
{
  window.ethereum.on('accountsChanged', (accounts) => {
    document.getElementById("web3_message").textContent="Accounts changed, realoading...";
    window.location.reload()
  })
  window.ethereum.on('networkChanged', (accounts) => {
    document.getElementById("web3_message").textContent="Network changed, realoading...";
    window.location.reload()
  })
}

const getWeb3 = async () => {
  return new Promise((resolve, reject) => {
    if(document.readyState=="complete")
    {
      if (window.ethereum) {
        const web3 = new Web3(window.ethereum)
        metamaskReloadCallback()
        try {
          // ask user permission to access his accounts
          (async function(){
            await window.ethereum.request({ method: "eth_requestAccounts" })
          })()
          resolve(web3)
        } catch (error) {
          reject(error)
        }
      } else {
        reject("must install MetaMask")
        document.getElementById("web3_message").textContent="Error: Please install Metamask";
      }
    }else
    {
      window.addEventListener("load", async () => {
        if (window.ethereum) {
          const web3 = new Web3(window.ethereum)
          metamaskReloadCallback()
          try {
            // ask user permission to access his accounts
            await window.ethereum.request({ method: "eth_requestAccounts" })
            resolve(web3);
          } catch (error) {
            reject(error);
          }
        } else {
          reject("must install MetaMask")
          document.getElementById("web3_message").textContent="Error: Please install Metamask";
        }
      });
    }
  });
};

function handleRevertError(message) {
  alert(message)
}

async function getRevertReason(txHash) {
  const tx = await web3.eth.getTransaction(txHash)
  await web3.eth
    .call(tx, tx.blockNumber)
    .then((result) => {
      throw Error("unlikely to happen")
    })
    .catch((revertReason) => {
      var str = "" + revertReason
      json_reason = JSON.parse(str.substring(str.indexOf("{")))
      handleRevertError(json_reason.message)
    });
}

const getContract = async (web3) => {
  const data = await getJSON("./contracts/MyNFTCollection.json")

  const netId = await web3.eth.net.getId()
  const deployedNetwork = data.networks[netId]
  const contract = new web3.eth.Contract(
    data.abi,
    deployedNetwork && deployedNetwork.address
  )
  return contract
}

function getJSON(url) {
  return new Promise(resolve => {
    var xhr = new XMLHttpRequest()
    xhr.open("GET", url, true)
    xhr.responseType = "json"
    xhr.onload = function () {
      resolve(xhr.response)
    };
    xhr.send()
  });
}

async function loadApp() {
  var awaitWeb3 = async function () {
    web3 = await getWeb3()
    web3.eth.net.getId((err, netId) => {
      if (netId == NETWORK_ID) {
        var awaitContract = async function () {
          contract = await getContract(web3);
          var awaitAccounts = async function () {
            accounts = await web3.eth.getAccounts()
            document.getElementById("web3_message").textContent="Connected";
            balance = await contract.methods.balanceOf(accounts[0]).call()
            document.getElementById("nft_balance").textContent=balance;
            price = await contract.methods.PRICE().call()
          };
          awaitAccounts();
        };
        awaitContract();
      } else {
        document.getElementById("web3_message").textContent="Please connect to Rinkeby Testnet";
      }
    });
  };
  awaitWeb3();
}

loadApp()

const mint = async () => {
  let mint_amount = document.getElementById("mint_amount").value
  const result = await contract.methods.mint(accounts[0], mint_amount)
    .send({ from: accounts[0], gas: 0, value: price * mint_amount })
    .on('transactionHash', function(hash){
      document.getElementById("web3_message").textContent="Minting...";
    })
    .on('receipt', function(receipt){
      document.getElementById("web3_message").textContent="Success! Minting finished.";    })
    .catch((revertReason) => {
      getRevertReason(revertReason.receipt.transactionHash);
    });
}
```

## 4. Interactuar vía web

```bash
npm install -g lite-server
cd client
lite-server
```

El resultado se encuentra en [http://localhost:3000](http://localhost:3000).

A partir de aquí puedes hacer cambios en el contrato y luego en la web para reflejarlos. También considera cambiar la configuración y el frontend para migrar a otra red (mainnet, L2, polygon, avalanche, etc...). Y para lanzar tu interfaz web te recomendaría Netlify, es grátis y sencillo.

También puedes trabajar en base a la [template pública en Github](https://github.com/FilosofiaCodigo/NFTCollectionTemplate).

**¡Gracias por ver este tutorial!**

Sígueme en dev.to y en [Youtube](https://www.youtube.com/channel/UCNRB4tgwp09z4391JRjEsRA) para todo lo relacionado al desarrollo en Blockchain en Español.