Skip to main content

合约部署与调用

由于在 Neo N3 中绝大部分功能都是通过合约提供的,所以理解合约是使用 Neo N3 的基础,每个合约由脚本哈希 (ScriptHash) 作为唯一标识,通常也是调用合约的必须参数。本文档主要介绍以下 SDK 功能:

  • 提供了合约部署交易的构建方法
  • 使用只读模式调用合约中方法
  • Nep17API 类封装了调用 NEP17 合约相关的方法

合约部署#

ContractClient 中提供了合约部署交易的构建方法 CreateDeployContractTxAsync, 参数为合约脚本,manifest 和支付系统费和网络费的账户密钥对,其中合约脚本和 manifest 可通过编译获取,账户中需要有足够的 GAS 支付所需费用。

读取合约 nef 和 manifest.json 文件:

// read nefFile & manifestFileNefFile 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 transactionTransaction transaction = await contractClient.CreateDeployContractTxAsync(nefFile.ToArray(), manifest, senderKeyPair);

交易构建后需要广播到链上:

// Broadcast the transaction over the Neo networkawait client.SendRawTransactionAsync(transaction);Console.WriteLine($"Transaction {transaction.Hash.ToString()} is broadcasted!");

等待交易上链后可以获取交易的执行状态以确保合约部署成功:

// print a message after the transaction is on chainWalletAPI 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 openedRpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json"));ContractClient contractClient = new ContractClient(client);
// get the contract hashUInt160 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 openedRpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json"));
// get the contract hashUInt160 scriptHash = NativeContract.NEO.Hash;
byte[] script = scriptHash.MakeScript("name");// call invoke scriptRpcInvokeResult invokeResult = await client.InvokeScriptAsync(script).ConfigureAwait(false);Console.WriteLine($"The name is {invokeResult.Stack.Single().GetString()}");

合约调用(上链交易)#

上链的合约调用一般要经过以下步骤:

  1. 构建调用脚本

    以调用原生合约 NEO 的 transfer 方法为例:

    // construct the script, in this example, we will transfer 1024 NEO to receiverUInt160 scriptHash = NativeContract.NEO.Hash;byte[] script = scriptHash.MakeScript("transfer", sender, receiver, 1024);
  2. 构建交易:

    // initialize the TransactionManagerFactory with rpc client and magic// fill the script and cosignersTransactionManager txManager = await new TransactionManagerFactory(client, 5195086)    .MakeTransactionAsync(script, cosigners).ConfigureAwait(false);// add signature and sign transaction with the added signatureTransaction tx = await txManager.AddSignature(sendKey).SignAsync().ConfigureAwait(false);
  3. 交易构造后广播到链上:

    // broadcasts the transaction over the Neo networkawait client.SendRawTransactionAsync(tx).ConfigureAwait(false);
  4. 等待交易上链后,获取交易的执行状态以确保合约调用成功:

    // print a message after the transaction is on chainWalletAPI 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 namestring name = await nep17API.NameAsync(NativeContract.NEO.Hash).ConfigureAwait(false);
// get nep17 symbolstring symbol = await nep17API.SymbolAsync(NativeContract.NEO.Hash).ConfigureAwait(false);
// get nep17 token decimalsbyte decimals = await nep17API.DecimalsAsync(NativeContract.NEO.Hash).ConfigureAwait(false);
// get nep17 token total supplyBigInteger totalSupply = await nep17API.TotalSupplyAsync(NativeContract.NEO.Hash).ConfigureAwait(false);
// get the balance of nep17 tokenUInt160 account = Utility.GetScriptHash("NXjtqYERuvSWGawjVux8UerNejvwdYg7eE");BigInteger balance = await nep17API.BalanceOfAsync(NativeContract.NEO.Hash, account).ConfigureAwait(false);
// get token informationRpcNep17TokenInfo tokenInfo = await nep17API.GetTokenInfoAsync(NativeContract.NEO.Hash).ConfigureAwait(false);