智能合约Smart Contract技术详解

这篇具有很好参考价值的文章主要介绍了智能合约Smart Contract技术详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

合约编写

建议读者先了解下solidity,这里推荐CryptoZombies,还是比较详细的。
ok当你大概知道自己在做什么之后,我们就可以开始编写智能合约了,首先我们需要一个编译器,我是用的web ide remix,当然他也有桌面版,使用起来都是一样的,web版本的话记得做备份,如果仅靠浏览器缓存来做备份的话,很容易吃亏找不到代码了等会。

基本介绍

先看几个关键常量

	uint public constant MAX_TOKENS = 2000;
    uint private constant TOKENS_RESERVED = 4;
    
    //normal whitelist price
    uint public white_price = 0.008 ether;
    //normal price
    uint public price = 0.015 ether;

    uint256 public constant MAX_MINT_PER_TX = 4;

    bool public isSaleActive = false;
    uint256 public totalSupply;
    mapping(address => uint256) private mintedPerWallet;

    string public baseUri;
    string public baseExtension = ".json";

MAX_TOKENS指的是该合约最大能mint的数量
white_price指的是白名单价格(如果你的合约有白名单的话),注意这里价格会带上ether关键字后缀,表示每一个nft的单价
price指的是普通价格
MAX_MINT_PER_TX表示一个账户能mint的数量(如果你的合约有这个需求的话)
isSaleActive表示当前合约是否可以mint的状态
mintedPerWallet是一个map,记录了每一个账户mint的数量,对应MAX_MINT_PER_TX.

构造方法

    constructor() ERC721("the smart contract's name", "SYMBOL") {
        baseUri = "ipfs://xxxxxxxxxxxxxxxxx/";
        whiteRootOG = xxxxxxxx;
        whiteRootNormal = xxxxxxxxx;
        // for(uint256 i = 1; i <= TOKENS_RESERVED; ++i) {
        //     _safeMint(msg.sender, i);
        // }
        // totalSupply = TOKENS_RESERVED;
    }

构造方法第一个参数为合约名字,baseUri为ipfs的json地址,白名单稍后会讲解,构造方法这里有些合约会直接构造出来几个给团队自己使用,看自己需求。

ipfs

ipfs全名InterPlanetary File System, 是一个分布式的web,实现了点到点超媒体协议,可以让我们的互联网速度更快,更加安全, 并且更加开放。 理论上的话未来可以取代http。如果我们传上去一个相同的图片,得到的ipfs链接是一样的,所以ipfs比http更能确保文件的安全性,而且由于是p2p的形式去下载,所以下载速度相较http也会快速很多。
ok,简单介绍了下ipfs,那么我们该如何使用呢?
ipfs上传工具目前还是比较多的,我这里建议使用ipfs desktop,像pinata也很方便,但普通用户都有存储限制。
首先我们上传一个包含图片的文件夹以后获取到一个ipfs的cid地址,然后我们就得生成一个json去告诉用户,你的nft的图片,描述,名字等。
类似:
smart contracts建筑业,web3,智能合约,区块链

tips:如果你要查看你的ipfs上传的文件,你可以使用这个链接:https://ipfs.io/ipfs/your-ipfs-cid/
把your-ipfs-cid换成你的文件cid即可。

当然一般nft使用场景里会有很多很多nft,那么这里就需要把生成json文件脚本化比较方便了,其实就是一个string字符串写入生成文件,可以用java,python等,这里就不贴了。

然后刚才生成的json文件夹必须取名为metadata,然后这个metadata文件夹的ipfs cid即是我们合约里要用到的baseUri,当然这个baseUri也是可以动态替换的,这个后面会详解,主要用在一些一开始给到用户的nft是未揭秘,然后解密后的这种场景。

mint

function mint(uint256 _numTokens, bytes32[] calldata whitelist_og_proof, bytes32[] calldata whitelist_normal_proof) external payable {
        require(isSaleActive, "The sale is paused.");
        require(_numTokens <= MAX_MINT_PER_TX, "You cannot mint that many in one transaction.");
        require(mintedPerWallet[msg.sender] + _numTokens <= MAX_MINT_PER_TX, "You cannot mint that many total.");
        uint256 curTotalSupply = totalSupply;
        require(curTotalSupply + _numTokens <= MAX_TOKENS, "Exceeds total supply.");
		require(_numTokens * price <= msg.value, "Insufficient funds.");

        for(uint256 i = 1; i <= _numTokens; ++i) {
            _safeMint(msg.sender, curTotalSupply + i);
        }
        mintedPerWallet[msg.sender] += _numTokens;
        totalSupply += _numTokens;
    }

这里注意函数后缀加了一个关键词payable,意思就是这个是支付函数。require方法有点类似其他语言中的捕捉异常,如果条件为false的话,则直接报错,错误信息为后面的string。
那么我们来简单的看下这个mint函数,忽略白名单相关形参,第一行示意如果合约现在状态isSaleActive为false的话,那么现在无法交易。
第二行就是控制交易数量,如果用户申请了超过每个人最大能mint的数量的话,直接报错。
第三行是控制每个人能mint的数量,会去map里去读取每个人mint的数量,不能超哥一个人能mint的最大数量。
下面就是控制最大数量了,如果你发行200个nft,现在已经被mint了199个,这个时候你还要去mint2个话,就会直接报错Exceeds total supply。
当然以上这些情况都需要根据你实际合约的需求去自定义。

然后就是去判断价格,这里要注意一点,_safeMint方法是可以直接去mint的,msg.sender指的是发起该交易的用户的account,所以如果你要去给nft设置价格的话,必须在_safeMint前去做一道价格的关卡来控制价格,如果有白名单等价格不一样的话,这里都要去做价格限制。合约和支付宝、微信支付等不一样的地方就是他的设置价格是在这里进行条件判断设置的。

提现

    function withdrawAll() external payable onlyOwner {
        uint256 balance = address(this).balance;
        uint256 balanceOne = balance * 0.5;
        uint256 balanceTwo = balance * 0.5;
        ( bool transferOne, bool transferTwo ) = payable(msg.sender1, msg.sender2).call{value: balanceOne, balanceTwo}("");
        require(transferOne, "Transfer failed.");
    }

这里注意有一个关键字onlyOwner,意思就是只有创建合约的账号可以调用的方法。这里我们把收益五五分成,分给了msg.sender1和msg.sender2,当然这里也可以改成只给一个正好,这里根据自己的需求来自定义即可。

白名单

在一些nft中,会有一部分用户的mint价格和普通用户的mint价格不一样,所以我们要存下这部分用户的account id,然后如果是这个用户群体的话,那么前面控制价格那里可以针对这些用户进行不一样的价格控制操作。
正常逻辑,我们放一个数组来存放这些account,但是如果智能合约发布以后,我们要去修改这个白名单账号群体的增删改查,如果是一个数组的话,那就很麻烦,而且批量操作如果写的不当的话,就会多出很多gas费,而且数组的话存放空间也会变大,完全不适合这种动态化的case,所以我们只能另外寻找方法来解决这个case。
区块链技术中有一个概念叫做默克尔树,也就是Merkle树。
默克尔树是一种哈希树,其中每个叶子节点都标有数据块的加密哈希值,而每个非叶子节点都标有其子节点的加密哈希值的标签。大多数哈希树的实现是二进制的(每个节点有两个子节点),但它们也可以有更多的子节点。它允许你验证某些数据是否存在于树中,而不需要去轮训啊遍历啊。
smart contracts建筑业,web3,智能合约,区块链
我的理解是这样,有多少数据就有多少叶子结点,叶子结点的数据是该数据的hash值,两个叶子结点会生成对应的父节点,然后以此往上推,会有一个唯一的根结点,数据不同根结点也会不同,所以其实可以根据根节点的hash和叶子结点的hash来类推出这个叶子结点是否是该数据集中。
ok,原理介绍的差不多了,我们来简单介绍下具体该如何实操。

合约

import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

constructor() ERC721("smart contract's name", "SYMBOL") {
	baseUri = "ipfs://xxxxxxxxxx/";
	whiteRootOG = 0xad8403ee270f9d5d3aae410de98f923e33c6e9c57df0f1c986119fa61192e14c;
	//.,.........
}

function isVerifyMerkleNormal(bytes32[] calldata proof) view public returns (bool) {
	bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
    return MerkleProof.verify(proof, whiteRootNormal, leaf);
}

首先你需要import,然后合约的角色是去验证,传入proof,然后生成leaf对象,verify方法会根据root去做校验,这里会在构造方法里先初始化一个root,后面如果有白名单的增删改查的话,只要去修改这个root就行了,如果有修改只需要修改一次gas费即可。

前端

前端这里的流程是这样的,有一个accounts的数组,根据这个数组去生成默克尔树的roots,如果是部署合约的时候,直接把这个roots写进合约里就行了,但如果是增删改查白名单的话,就需要在合约里的write contract方法里去更新这个roots了。
这里注意,如果非数组里的账户生成的proof为空,前端如果要测试的话就可以这么来测试是否为白名单。

npm i -D merkletreejs keccak256

首先我们要npm install需要的插件。

  //生成白名单
  const generateWhiteOGProofs = () => {

    //buffer化叶子结点
    const leafNodes = whitelistAddressesOG.map(addr => keccak256(addr));
    //实例化默克尔树
    const merkleTree = new MerkleTree(leafNodes, keccak256, { sortPairs: true });
    setMerkleTreeOG(merkleTree);
    //获取根哈希值
    const rootHash = merkleTree.getRoot();
    console.log(rootHash);
    console.log('Whitelist Merkle Tree og\n', merkleTree.toString());
  }

部署

smart contracts建筑业,web3,智能合约,区块链
选择compile 合约文件,也就是编译检查,编译通过后,我们就可以去做发布操作了。
smart contracts建筑业,web3,智能合约,区块链
我这里选择的是Injected Provider,用的是Goerli测试网络,如果你账号里没有币的话,推荐这里,每天可以提取0.5eth。然后deploy按钮就可以直接付了gas费后直接测试发布。可以在etherscan里直接view。
smart contracts建筑业,web3,智能合约,区块链
这里,合约就部署成功了。

验证合约代码

由于我们先mint了几个,所以opensea上直接可以查看了。这里要特别注意,opensea上json数据出来比较慢,如果你的图片或者视频比较大的话,也可能会出现过了很久也出不来的情况,在这里建议图片或者视频小一点。如果数据没刷出来,可以在opensea上点击refresh data按钮。
smart contracts建筑业,web3,智能合约,区块链
目前合约部署成功,但contract的方法并没有显示。所有如果我们要在etherscan上直接读取合约的一些数据,或者对合约进行了一些修改操作,比如修改价格,修改白名单等,就需要对contract方法进行验证。
可以直接在remix上操作,插件里搜索flattener,点击activate。
smart contracts建筑业,web3,智能合约,区块链
然后直接保存sol文件直接在etherscan里保存进去进行验证即可。
smart contracts建筑业,web3,智能合约,区块链
成功以后我们就可以直接在etherscan上read和write contract了。

前端和合约交互

前端和合约的交互的话主要分为两类,对应contact里的read和write方法,这里我分别以read里的获取已经mint了多少个和mint方法去对应read合约方法和write合约方法为例子。

准备工作

首先我们在remix里目录contracts/artifacts目录下找到对应的合约名字的json文件,在你的前端项目中新建一个contract文件夹,将这个json文件拷贝过来,并且记录下你的合约地址进行替换,我们需要根据这个地址去获取合约地址对象。

npm install ethers

npm下载ethers插件。

import { MerkleTree } from 'merkletreejs';
import { keccak256 } from 'ethers/lib/utils';
import { ethers } from 'ethers';
import { message } from 'antd';

import contract from "./../../../../contracts/NFT.json";

//............

const contractAddress = "your contract address";
const abi = contract.abi;

获取已经mint了的数量

const getTotalSupply = async () => {
    try {
      const { ethereum } = window;

      if (ethereum) {
        const provider = new ethers.providers.Web3Provider(ethereum);
        const signer = provider.getSigner();
        const nftContract = new ethers.Contract(contractAddress, abi, signer);

        //获取总共多少币了
        nftContract.totalSupply().then(c => {
          console.log('已经mint了: ' + parseInt(c));
          setAlreadyMint(parseInt(c));

          if (alreaderMint == maxSale) {
            setStatus('SoleOut');
          }
        });
      }
    } catch (err) {
      console.log(err);
    }
  }

异步方法,获取到nftContract对象后,直接可以调用totalSupply方法即可,totalSupply方法为编写合约的时候写的read方法,如果你要在前端查看价格等其他read方法,道理相同。

mint

const mintNftHandler = async () => {
    try {

      if (currentAccount == null) {
        connectWalletHandler();

        return;
      } else {
        if (minNum != "1" && minNum != "2" && minNum != "3" && minNum != "4") {
          // alert("Up to 5 can be minted");
          message.open({ content: 'Up to 4 can be minted' });
        } else {

          if (alreaderMint == maxSale) {
            message.open({ content: 'Sold Out' });
          }

          const { ethereum } = window;

          if (ethereum) {
            const provider = new ethers.providers.Web3Provider(ethereum);
            const signer = provider.getSigner();
            const nftContract = new ethers.Contract(contractAddress, abi, signer);

            let normalCost = 0;
            if (checkIsWhiteListOG()) {
              if (isCurrentAccountMinted) {
                normalCost = parseInt(minNum) * whitelistOGPriceLast3;
              } else {
                normalCost = whitelistOGPrice + (parseInt(minNum) - 1) * whitelistOGPriceLast3;
              }
            } else if (checkIsWhiteListNormal()) {
              if (isCurrentAccountMinted) {
                normalCost = parseInt(minNum) * whitelistNormalPriceLast3;
              } else {
                normalCost = whitelistNormalPrice + (parseInt(minNum) - 1) * whitelistNormalPriceLast3;
              }
            } else {
              normalCost = parseInt(minNum) * normalPrice;
            }

            const errAddress = keccak256(currentAccount);
            console.log(merkleTreeOG.toString());

            //取得默克尔证明
            const hexProofOG = merkleTreeOG.getHexProof(errAddress);
            const hexProofNormal = merkleTreeNormal.getHexProof(errAddress);
            let nftTxn = await nftContract.mint(minNum, hexProofOG, hexProofNormal,
              { value: ethers.utils.parseEther(normalCost + "") });

            message.open({ content: 'Transaction in progress, Please wait...' });
            await nftTxn.wait();

            message.open({ content: 'mint successful' });
          } else {
            message.open({ content: 'Ethereum object does not exist' });
          }
        }
      }
    } catch (err) {
      console.log(err + "");
    }
  }

注意这是一个异步方法,首先根据用户类型去做判断,需要支付的价格,然后就直接调用nftContract.mint方法直接去mint就可以了,这里参数直接参照合约里的mint方法,加入了一个value参数也就是算好的价格。这里有一个安全性考虑,如果用户在前端代码反编译了以后去修改了价格,因为我们合约里是有做价格保护的,所以会直接报错价格不够。

ok,至此,一个智能合约的基本流程就通了。文章来源地址https://www.toymoban.com/news/detail-779872.html

到了这里,关于智能合约Smart Contract技术详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 区块链相关技术、概念以及技术实现过程中的一些关键问题 Smart Contracts and Blockchains

    作者:禅与计算机程序设计艺术 2017年底,区块链已经成为众多投资人和技术人员最关注的话题之一。随着现实世界的不断复杂化、数字货币的流行以及IoT设备的普及,加密数字货币市场正变得越来越活跃。由于区块链具有去中心化、不可篡改、透明性、高并发等特点,使其

    2024年02月09日
    浏览(52)
  • native smart contracts, a easy forgotten thing.

    \\\"内置的智能合约\\\"是指在区块链网络中预先存在的智能合约。这些合约通常由区块链的开发者编写,并在区块链网络创建时就已经部署。这些合约的功能通常包括处理基本的交易和账户管理等核心功能。 以太坊网络就有一种内置的智能合约,叫做 ERC-20 合约1。ERC-20 是一种代币

    2024年02月20日
    浏览(33)
  • 【论文研读】-An Efficient Framework for Optimistic Concurrent Execution of Smart Contracts

    区块链平台中的一个个交易都是由智能合约编写的,每一个交易想要成功上链,首先需要经过矿工(想要进行上链的节点,也就是新区块)进行挖矿,然后将挖好的区块交给验证者(区块链中已经挖矿成功的节点进行验证)进行验证,验证成功就会将区块上链;验证失败,则

    2024年01月21日
    浏览(41)
  • Smart Fuzzing智能模糊测试

    BSidesLison2016-Keynote-The Smart Fuzzer Revolution by Dan Guido 演讲者:Dan Guido 解读:CSDN@IT鹅 智能模糊测试在未来市场具有巨大潜能,掌握这套技术意味着我们对更多的安全产生了威胁,正如棱角的一句话来讲: “网络本是安全的,自从又了安全研究员,就不安全了”。 1988年,华盛顿

    2024年02月07日
    浏览(80)
  • Solidity Uniswap V2 Router contract addLiquidity

            router 合约是一种高级合约,是大多数用户应用程序的入口点。通过该合约,可以更轻松地创建交易对、添加和删除流动性、计算交换中的价格变化以及执行交换。Router 适用于通过工厂合约部署的所有交易对,是一种通用合约。 GitHub - XuHugo/solidityproject: DApp go go

    2024年04月27日
    浏览(40)
  • fabric-contract-api-go快速上手

    hi,好久没有更新Fabric相关文章,今天给大家带来fabric-contract-api-go快速上手,相较于原文省略较多,希望深入理解的小伙伴可以点原文学习。 背景 Fabric提供了大量的API来开发智能合约,支持 Go, Node.js, 和Java。本文对fabric-contract-api-go进行简单梳理,可以使读者快速上手。 原文

    2024年02月19日
    浏览(37)
  • 快速理解Laravel容器(IOC、DI、Provider、Contract)

    分享一些个人见解。 Laravel里面的某些概念,就像魔术一样,看起来很厉害,当知道魔术怎么变的,就会认为也不过如此。所以不必感觉Laravel里有些概念难以理解。 应当抛除被框架约束思维的枷锁,用PHP设计的角度去思考,关注大概,而不是在在框架层面逐行磨叽。毕竟源码

    2024年04月22日
    浏览(28)
  • SMART原则

    SMART原则|由管理学大师彼得·德鲁克在他的著作《管理实践》一书中提出。 SMART原则是目标管理的一个很好的方法,可以帮助我们更有效、科学、规范的制定目标和任务。不论是工作、学习还是生活,都非常有用。 Specific明确的-目标要用具体的语言清楚描述要达成的行为标准

    2024年02月12日
    浏览(32)
  • 西门子200SMART笔记

    上位机 控件库 HslControls SunnyUI 初级课程 传感器接线方式 棕色(BN) + 蓝色(BL) - 黑色(BK) 信号线 NPN型 1M(M)接 +24V PNP型 1M(M)接 0V PLC输出接线 电路图 — 梯形图 过载 停止信号 输入端接常闭 ---- 因为接了常闭 所以输入点有信号 程序中的常开点闭合 程序 中使用常开 KA ===== M / V M:25

    2024年02月06日
    浏览(66)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包