blockchain - truffle - 创建erc-721 create nft call contract
访问量: 142
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) )