This is the old Algorand Developer Portal. Please head over to dev.algorand.co to explore our newly rebuilt documentation site. Please excuse us as we continue to transition content to the new portal

创建文章

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Solution Thumbnail

使用.net在Algorand区块链上进行NFT开发

简介

本解决方案的视频版请参照:https://www.bilibili.com/video/BV1s54y1h7oB/

最近NFT越来越流行了。如果你是一个区块链的爱好者或者从业人员连NFT是什么都不知道,那你是真的落伍了。那么在Algorand区块链上能否进行NFT的开发呢?今天我们就来探讨一下这个问题。

一、基本概念

但为了使所有读者都能更好的理解此解决方案,我先介绍一下什么是NFT。

我先举一个例子。小明是一个杀猪的,他赚了100块钱。而我呢,我是一名工程师,也赚了100块钱。那么小明的这100块钱和我的这100块钱有没有什么区别呢?绝大多数情况下,这两个100块钱是完全没有任何区别的。这种情况就叫做同质化货币。但是有一些东西就不大一样。比方说我家的一只小狗和小明家的一只小狗,即使品种一样,然后长得也差不多,但是我们还是不愿意互换。这两者之间是有很大区别的,我们可以说在世界上没有两只相同的小狗。这种情况下我们叫非同质化代币(Non-Fungible Token),也就是NFT。说白了NFT是一种代币,但每一个NFT拥有独特且唯一的标识,两两不可互换,最小单位是1且不可分割。

NFT的用处还是非常广泛的,例如在游戏中,我们可以把装备设计的更具各性化,并让玩家真正的“拥有”这件装备,那么此时最好的做法就是将这件装备设计成NFT。再比如说数字产品的知识产权保护:数字产品虽然可以复制,但数字产品的创作者是其唯一的拥有者,而且拥有者也是可以转让的。这些情景都非常适合使用NFT对其进行数字化。

我们都知道algorand是实现了标准化资产(ASA)的,那么algorand里面有没有实现NFT呢?实际上是没有的,但我们可以通过一定手段实现类似于NFT的代币。下面就让我们一起使用dotnet-algorand-sdk来实现一个NFT代币并进行一些相关操作吧。

二、NFT设计概述

Algorand是支持智能合约的,所以你当然可以使用智能合约来实现NFT。但我们也可以根据algorand的系统中已经设计好的ASA来加以改造,来生成更加安全、稳定的NFT类代币。

图一:Algorand区块连中ASA与NFT特征的相关性

图一:Algorand区块连中ASA与NFT特征的相关性

我们可以把NFT当作一个总数量为1且不可再分的代币,这样ASA就有了唯一性。

对于现在非常流行的数字艺术品的应用,我们可以再对ASA稍加改进,使之能够存储相关的电子文件。电子文件的存储与需要配合IPFS系统完成的,具体做法如下:

  1. 生成文件(通常为图片)

  2. 上传到IPFS中(返回32字节的hash)

  3. 将Hash写入到 MetaDataHash 字段(由于Algorand设计的限制,其实这里只使用了步骤2中hash的前24个字节)

  4. 将IPFS地址写入到URL字段(由于Algorand设计限制,URL长度只能在32字节以内,所以这里可能需要使用地址缩短的服务)

  5. 生成NFT

NFT生成需要注意的问题:

  1. NFT真伪鉴别。在以太坊网络中,一种ERC721代币可以生成若干个NFT代币,我们也可以根据这种代币的合约地址来判别一个NFT代币是否为“真币”。比方说,一副名家画作在以太坊中的电子拷贝被制作成了一个NFT,那么我们可以通过辨认其智能合约地址来判断是不是“真品”。而在Algorand中,并没有合约地址这一说,与之功能基本类似的是Asset ID。在Algorand区块链中,你只能通过Asset ID来判断一个NFT的真伪。任何人都可以发布相同名字、缩写、MeatdataHash及URL都相同的代币,甚至一个账号也可以创建多个任何信息(除了Asset ID)都相同的NFT。

  2. 在Algorand网络中,一个账号想要接收一种ASA,必须进行Opt In操作。而每一枚NFT其实都是一种ASA资产。这就造成了一个问题,那就是一个用户在接收一个NFT之前,必须对这种资产进行Opt In操作。

毕竟ASA在设计之初并没有考虑到NFT的问题,所以使用ASA来实现NFT功能时难免会出现各种问题。如果大家发现新的问题或者更有趣的解决方案,请随时与我讨论。

三、程序设计概述

为了便于大家使用,本文会设计一个简单的应用程序,实现NFT的生成、Opt In、转移、查询NFT相关属性等操作。为了简单起见,将程序设计成无界面的命令行行式,具体功能描述如下:

  1. 根据输入的相关参数生成NFT。

  2. Opt In NFT代币

  3. 查询账号持有的NFT(根据创建者)

  4. 查询具体NFT的详细信息

  5. NFT转账

使用界面如下:

图二:程序界面

图二、程序界面

关于软件更具体的使用方法,会在本解决方案的第五节:操作模拟中进行更详细的介绍。

四、NFT相关操作

本章讲述有关NFT相关操作的代码。从本质上讲,NFT是一种ASA,所以如果大家有学习过之前的教程《使用.net进行Algorand开发系列教程之ASA转账》和《使用.net进行Algorand开发系列教程之ASA的创建及管理》,那么大家会对后面的操作非常熟悉。后面主要讲述了NFT的创建、NFT的Opt In及NFT转账。

4.1 NTF的创建

NFT是一种有特定特征的ASA,所以NFT的创建与ASA的创建大同小异,主要代码如下:

static long? CreateNFT(string nftName, string nftUrl, string metadataHash)
{
  if(act.Address.ToString() != reliableAddress)
  {

    Console.WriteLine("只有可信赖的地址生成的NFT会被本程序识别!");
    return 0;
  }

  var transParams = algodApiInstance.TransactionParams();

  // Create the Asset
  // Total number of this asset available for circulation
  //var ap = new AssetParams(creator: act.Address.ToString(), name: nftName, unitName: "NFT", total: 1,
  //  decimals: 0, url: nftUrl, metadataHash: Encoding.ASCII.GetBytes(metadataHash))

  var ap = new AssetParams(creator: act.Address.ToString(), name: nftName, unitName: "NFT", total: 1, decimals: 0, url: nftUrl, 
    metadataHash: Encoding.ASCII.GetBytes(StrToHexByte(metadataHash.Substring(0, 48))));

  var tx = Utils.GetCreateAssetTransaction(ap, transParams, "NFT creation transaction");

  // Sign the Transaction by sender
  SignedTransaction signedTx = act.SignTransaction(tx);

  // send the transaction to the network and
  // wait for the transaction to be confirmed
  long? assetID = 0;
  try
  {
    var id = Utils.SubmitTransaction(algodApiInstance, signedTx);
    Console.WriteLine("Transaction ID: " + id);
    Console.WriteLine("Confirmed Round is: " +
      Utils.WaitTransactionToComplete(algodApiInstance, id.TxId).ConfirmedRound);

    // Now that the transaction is confirmed we can get the assetID
    var ptx = algodApiInstance.PendingTransactionInformation(id.TxId);
    assetID = ptx.AssetIndex;
  }
  catch (Exception e)
  {
    Console.WriteLine(e.StackTrace);
    return 0;
  }

  Console.WriteLine("AssetID = " + assetID);
  // now the asset already created
  return assetID;
}

4.2 NFT的Opt In操作

static void OptInNFT(long? assetID)
{
  var transParams = algodApiInstance.TransactionParams();   
  var tx = Utils.GetAssetOptingInTransaction(act.Address, assetID, transParams, "opt in transaction"); 
  var signedTx = act.SignTransaction(tx);

  try
  {
    var id = Utils.SubmitTransaction(algodApiInstance, signedTx);
    Console.WriteLine("Transaction ID: " + id.TxId);
    Console.WriteLine("Confirmed Round is: " +
      Utils.WaitTransactionToComplete(algodApiInstance, id.TxId).ConfirmedRound);

    // We can now list the account information for acct3 
    // and see that it can accept the new asseet
    var actInfo = algodApiInstance.AccountInformation(act.Address.ToString());
    Console.WriteLine(actInfo);
  }
  catch (Exception e)
  {
    Console.WriteLine(e.Message);
    return;
  }
}

4.3 NFT转账

private static void TransferNFT(long? aid, string desAddress)
{
  var transParams = algodApiInstance.TransactionParams();
  ulong assetAmount = 1;
  var tx = Utils.GetTransferAssetTransaction(act.Address, new Address(desAddress), aid, assetAmount, transParams, null, "transfer message");

  var signedTx = act.SignTransaction(tx);
  try
  {
    var id = Utils.SubmitTransaction(algodApiInstance, signedTx);
    Console.WriteLine("Transaction ID: " + id.TxId);
    Console.WriteLine("Confirmed Round is: " +
      Utils.WaitTransactionToComplete(algodApiInstance, id.TxId).ConfirmedRound);
  }
  catch (Exception e)
  {
    //e.printStackTrace();
    Console.WriteLine(e.Message);
    return;
  }
}

注:以上的代码片断是无法独立运行的,只是作为程序的一部分完成相应的操作。如果你需要完整的代码,请参照https://github.com/RileyGe/algorand-nft

五、操作模拟

经过上面的编码工作,现在我们已经初步有了一个可以运行的基于Algorand的NFT操作软件了,下面以act1和act2两个用户来进行相关操作,并记录相关过程:

act1信息:

账号地址:7XVBE6T6FMUR6TI2XGSVSOPJHKQE2SDVPMFA3QUZNWM7IY6D4K2L23ZN2A

账号助记词:spray daughter bar job flush vessel yellow galaxy below neutral they elbow cereal short can crime resource depend social history enact merge lesson abstract brick

act2信息:

账号地址:AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU

账号助记词:place blouse sad pigeon wing warrior wild script problem team blouse camp soldier breeze twist mother vanish public glass code arrow execute convince ability there

操作模拟的具体流程如下:

图三:操作模拟的具体流程

图三、操作模拟的具体流程

首先需要打开程序,程序后面紧跟两个变量,分别表示Algorand区块链的接入点和秘钥。大家可以自建节点,也可以使用Algorand的BSN服务,但要注意purestake的服务部分功能不可用。

后续需要根据提示输入助记词来解锁账号(注,这里只是演示用,在实际生产环境中需要更好的保存助记词)。

图四:act1创建NFT

图四:act1创建NFT

该操作的详细结果可以参照Algorand浏览器:https://testnet.algoexplorer.io/tx/3QN4NK6DVTTUDQGD6KTPGVGOON3RPJ4GKJQKXN66ZUEVHRKXDEUQ

之后就可以进行NFT的查询。查询是按照以下规则进行的:

  1. 资产的创建者为act1

  2. 资产的UnitName为NFT

  3. 资产的总量为1

  4. 资产不可分

满足上面4个条件就被认为是NFT,查询结果如下:

图五:act1查询NFT列表

图五:act1查询NFT列表

对于每一个NFT都可以查看其详细信息:

图六:查询NFT的详细信息

图六:查询NFT的详细信息

在进行act2的操作时必须重新打开一个命令行(之前的命令行不需要关闭),然后根据提示输入act2的助记词,Opt In的界面如下:

图七:act2的Opt_In操作

图七:act2的Opt In操作

在act2进行了Opt In操作之后,act1就可以将自己有的NFT发送给act2了,操作界面如下:

图八:act1向act2进行NFT转账

图八:act1向act2进行NFT转账

转账操作的详细信息可以参照:https://testnet.algoexplorer.io/tx/BO7VRN3TVCQ424N75JCZAXENPDBQD3GZD3R6S5GQLHSUUCEZULSQ

转账后act2就拥有了NFT,act2进行查询的操作界面如下:

图九:act1的NFT资产列表

图九:act1的NFT资产列表

图十:act2查询NFT的详细信息

图十:act2查询NFT的详细信息

上述操作涵盖了NFT的一些基本操作,大家可以根据自己实际需求定制更加复杂的操作。

六、总结

本文主要阐述了如何应用 Algorand 区块链来构建NFT代币,并在自己的Dapp中使用NFT代币。

Algorand 区块链的NFT代币基于其标准资产ASA,继承了ASA的易用性与安全性。我们只需要少量的代码就可以构建出一个NFT资产。但同时也由于ASA并没有专门为NFT进行设计,所以基于Algorand区块链标准资产的NFT也有其局限性:如每个账户要进行NFT资产接收之前都要进行Opt In操作、代币中可存储的信息量不足(如无法保存原url地址等)等。

即使存在以上问题,我们仍然可以基于Algorand区块链构建NFT并进行更多创造性的工作。本文只是一个引子,希望能抛砖引玉,引出更多更好的应用。