Dapp开发实战:去中心化NFT交易平台

在以太坊上建立数字市场

在本次实战所使用的技术是React, Next.js, Tailwind CSS, RemixIDE, Solidity, Ethers.

编写智能合约
市场将由两个主要的智能合约组成:
用于铸造 NFT的NFT合约 促进 NFT销售的市场合约

为了编写 NFT,我们可以使用OpenZeppelin获得的ERC721标准。
参考:https://docs.openzeppelin.com/contracts/4.x/erc721

contract NFT is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    address contractAddress;

    constructor(address marketplaceAddress) ERC721("Xia Blocks NFTs", "XBNFT") {
        contractAddress = marketplaceAddress;
    }//合约可以允许市场合约批准将代币从所有者转移到卖家
    function createToken(string memory tokenURI) public returns (uint) {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
        _mint(msg.sender, newItemId);
        _setTokenURI(newItemId, tokenURI);
        setApprovalForAll(contractAddress, true);
        return newItemId;
    }
}

编写市场合约
继承ReentrancyGuard:有助于防止对函数的可重入调用的合同模块

// SPDX-License-Identifier: XXL
pragma solidity ^0.8.3;
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract NFTMarket is ReentrancyGuard {
  using Counters for Counters.Counter;
  Counters.Counter private _itemIds;
  Counters.Counter private _itemsSold;
  uint[] marketItems;
	//存储记录
  struct MarketItem {
    uint itemId;
    address nftContract;
    uint256 tokenId;
    address payable seller;
    address payable owner;
    uint256 price;
  }

  mapping(uint256 => MarketItem) private idToMarketItem;

  event MarketItemCreated (
    uint indexed itemId,
    address indexed nftContract,
    uint256 indexed tokenId,
    address seller,
    address owner,
    uint256 price
  );

  function getMarketItem(uint256 marketItemId) public view returns (MarketItem memory) {
    return idToMarketItem[marketItemId];
  }

  function createMarketItem(
    address nftContract,
    uint256 tokenId,
    uint256 price
  ) public payable nonReentrant {
    require(price > 0, "Price must be at least 1 wei");

    _itemIds.increment();
    uint256 itemId = _itemIds.current();
    marketItems.push(itemId);
  
    idToMarketItem[itemId] =  MarketItem(
      itemId,
      nftContract,
      tokenId,
      payable(msg.sender),
      payable(address(0)),
      price
    );

    IERC721(nftContract).transferFrom(msg.sender, address(this), tokenId);

    emit MarketItemCreated(
      itemId,
      nftContract,
      tokenId,
      msg.sender,
      address(0),
      price
    );
  }

  function createMarketSale(
    address nftContract,
    uint256 itemId
    ) payable public {
    uint price = idToMarketItem[itemId].price;
    uint tokenId = idToMarketItem[itemId].tokenId;
    require(msg.value == price, "Please submit the asking price in order to complete the purchase");

    idToMarketItem[itemId].seller.transfer(msg.value);
    IERC721(nftContract).transferFrom(address(this), msg.sender, tokenId);
    idToMarketItem[itemId].owner = payable(msg.sender);
    _itemsSold.increment();
  }

  function fetchMarketItem(uint itemId) public view returns (MarketItem memory) {
    MarketItem memory item = idToMarketItem[itemId];
    return item;
  }
	//返回所有仍在销售的市场商品
  function fetchMarketItems() public view returns (MarketItem[] memory) {
    uint itemCount = _itemIds.current();
    uint unsoldItemCount = _itemIds.current() - _itemsSold.current();
    uint currentIndex = 0;

    MarketItem[] memory items = new MarketItem[](unsoldItemCount);
    for (uint i = 0; i < itemCount; i++) {
      if (idToMarketItem[i + 1].owner == address(0)) {
        uint currentId = idToMarketItem[i + 1].itemId;
        MarketItem storage currentItem = idToMarketItem[currentId];
        items[currentIndex] = currentItem;
        currentIndex += 1;
      }
    }
   
    return items;
  }
	//返回用户已购买的NFT
  function fetchMyNFTs() public view returns (MarketItem[] memory) {
    uint totalItemCount = _itemIds.current();
    uint itemCount = 0;
    uint currentIndex = 0;
    for (uint i = 0; i < totalItemCount; i++) {
      if (idToMarketItem[i + 1].owner == msg.sender) {
        itemCount += 1;
      }
    }
    MarketItem[] memory items = new MarketItem[](itemCount);
    for (uint i = 0; i < totalItemCount; i++) {
      if (idToMarketItem[i + 1].owner == msg.sender) {
        uint currentId = idToMarketItem[i + 1].itemId;
        MarketItem storage currentItem = idToMarketItem[currentId];
        items[currentIndex] = currentItem;
        currentIndex += 1;
      }
    }
    return items;
  }
}

编写前端
首页
在这里插入图片描述

Home页面:使用web3Modal加载钱包连接以太坊网络
调用合约函数fetchMarketItems加载页面

  const [nfts, setNfts] = useState([])
  const [loaded, setLoaded] = useState('not-loaded')
  useEffect(() => {
    loadNFTs()
  }, [])
  
 async function loadNFTs() {
    const providerOptions = {
      fortmatic: {
        package: Fortmatic,
        options: {
          // Mikko's TESTNET api key
          key: "pk_test_391E26A3B43A3350"
        }
      },
      walletconnect: {
        package: WalletConnectProvider, // required
        options: {
          infuraId: "INFURA_ID" // required
        }
      }
    };
    const web3Modal = new Web3Modal({
      network: "mainnet",
      cacheProvider: true,
      disableInjectedProvider: false,
      providerOptions: providerOptions, // required
    });
    const connection = await web3Modal.connect()
    const provider = new ethers.providers.Web3Provider(connection)
    const tokenContract = new ethers.Contract(nftaddress, nftabi, provider)
    const marketContract = new ethers.Contract(nftmarketaddress, nftmarketabi, provider)
    const data = await marketContract.fetchMarketItems()

    const items = await Promise.all(data.map(async i => {
      const tokenUri = await tokenContract.tokenURI(i.tokenId)
      const meta = await axios.get(tokenUri)
      let price = web3.utils.fromWei(i.price.toString(), 'ether');
      let item = {
        price,
        tokenId: i.tokenId.toNumber(),
        seller: i.seller,
        owner: i.owner,
        image: meta.data.image,
        name: meta.data.name,
        description: meta.data.description,
      }
      return item
    }))
    console.log('items: ', items)
    setNfts(items)
    setLoaded('loaded')
  }

Create NFT 铸币页面(信息上传ipfs返回链接作为tokenURI)
在这里插入图片描述

const client = ipfsHttpClient('https://ipfs.infura.io:5001/api/v0')
  const [fileUrl, setFileUrl] = useState(null)
  const [formInput, updateFormInput] = useState({ price: '', name: '', description: '' })
  const router = useRouter()

  async function createSale(url) {
    const web3Modal = new Web3Modal({
      network: "mainnet",
      cacheProvider: true,
    });
    const connection = await web3Modal.connect()
    const provider = new ethers.providers.Web3Provider(connection)    
    const signer = provider.getSigner()
    
    let contract = new ethers.Contract(nftaddress,nftabi, signer)
    let transaction = await contract.createToken(url)
    let tx = await transaction.wait()
    let event = tx.events[0]
    let value = event.args[2]
    let tokenId = value.toNumber()
    const price = web3.utils.toWei(formInput.price, 'ether')
  
    const listingPrice = web3.utils.toWei('0.1', 'ether')

    contract = new ethers.Contract(nftmarketaddress, nftmarketabi, signer)
    transaction = await contract.createMarketItem(nftaddress, tokenId, price, { value: listingPrice })
    
    await transaction.wait()
    router.push('/')
  }
 async function onChange(e) {
    const file = e.target.files[0];
    try {
      const added = await client.add(
        file,
        {
          progress: (prog) => console.log(`received: ${prog}`)
        }
      )
      const url = `https://ipfs.infura.io/ipfs/${added.path}`
      setFileUrl(url)
    } catch (error) {
      console.log('Error uploading file: ', error);
    }  
  }
  
  async function createMarket() {
    const { name, description, price } = formInput
    if (!name || !description || !price || !fileUrl) return
    // first, upload to IPFS
    const data = JSON.stringify({
      name, description, image: fileUrl
    })
    try {
      const added = await client.add(data)
      const url = `https://ipfs.infura.io/ipfs/${added.path}`
      createSale(url)
    } catch (error) {
      console.log('Error uploading file: ', error);
    }  
  }
  


MyNFT 同上loadNFTS一样,不过调用合约fetchMyNFTs
在这里插入图片描述
完整代码已放置Gitee https://gitee.com/xiaxuliang/nftmakret

  • 9
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
区块链智能合约是一种基于区块链技术的智能化合约,它通过编写智能合约代码,实现了自动化执行、验证和执行合约条件的功能。区块链智能合约具有去中心化、透明、安全等特点,可以用于各种领域的应用。 DApp去中心化应用)是构建在区块链上的应用程序,与传统的中心化应用不同,DApp使用智能合约来管理和执行应用逻辑,数据存储在区块链上,确保信息的可靠性和安全性。 实战电子版指的是对区块链智能合约与DApp应用进行实际操作和开发的电子版本。通过实战电子版,用户可以学习如何编写智能合约代码,了解区块链技术的应用场景,以及如何开发DApp应用。 实战电子版可以提供一系列的案例和示例代码,提供操作指南和开发工具,帮助用户深入理解区块链智能合约与DApp应用的原理和使用方法。用户可以通过实践和模拟操作来学习,并将所学知识应用到实际的区块链项目中。 通过实战电子版,用户可以学习到智能合约的编写和部署、DApp应用的开发和测试、区块链节点的搭建和管理等相关知识。同时,实战电子版还可以提供实时更新和维护,以适应区块链技术的不断发展和更新。 总而言之,区块链智能合约与DApp应用实战电子版是一种通过实践和模拟操作来学习和探索区块链技术的电子学习资源,对于理解、应用和开发区块链相关项目具有重要意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值