实现NEP-5
目的: 了解NEP-5的基本概念
要点:
根据NEP-5标准来实现每个要点
在NEO-Gui上使用NEP-5通证
首先,我们定义一个只读属性Owner来表示合约的所有者。如下所示,Owner
是一个字节数组,长度为 20
。
// 这里,字符串“xxx”表示所指定的作为所有者的地址。
private static readonly byte[] Owner = "xxxxxxxxxxxxxxxxxxxxx".ToScriptHash(); //所有者地址
现在我们开始学习一下main方法和触发器:
public static object Main(string method, object[] args){
if (Runtime.Trigger == TriggerType.Verification)
{
return Runtime.CheckWitness(Owner);
}
else if (Runtime.Trigger == TriggerType.Application)
{
return true;
}
}
这里,main方法接受两个参数。第一个是代表方法名的字符串 method
,它是用户将在这个智能合约中调用的nep-5方法。第二个是数组对象 args
,它表示nep-5方法中使用的参数列表。
这里我们也会对触发器类型进行判断。当触发器类型是 Verification
时,它意味着终端用户使用资产交易来调用交易。换句话说,终端用户可能希望将NEO或GAS等全局资产发送到该合约或者从该合约获取这些资产。在这种情况下,我们应该判断调用者(或对合约进行签名的地址)是否是所有者。
当触发器类型是 Application
时,这意味着应用程序(Web/App)正在通过InvocationTransaction来调用智能合约。在这种情况下,我们应该根据方法值调用其他函数。这部分内容我们之后会补充。
然后我们定义表示名称、符号、小数位的相应函数,这些对于这个合约而言都是固定值。
[DisplayName("name")]
public static string Name() => "MyToken"; //通证的名称
[DisplayName("decimals")]
public static byte Decimals() => 8;
[DisplayName("symbol")]
public static string Symbol() => "MYT"; //通证的符号
我们还需要定义一个转账事件,该事件也在 NEP-5
标准中指定了。
[DisplayName("transfer")]
public static event Action<byte[], byte[], BigInteger> Transferred;
现在。让我们定义合约的的totalSupply方法。在此之前,我们应该首先定义一个 deploy
方法。deploy方法没有在 NEP-5
标准中指定,但是应该是智能合约所有者调用的第一个函数,并且只能调用一次。deploy函数的目的是设置 NEP-5
通证的 总供应量
值,并将所有通证转入到所有者的账户余额中。
需要注意的是,在通证化的智能合约中,资产以键值对的形式存储在存储区中,其中键是地址,值是余额。下面的表格可以说明这一点。
地址 | 值 |
---|---|
address1 | 1000 |
address2 | 200 |
//代表总供应量的静态只读值
private static readonly BigInteger TotalSupplyValue = 10000000000000000;
[DisplayName("deploy")]
public static bool Deploy()
{
if (TotalSupply() != 0) return false;
StorageMap contract = Storage.CurrentContext.CreateMap(nameof(contract));
contract.Put("totalSupply", TotalSupplyValue);
StorageMap asset = Storage.CurrentContext.CreateMap(nameof(asset));
//合约所有者拥有所有的nep-5通证
asset.Put(Owner, TotalSupplyValue);
// 当发生NEP-5资产转账时触发该事件
Transferred(null, Owner, TotalSupplyValue);
return true;
}
现在,我们已经在部署阶段定义了总供应量,我们可以实现totalSupply方法了,该方法从存储区中获取totalSupply值。
[DisplayName("totalSupply")]
public static BigInteger TotalSupply()
{
StorageMap contract = Storage.CurrentContext.CreateMap(nameof(contract));
return contract.Get("totalSupply").AsBigInteger();
}
让我们设置另一个 balanceOf
方法,它获取指定地址所对应账户的 NEP-5
余额。
[DisplayName("balanceOf")]
public static BigInteger BalanceOf(byte[] account)
{
// 参数校验
if (account.Length != 20)
throw new InvalidOperationException("The parameter account SHOULD be 20-byte addresses.");
StorageMap asset = Storage.CurrentContext.CreateMap(nameof(asset));
return asset.Get(account).AsBigInteger();
}
现在,除了转账方法之外,我们几乎已经完成了 NEP-5
标准中要求的所有方法,让我们先填充一下main方法。
public static object Main(string method, object[] args)
{
if (Runtime.Trigger == TriggerType.Verification)
{
return Runtime.CheckWitness(Owner);
}
else if (Runtime.Trigger == TriggerType.Application)
{
if (method == "balanceOf") return BalanceOf((byte[])args[0]);
if (method == "decimals") return Decimals();
if (method == "name") return Name();
if (method == "symbol") return Symbol();
if (method == "supportedStandards") return SupportedStandards();
if (method == "totalSupply") return TotalSupply();
if (method == "transfer") return Transfer((byte[])args[0], (byte[])args[1], (BigInteger)args[2]);
}
return false;
}
现在,剩下的唯一方法就是转账函数。转账函数首先要做的是检查参数,并检查合约调用者是否是合约的所有者。如果满足所有要求,就从存储区中获取 from
地址所对应账户中的余额,并检查该余额是否能够满足转账的数量要求。如果 NEP-5
通证的数量足够,则计算并更新 from
账户和 to
账户中的余额。
private static bool Transfer(byte[] from, byte[] to, BigInteger amount, byte[] callscript)
{
//参数校验
if (from.Length != 20 || to.Length != 20)
throw new InvalidOperationException("The parameters from and to SHOULD be 20-byte addresses.");
if (amount <= 0)
throw new InvalidOperationException("The parameter amount MUST be greater than 0.");
if (!Runtime.CheckWitness(from))
return false;
StorageMap asset = Storage.CurrentContext.CreateMap(nameof(asset));
var fromAmount = asset.Get(from).AsBigInteger();
if (fromAmount < amount)
return false;
if (from == to)
return true;
//减少发送方的账户余额
if (fromAmount == amount)
asset.Delete(from);
else
asset.Put(from, fromAmount - amount);
//增加接收方的账户余额
var toAmount = asset.Get(to).AsBigInteger();
asset.Put(to, toAmount + amount);
Transferred(from, to, amount);
return true;
}
现在NEP-5通证已经完成,可以在我们的私有链上测试了。你可以点击此处查看源码。
编译NEP5.cs并获得avm文件后,部署它。如果合约已经部署在区块链上的话,那么首先调用deploy方法,该方法会初始化通证的totalSupply变量。
现在,在NEP-GUI中单击 高级
-> 选项
,添加合约的scriptHash,你可以在资产选项卡中查看NEP-5资产。
让我们测试一下NEP-5标准中的另一个转账方法。打开调用功能选项卡并填写相应的参数。字符串部分是要调用的智能合约方法。这里是 transfer
。在数组中,参数是 from
、to
、amount
。from
地址和 to
地址的格式是字节数组,可以由钱包地址进行转换。例如,点击这个工具链接,它可以将钱包地址转换成字节数组。对于 amount
参数,不要忘了将小数乘以10^8。这里我想把我的NEP-5通证从我的账户转250000000个到其他账户。
待交易被记录且确认后,打开目标地址的钱包,查看更新后的NEP-5余额。
作业
自己定义一个新的NEP-5通证。
下一步骤
在本教程中,你已经了解了NEP-5的标准,以及如何通过定义自己的通证来实现NEP-5标准。现在我们可以扩展这一步骤来发起我们的通证,并公开发布。