Skip to main content

钱包相关接口

Neo 中大部分的操作与账户有关,而钱包是账户的集合,包含一个或多个账户。本篇文档将主要介绍以下内容:

  • 账户与钱包的基本概念和操作;
  • 使用 WalletAPI,该方法封装了钱包相关接口,提供余额查询、GAS 提取、转账等功能。

账户与钱包基础#

账户#

Neo 中的账户是用户身份的证明,本质上是一个 KeyPair 类型的密钥对,其中包含私钥和公钥。

// create a new KeyPairbyte[] privateKey = new byte[32];using (RandomNumberGenerator rng = RandomNumberGenerator.Create()){    rng.GetBytes(privateKey);}KeyPair keyPair = new KeyPair(privateKey);

私钥#

私钥用于对交易签名,是授权的工具,掌握了私钥就表示拥有某个账户,可以处理账户的所有资产。私钥本质上是一个32位长的byte数组,可以表示为十六进制的字符串,例如: "0x450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005"

  // export private key to hex string  string privateHex = keyPair.PrivateKey.ToHexString();
  // get KeyPair from private hex string  keyPair = Utility.GetKeyPair(privateHex);

WIF#

WIF是私钥的另一种字符串表示,与私钥在作用上是等价的,比如上面的私钥表示为WIF: "KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"

  // export KeyPair as WIF  string wif = keyPair.Export();
  // get KeyPair from WIF  KeyPair keyPair1 = Utility.GetKeyPair(wif);

公钥#

公钥可以用来验证私钥的签名,在Neo中对应ECPoint类型,拥有私钥的情况下可以计算出公钥,公钥一般可以表示为长度为66的十六进制字符串: "02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575"

  // export public key hex string  string publicHex = keyPair.PublicKey.ToString();
  // get public key from hex string  Neo.Cryptography.ECC.ECPoint publicKey = Neo.Cryptography.ECC.ECPoint.Parse(publicHex, Neo.Cryptography.ECC.ECCurve.Secp256r1);

账户的脚本哈希 (ScriptHash)#

ScriptHash 在 Neo 中对应 UInt160,本质上是一个 20 位的 byte 数组,由公钥经过脚本构造和哈希得出(由于哈希算法不可逆,所以不可根据脚本哈希逆向计算出公钥)。ScriptHash 一般表示为反序的十六进制字符串(大端序表示): "0xb0a31817c80ad5f87b6ed390ecb3f9d312f7ceb8"

  // get ScriptHash of KeyPair account  UInt160 scriptHash = Contract.CreateSignatureContract(keyPair.PublicKey).ScriptHash;  string strScriptHash = scriptHash.ToString();

地址#

地址是 ScriptHash 的另一种字符串表示,可以和 ScriptHash 互相转换。地址作为账户的唯一标识,是最常用的账户形式,相当于传统账户中的账号,比如转账时可以向指定地址转账。地址形式:"Ncm9TEzrp8SSer6Wa3UCSLTRnqzwVhCfuE"

using Neo.Wallets;
// ScriptHash to addressstring adddress = scriptHash.ToAddress(ProtocolSettings.Default.AddressVersion);// address to ScriptHashscriptHash = adddress.ToScriptHash(ProtocolSettings.Default.AddressVersion);

钱包#

钱包是账户的集合,NEP6 是Neo中最常用的钱包标准,NEP6 钱包可以序列化为一个 JSON 格式的文件,其中保存了加密后的账户私钥,需要对应的密码才能解密获取私钥。

可参考如下示例:

创建新的 NEP6 钱包,添加账户并保存为 JSON 文件:

// create walletstring path = "wallet_new.json";string password = "MyPass";NEP6Wallet wallet_new = new NEP6Wallet(path);using (wallet_new.Unlock(password)){    wallet_new.CreateAccount(keyPair.PrivateKey);}wallet_new.Save();

从 JSON 文件读取 NEP6 钱包,并解密账户:

// load wallet from nep6 walletNEP6Wallet wallet = new NEP6Wallet(path);KeyPair keyPair2;using (wallet.Unlock(password)){    keyPair2 = wallet.GetAccounts().First().GetKey();}

使用 WalletAPI#

初始化#

WalletAPI 初始化:

// choose a neo node with rpc openedRpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json"));WalletAPI walletAPI = new WalletAPI(client);

查询余额#

note

账户余额的类型一般是 BigInteger,这是把小数部分取整后的一种表示,需要除以 Factor 才能得出Token 的实际数量。

查询 NEP17 资产余额查询可以使用字符串参数:

// get the neo balance of accountstring tokenHash = NativeContract.NEO.Hash.ToString();string address = "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW";BigInteger balance = await walletAPI.GetTokenBalanceAsync(tokenHash, address).ConfigureAwait(false);

也可以使用 ScriptHash 类型的参数:

// Get the NEO balance of accountUInt160 tokenScriptHash = Utility.GetScriptHash(tokenHash, ProtocolSettings.Default);UInt160 accountHash = Utility.GetScriptHash(address, ProtocolSettings.Default);Nep17API nep17API = new Nep17API(client);BigInteger balance = await nep17API.BalanceOfAsync(tokenScriptHash, accountHash).ConfigureAwait(false);

在 Neo N3 中 NEO 和 GAS 都是 NEP17 资产,且脚本哈希固定,所以这里提供了更简单的接口:

// Get the NEO balanceuint neoBalance = await walletAPI.GetNeoBalanceAsync(address).ConfigureAwait(false);
// Get the GAS balancedecimal gasBalance = await walletAPI.GetGasBalanceAsync(address).ConfigureAwait(false);

提取 GAS#

在 Neo N3 中提取 GAS 的过程是在 NEO 转账时自动进行的,你可以构建一笔给自己转账的交易来提取 GAS。

  1. 首先查询当前地址可以提取的 GAS 数量,例如:

    // Get the claimable GAS of one addressstring address = "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW";decimal gasAmount = await walletAPI.GetUnclaimedGasAsync(address).ConfigureAwait(false);

    也可以使用账户的 ScriptHash 查询:

    string address = "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW";UInt160 accountHash = Utility.GetScriptHash(address);decimal gasAmount = await walletAPI.GetUnclaimedGasAsync(accountHash).ConfigureAwait(false);
  2. 构建一笔给自己转账的交易,自动提取 GAS:

    // Claiming GAS needs the KeyPair of account. You can also use wif or private key hex stringstring wif = "L1rFMTamZj85ENnqNLwmhXKAprHuqr1MxMHmCWCGiXGsAdQ2dnhb";Transaction transaction = await walletAPI.ClaimGasAsync(wif).ConfigureAwait(false);

    也可以使用KeyPair

    KeyPair keyPair = Utility.GetKeyPair(wif);Transaction transaction = await walletAPI.ClaimGasAsync(keyPair).ConfigureAwait(false);

资产转账#

WalletAPI 中封装了 NEP17 转账方法。

可以使用字符串参数:

string tokenHash = NativeContract.NEO.Hash.ToString();string wif = "L1rFMTamZj85ENnqNLwmhXKAprHuqr1MxMHmCWCGiXGsAdQ2dnhb";string address = "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW";
// Transfer 10 NEO from wif to addressawait walletAPI.TransferAsync(tokenHash, wif, address, 10).ConfigureAwait(false);
// 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}"));

也可以使用 KeyPairUInt160 (ScriptHash):

string wif = "L1rFMTamZj85ENnqNLwmhXKAprHuqr1MxMHmCWCGiXGsAdQ2dnhb";string address = "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW";
KeyPair sender = Utility.GetKeyPair(wif);UInt160 receiver = Utility.GetScriptHash(address, ProtocolSettings.Default);
// Transfer 10 NEO from wif to addressawait walletAPI.TransferAsync(NativeContract.NEO.Hash, sender, receiver, 10).ConfigureAwait(false);

多签账户的 NEP17 转账:

KeyPair receiverKey = Utility.GetKeyPair("L1rFMTamZj85ENnqNLwmhXKAprHuqr1MxMHmCWCGiXGsAdQ2dnhb");KeyPair keyPair1 = Utility.GetKeyPair("L1rFMTamZj85ENnqNLwmhXKAprHuqr1MxMHmCWCGiXGsAdQ2dnhb");KeyPair keyPair2 = Utility.GetKeyPair("L2ynA5aq6KPJjpisXb8pGXnRvgDqYVkgC2Rw85GM51B9W33YcdiZ");KeyPair keyPair3 = Utility.GetKeyPair("L3TbPZ3Gtqh3TTk2CWn44m9iiuUhBGZWoDJQuvVw5Zbx5NAjPbdb");KeyPair keyPair4 = Utility.GetKeyPair("L3Ke1RSBycXmRukv27L6o7sQWzDwDbFcbfR9oBBwXbCKHdBvb4ZM");
//make transaction Transaction tx = await walletAPI.TransferAsync(NativeContract.GAS.Hash, 3, new ECPoint[] { keyPair1.PublicKey, keyPair2.PublicKey, keyPair3.PublicKey, keyPair4.PublicKey }, new KeyPair[] { keyPair1, keyPair2, keyPair3 }, Contract.CreateSignatureContract(receiverKey.PublicKey).ScriptHash, 10 * NativeContract.GAS.Factor).ConfigureAwait(false);