Back

blockchain - truffle - 创建erc-721 create nft call contract

发布时间: 2022-06-18 23:07:00

refer to:

(本参考这个)

https://docs.infura.io/infura/tutorials/layer-2-networks/using-aurora-to-deploy-an-ethereum-smart-contract

(其实这个也很有用,从标题来看,我还没细看)

https://docs.infura.io/infura/tutorials/ethereum/create-an-nft-using-truffle

1. mkdir test_erc_721

2. truffle init

3. 编辑truffle-config.js 内容如下:

$ cat truffle-config.js
require('dotenv').config();
const HDWalletProvider = require('@truffle/hdwallet-provider');
const { INFURA_API_URL, MNEMONIC } = process.env;

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*"
    },
    goerli: {
      provider: () => new HDWalletProvider(MNEMONIC, INFURA_API_URL),
      network_id: '5',
      gas: 5500000,
      networkCheckTimeout: 1000000,
      timeoutBlocks: 200,
      addressIndex: 2
    }
  },
  // 注意这个版本,很有用
  compilers: {   
    solc: {
      version: "^0.8.0",
    }
  }
};

4.  npm install @openzeppelin/contracts

5. npm install --save dotenv

6. 在contracts 目录下,创建 MyTestNft.sol 内容如下 :

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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


contract MyTestNft is ERC721URIStorage {
    uint256 private _tokensCount = 0;
    address public minter = address(0);

    modifier onlyMinter(){
        require(
            minter == msg.sender,
            'Invalid Minter'
        );
        _;
    }

    constructor() ERC721("MyTestNft", "MTN") {
        minter = msg.sender;
    }

    function mint(address to) external onlyMinter {
        uint256 tokenId = _tokensCount + 1;
        _mint(to, tokenId);
        _tokensCount = tokenId;
    }

    function burn(uint256 tokenId) external {
        _burn(tokenId);
        _tokensCount -= 1;
    }

    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
        require(minter == msg.sender || to == minter, 'Invalid Transfer');
        safeTransferFrom(from, to, tokenId, "");
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
        require(minter == msg.sender || to == minter, 'Invalid Transfer');
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }
}

6.2  并且创建对应的migration文件:$ vim migrations/2_deploy_contracts.js

内容如下:
const MyTestNft = artifacts.require('MyTestNft.sol');

module.exports = function(deployer) {
  deployer.deploy(MyTestNft);
}

7. 运行:$ truffle migrate --network goerli --verbose-rpc --interactive

Starting migrations...
======================
> Network name:    'goerli'
> Network id:      5
> Block gas limit: 29970676 (0x1c950f4)


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x90efe7d70f5ba7c5164cbf59f2afa41360662818ceedab9ea27972a62f0bc697
   > Blocks: 0            Seconds: 4
   > contract address:    0xeA2Cb7c1E9574B158350fAE01202ED47A32cfCf8
   > block number:        7082520
   > block timestamp:     1655593578
   > account:             0xc0dD5021e298dB57bEF361C735cd1C04cef2E48A
   > balance:             7.466584663503196232
   > gas used:            193243 (0x2f2db)
   > gas price:           2.500000007 gwei
   > value sent:          0 ETH
   > total cost:          0.000483107501352701 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:     0.000483107501352701 ETH


2_deploy_contracts.js
=====================

   Deploying 'MyTestNft'
   ---------------------
   > transaction hash:    0xb8231f2ebc2a55ca10a0635cb7c41fa954cf942ab62858a6bf42a86dcc17dd72
   > Blocks: 1            Seconds: 24
   > contract address:    0x81Ec27587694f9996a69dc26230643dD619cfaba
   > block number:        7082523
   > block timestamp:     1655593623
   > account:             0xc0dD5021e298dB57bEF361C735cd1C04cef2E48A
   > balance:             7.459741808484036238
   > gas used:            2691404 (0x29114c)
   > gas price:           2.500000007 gwei
   > value sent:          0 ETH
   > total cost:          0.006728510018839828 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:     0.006728510018839828 ETH

Summary
=======
> Total deployments:   2
> Final cost:          0.007211617520192529 ETH

8. 对该NFT的 contract verify 

我们看到目标contract已经部署到了eth goerli网络上,先一步进行verify  (为了方便我们使用remix 进行调试)

这里不能简单的使用 ether 提供的GUI进行verify, 因为无论是 json 文件还是多个sol文件,都不行

我另外写了一个文章,在这里: http://siwei.me/blog/posts/blockchain-truffle-nft-erc-721-contract-verify

8. 对该NFT的调用

8.1 mint 一个nft

8.2 查询该nft owner

8.3 转移给其他人

8.4 查询该nft owner

8.5 mint一个nft

8.6 销毁之

8.7 根据owner, 查看balance

8.8 根据nft id , 查看owner

运行下面代码即可:

const Web3 = require('web3')
const fs = require('fs')

async function main(file_name_without_suffix, contract_address){
  const { abi } = JSON.parse(fs.readFileSync("build/contracts/" +file_name_without_suffix+'.json'))

  // step1. 初始化web3 实例,增加json rpc server
  const web3 = new Web3(
    new Web3.providers.HttpProvider( 'HTTP://192.168.10.54:3355')
  )

  let private_key = "5da76275302d9b9fb14240871db41bfbd9ff08362150c0a8b24f0fd69e??????"

  // step2. 创建signer
  const signer = web3.eth.accounts.privateKeyToAccount(private_key)
  web3.eth.accounts.wallet.add(signer)

  // step3. 创建contract, abi是关键
  const contract = new web3.eth.Contract(abi, contract_address)

  let result = ''
  result = await contract.methods.minter().call()
  console.info("minter: ", result)
  result = await contract.methods.name().call()
  console.info("name: ", result)
  result = await contract.methods.symbol().call()
  console.info("symbol: ", result)

  let target_address = '0xDEC781c67a86570c004c7840BE1AddAF51C39487'

  let tx = ''
  tx = await contract.methods.mint(target_address)
  let from = signer.address
  console.info("== now let's mine one: ,from: ", from)
  result = await tx
    .send({from: from, gas: await tx.estimateGas()})
    .once("transactionHash" , (txHash) => {
      console.info("mining transaction...", txHash)
    })
    .on('error', (error) => {
      console.info("--- on error: ", error)
    })
  console.info("mint result: ", result)

  result = await contract.methods.balanceOf(target_address).call()
  console.info(`balance of ${target_address}: `, result)

  result = await contract.methods.ownerOf(1).call()
  console.info("ownerOf n: ", result)

  tx = await contract.methods.burn(1)
  result = await tx
    .send({from: from, gas: await tx.estimateGas()})
    .once("transactionHash", (txHash) => {
      console.info("burning id: ", 1)
    })
  console.info(" burn result: ", result)

}

console.info("== 使用方式: $ node call.js TestContract 0xa1b2..z9   (该TestContract.json 和  必须存在)")
main(process.argv[2], process.argv[3]).then( () => process.exit(0) )

Back