合约部署与调用
由于在 Neo N3 中绝大部分功能都是通过合约提供的,所以理解合约是使用 Neo N3 的基础,每个合约由脚本哈希 (ScriptHash) 作为唯一标识,通常也是调用合约的必须参数。本文档主要介绍以下 SDK 功能:
-
提供了合约部署交易的构建方法
-
使用只读模式调用合约中方法
-
Nep17API类封装了调用 NEP17 合约相关的方法
合约部署
ContractClient 中提供了合约部署交易的构建方法 CreateDeployContractTxAsync , 参数为合约脚本,manifest 和支付系统费和网络费的账户密钥对,其中合约脚本和 manifest 可通过编译获取,账户中需要有足够的 GAS 支付所需费用。
读取合约 nef 和 manifest.json 文件:
// read nefFile & manifestFile
NefFile nefFile;
using (var stream = new BinaryReader(File.OpenRead(nefFilePath), Encoding.UTF8, false))
{
nefFile = stream.ReadSerializable<NefFile>();
}
ContractManifest manifest = ContractManifest.Parse(File.ReadAllBytes(manifestFilePath));
构造发布合约的交易:
// create the deploy contract transaction
Transaction transaction = await contractClient.CreateDeployContractTxAsync(nefFile.ToArray(), manifest, senderKeyPair);
交易构建后需要广播到链上:
// Broadcast the transaction over the Neo network
await client.SendRawTransactionAsync(transaction);
Console.WriteLine($"Transaction {transaction.Hash.ToString()} is broadcasted!");
等待交易上链后可以获取交易的执行状态以确保合约部署成功:
// print a message after the transaction is on chain
WalletAPI neoAPI = new WalletAPI(client);
await neoAPI.WaitTransactionAsync(transaction)
.ContinueWith(async (p) => Console.WriteLine($"Transaction vm state is {(await p).VMState}"));
下面是完整的示例:
using Neo.Network.P2P.Payloads;
using Neo.Network.RPC;
using Neo.SmartContract;
using Neo.SmartContract.Manifest;
using Neo.Wallets;
using System;
using Neo.IO;
using System.IO;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Test().GetAwaiter().GetResult();
Console.Read();
}
private static async Task Test()
{
// choose a neo node with rpc opened, here we use the localhost
RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json"));
ContractClient contractClient = new ContractClient(client);
string nefFilePath = "sc/Contract1.nef";
string manifestFilePath = "sc/Contract1.manifest.json";
// read nefFile & manifestFile
NefFile nefFile;
using (var stream = new BinaryReader(File.OpenRead(nefFilePath), Encoding.UTF8, false))
{
nefFile = stream.ReadSerializable<NefFile>();
}
ContractManifest manifest = ContractManifest.Parse(File.ReadAllBytes(manifestFilePath));
// deploying contract needs sender to pay the system fee
KeyPair senderKey = Utility.GetKeyPair("L53tg72Az8QhYUAyyqTQ3LaXMXBE3S9mJGGZVKHBryZxya7prwhZ");
// create the deploy transaction
Transaction transaction = await contractClient.CreateDeployContractTxAsync(nefFile.ToArray(), manifest, senderKey).ConfigureAwait(false);
// Broadcast the transaction over the NEO network
await client.SendRawTransactionAsync(transaction).ConfigureAwait(false);
Console.WriteLine($"Transaction {transaction.Hash.ToString()} is broadcasted!");
// print a message after the transaction is on chain
WalletAPI neoAPI = new WalletAPI(client);
await neoAPI.WaitTransactionAsync(transaction)
.ContinueWith(async (p) => Console.WriteLine($"Transaction vm state is {(await p).VMState}"));
}
}
}
合约模拟调用
ContractClient 提供了 TestInvokeAsync 方法来对合约进行模拟调用,执行后不会影响链上数据。可以直接调用读取信息的合约方法,比如下面的例子调用了NEO原生合约中的name方法
// choose a neo node with rpc opened
RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json"));
ContractClient contractClient = new ContractClient(client);
// get the contract hash
UInt160 scriptHash = NativeContract.NEO.Hash;
// test invoking the method provided by the contract
RpcInvokeResult invokeResult = await contractClient.TestInvokeAsync(scriptHash, "name").ConfigureAwait(false);
Console.WriteLine($"The name is {invokeResult.Stack.Single().GetString()}");
或者使用 MakeScript 构造想要执行的脚本,再调用 RpcClient 中的方法 InvokeScriptAsync 获取执行结果
// choose a neo node with rpc opened
RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json"));
// get the contract hash
UInt160 scriptHash = NativeContract.NEO.Hash;
byte[] script = scriptHash.MakeScript("name");
// call invoke script
RpcInvokeResult invokeResult = await client.InvokeScriptAsync(script).ConfigureAwait(false);
Console.WriteLine($"The name is {invokeResult.Stack.Single().GetString()}");
合约调用(上链交易)
上链的合约调用一般要经过以下步骤:
-
构建调用脚本
以调用原生合约 NEO 的
transfer方法为例:// construct the script, in this example, we will transfer 1024 NEO to receiver UInt160 scriptHash = NativeContract.NEO.Hash; byte[] script = scriptHash.MakeScript("transfer", sender, receiver, 1024); -
构建交易:
// initialize the TransactionManagerFactory with rpc client and magic // fill the script and cosigners TransactionManager txManager = await new TransactionManagerFactory(client, 5195086) .MakeTransactionAsync(script, cosigners).ConfigureAwait(false); // add signature and sign transaction with the added signature Transaction tx = await txManager.AddSignature(sendKey).SignAsync().ConfigureAwait(false); -
交易构造后广播到链上:
// broadcasts the transaction over the Neo network await client.SendRawTransactionAsync(tx).ConfigureAwait(false); -
等待交易上链后,获取交易的执行状态以确保合约调用成功:
// print a message after the transaction is on chain WalletAPI neoAPI = new WalletAPI(client); await neoAPI.WaitTransactionAsync(tx) .ContinueWith(async (p) => Console.WriteLine($"Transaction vm state is {(await p).VMState}"));
完整示例请参考 构造交易 。
NEP17 合约
Nep17API 封装了转账交易的生成方法,以上交易过程可简化为:
Nep17API nep17API = new Nep17API(client);
Transaction tx = await nep17API.CreateTransferTxAsync(scriptHash, sendKey, receiver, 1).ConfigureAwait(false);
此外 Nep17API 还提供了以下读取方法:
// get nep17 name
string name = await nep17API.NameAsync(NativeContract.NEO.Hash).ConfigureAwait(false);
// get nep17 symbol
string symbol = await nep17API.SymbolAsync(NativeContract.NEO.Hash).ConfigureAwait(false);
// get nep17 token decimals
byte decimals = await nep17API.DecimalsAsync(NativeContract.NEO.Hash).ConfigureAwait(false);
// get nep17 token total supply
BigInteger totalSupply = await nep17API.TotalSupplyAsync(NativeContract.NEO.Hash).ConfigureAwait(false);
// get the balance of nep17 token
UInt160 account = Utility.GetScriptHash("NXjtqYERuvSWGawjVux8UerNejvwdYg7eE");
BigInteger balance = await nep17API.BalanceOfAsync(NativeContract.NEO.Hash, account).ConfigureAwait(false);
// get token information
RpcNep17TokenInfo tokenInfo = await nep17API.GetTokenInfoAsync(NativeContract.NEO.Hash).ConfigureAwait(false);