第五篇 在墨客区块链(MOAC BlockChain)部署ERC-721合约

本文基于墨客区块链(MOAC BlockChain),使用moac网页版钱包部署ERC-721合约,实现相应数字化操作。

环境:

墨客区块链版本:MOAC-pangu0.8.2;

节点版本:MOAC-pangu0.8.2-windows.exe;

操作系统:64位Windows 10家庭版。

1.安装并启动本地节点

1.1 安装moac节点

请参考文档《第三篇 墨客区块链(MOAC BlockChain)节点安装教程》。

1.2 启动moac节点

打开命令终端(cmd),转到墨客当前目录,在命令行中执行:

扫描二维码关注公众号,回复: 2445344 查看本文章
D:\moacPangu0.8.2-win>moac --rpc --rpccorsdomain "http://wallet.moac.io"

备注: 

--rpc  启动RPC服务,本机访问节点

--rpccorsdomain  启动浏览器访问服务,非本机访问节点

"http://wallet.moac.io" moac在线钱包网址,会自动连接到本机启动的moac节点并显示已有账号,如果本机节点没有启动,会显示以下提示信息。

启动正常,会显示以下账号信息。确保其中一个账号有足够的moac以进行智能合约部署。

2.编写并编译智能合约

2.1 以下为基于erc-721编写的一个代币合约

本文实际测试代码TestToken721.sol附在文章末尾。

注意:该代码为测试实例使用,非标准部署智能合约代码。

2.2 编译、部署合约

在网页钱包点击合约按钮“CONTRACTS”

然后点击部署新合约按钮“DEPLOY NEW CONTRACT”

选择一个有moac余额的账号。

把自己编写的智能合约代码放进代码区“Solidity contract source code”,该代码会自动进行编译,如果编译通过,右侧会显示"Select contract to deploy"。

如果编译没有通过,则不会显示相应内容。

在“Select contract to deploy”下拉框选择相应的token类型,本例为“Token Demo”。

在选择gas费用后,点击"DEPLOY"部署合约。此时会显示发布合约确认界面。

如果提示需要解锁(部署智能合约的)账户,进入moac命令行界面,输入命令:

>personal.unlockAccount(mc.accounts[0], "passwd", 300)

该命令共有三个参数:

第一个参数表示解锁的账户,

第二个参数是该账户的密码,

第三个参数是解锁时间,本例为300秒,300秒后会重新锁住账户;

如果解锁时间设为0,表示一直解锁,此时账号有风险,慎用。

为了隐藏密码,可以输入命令:

>personal.unlockAccount(mc.accounts[0])

回车后提示输入密码,此时密码不显示出来,该命令默认将账户解锁300秒。

成功解锁会返回true。此时会显示发布合约确认界面。

注意:Data里必须有数据,否则不能正常部署

点击“OK”,开始部署合约,界面下方会显示当前部署合约的进度条

进度条12区块确认,表示部署完毕。

点击成功部署的合约,可以看到基本内容,包括合约地址hash、被写入的区块号等。

登录moac区块链浏览器:http://explorer.moac.io/home。在tokens下拉框选择“View ERC721”。

在搜索栏输入合约地址,可以搜索到该合约的相应内容。

点击进去,可以看到合约基本信息、Token Transfers和Token Holders信息。

注意:该步骤可能需要已经有交易才能在浏览器查询。

至此,可以确认合约部署成功。

3.token的交易

3.1 查看合约状态

点击部署完毕的合约进度条上的“Token Demo (管理页面)”,进入合约管理界面。

管理界面的上半部分显示基本信息,顶部是合约地址,右边有通用功能按钮。主要包括:接收墨客和token、浏览器查看、拷贝地址、二维码和显示接口。

点击“显示接口”,跳出如下界面,显示“合约JSON接口”。提示如果有其他人需要管理或操作这个智能合约,则需要把接口内容连同合约地址一并发送给他。

管理界面的下半部分显示合约自身功能。

其中左边显示合约的读操作,本例中主要包括name、根据tokenID查询拥有者账户、根据账户查询余额等。

右边为对合约的写操作,简单理解就是合约中的public函数。

本例中,当前没有创建token,因此该合约中的721token数为0。

3.2 创建第一个erc721数字资产

本例中,创建token是一个写合约的操作,在右边select function下拉框选中“Create Token”,填入参数和拥有者地址,点击“EXECUTE”。

此处的参数property就是数据结构里的内容,用户可以根据自己的实际需要在合约里写入多个字段。

界面提示“transaction send”。

如果此处提示需要解锁账户,请参照前面的步骤执行解锁操作。

写入区块后,就可以在读操作界面进行查询了。如下图所示:

上图中,在index界面输入0,显示拥有该合约第一个721token(合约index编号从0开始,每次创建时,index编号自动递增)的地址。本例为主账户。

在owner中输入一个账号,自动显示该账号当前拥有的token数量。

3.3 交易erc721数字资产

本例中,发送token是一个写合约的操作,在右边select function下拉框选中“Transfer”,填入接收地址和发送的index编号,选择发送账户,点击“EXECUTE”。如果该账户拥有该token,则会发送成功;如果该账户不拥有该token,此处的操作也没有报错信息,只是不会有交易真实发生。

交易成功后,左边的合约读操作会自动更新。index编号为0的token有了新的拥有者,而之前的拥有者的token数量会自动减少1个,新的拥有者的数量会自动增加1个。

在主界面,将显示你的所有账户及它们所拥有的数字资产。

至此,一个典型的ERC-721智能合约部署完成。

ERC-721智能合约可以与实际生活中的唯一性资产对应,有着广阔的应用前景。

附件:

本文所用合约代码TestToken721.sol。

注意:该代码为测试实例使用,非通用智能合约代码。

pragma solidity ^0.4.16;

contract ERC721Token {
  //Non-fungible Token
  //与ERC20兼容的接口
  function balanceOf(address _owner) public constant returns (uint256 balance);  
  //所有权相关的接口
  function transfer(address _to, uint256 _token) public returns (bool success);  
  function transferFrom(address _from, address _to, uint256 _token) public returns (bool success);
  function approve(address _spender, uint256 _token) public returns (bool success);
  function allowance(address _owner, address _spender) public constant returns (uint256 remaining);  
  function ownerOf(uint256 _tokenId) public returns (address owner);  
  function tokensOfOwner(address _owner) public returns(uint256[] ownerTokens);  
  //元数据接口,可选
  function tokenMetadata(uint256 _tokenId) public constant returns (string infoUrl);  
  //事件
  event Transfer(address indexed _from, address indexed _to, uint256 _token);  
  event Approval(address indexed _owner, address indexed _spender, uint256 _token);
  
}

contract TokenDemo is ERC721Token {

  string  public name = "Test Token";    
  string  public symbol = "TEST";		 
  uint8   public decimals = 0;
  uint256 public INITIAL_SUPPLY = 100 * (10 ** uint256(decimals));  
  
  address minter;

  mapping (address => uint256) balances;
  mapping (address => mapping (address => uint256)) allowed;

  //token structure 
  struct Token {			//users can change structure for their dapp				
      string property;     
  }

  Token[] tokens;

  mapping (uint256 => address) public tokenIndexToOwner;
  mapping (address => uint256) ownershipTokenCount;
  mapping (uint256 => address) public tokenIndexToApproved;  
  mapping (uint256 => string) tokenLinks;  //元数据,例如IPFS哈希或HTTP(S)链接

  function TokenDemo() public {
	  minter = msg.sender;
  }

  //通证元数据接口
  function tokenMetadata(uint256 _tokenId) public constant returns (string infoUrl){
	  return tokenLinks[_tokenId];
  }
  
  function getProperty(uint256 _tokenId) public returns (string property){
      return tokens[_tokenId].property;
  }
 
  function totalSupply() public returns (uint) {
      return tokens.length;
  }

  function ownerOf(uint256 _tokenId)  public returns (address owner) {
      owner = tokenIndexToOwner[_tokenId];
      require(owner != address(0));
  }

  function tokensOfOwner(address _owner) public returns(uint256[] ownerTokens) {
      uint256 tokenCount = balanceOf(_owner);

      if (tokenCount == 0) {
          return new uint256[](0);
      } else {
          uint256[] memory result = new uint256[](tokenCount);
          uint256 totalTokens = totalSupply();
          uint256 resultIndex = 0;

          uint256 tokenId;
          for (tokenId = 0; tokenId < totalTokens; tokenId++) {
              if (tokenIndexToOwner[tokenId] == _owner) {
                  result[resultIndex] = tokenId;
                  resultIndex++;
              }
          }
          return result;
      }
  }

  function _transfer(address _from, address _to, uint256 _tokenId) internal {
      ownershipTokenCount[_to]++;
      tokenIndexToOwner[_tokenId] = _to;
      if (_from != address(0)) {
          ownershipTokenCount[_from]--;
          delete tokenIndexToApproved[_tokenId];
      }
      
      Transfer(_from, _to, _tokenId);	// Emit the transfer event
  }

  function createToken(string _property, address _owner) public returns (uint) { 	
	  require(msg.sender == minter);		//only minter can create token		
  
      Token memory token = Token({
	    property: _property
      });
	
      var newTokenId = tokens.push(token) - 1;
      require(newTokenId == uint256(uint32(newTokenId)));
      _transfer(0, _owner, newTokenId);
      return newTokenId;
  }

  function _owns(address _claimant, uint256 _tokenId) internal view returns (bool) {
      return tokenIndexToOwner[_tokenId] == _claimant;
  }

  function transfer(address _to, uint256 _tokenId) public returns (bool) {
      require(_to != address(0));
      require(_to != address(this));
      require(_owns(msg.sender, _tokenId));
      _transfer(msg.sender, _to, _tokenId);
  }

  function transferFrom(address _from, address _to, uint256 _tokenId) public returns (bool success) {
      require(_to != address(0));
      require(_to != address(this));
      require(_approvedFor(msg.sender, _tokenId));
      require(_owns(_from, _tokenId));
      _transfer(_from, _to, _tokenId);
  }

  function balanceOf(address _owner) public view returns (uint256 count) {
      return ownershipTokenCount[_owner];
  }

  function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
      return tokenIndexToApproved[_tokenId] == _claimant;
  }

  function _approve(uint256 _tokenId, address _approved) internal {
      tokenIndexToApproved[_tokenId] = _approved;
  }

  function approve(address _to, uint256 _tokenId) public returns (bool success) {
      require(_owns(msg.sender, _tokenId));
      _approve(_tokenId, _to);
      Approval(msg.sender, _to, _tokenId);
  }

  function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
      return allowed[_owner][_spender];
  }
  
}

猜你喜欢

转载自blog.csdn.net/lyq13573221675/article/details/81109474