钱包相关接口
Neo 中大部分的操作与账户有关,而钱包是账户的集合,包含一个或多个账户。本篇文档将主要介绍以下内容:
-
账户与钱包的基本概念和操作;
-
使用
WalletAPI
,该方法封装了钱包相关接口,提供余额查询、GAS 提取、转账等功能。
账户与钱包基础
账户
Neo 中的账户是用户身份的证明,本质上是一个 KeyPair
类型的密钥对,其中包含私钥和公钥。
// create a new KeyPair
byte[] 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 address
string adddress = scriptHash.ToAddress(ProtocolSettings.Default.AddressVersion);
// address to ScriptHash
scriptHash = adddress.ToScriptHash(ProtocolSettings.Default.AddressVersion);
钱包
钱包是账户的集合, NEP6
是Neo中最常用的钱包标准, NEP6
钱包可以序列化为一个 JSON 格式的文件,其中保存了加密后的账户私钥,需要对应的密码才能解密获取私钥。
可参考如下示例:
创建新的 NEP6 钱包,添加账户并保存为 JSON 文件:
// create wallet
string 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 wallet
NEP6Wallet wallet = new NEP6Wallet(path);
KeyPair keyPair2;
using (wallet.Unlock(password))
{
keyPair2 = wallet.GetAccounts().First().GetKey();
}
使用 WalletAPI
初始化
WalletAPI
初始化:
// choose a neo node with rpc opened
RpcClient client = new RpcClient(new Uri("http://localhost:20332"), null, null, ProtocolSettings.Load("config.json"));
WalletAPI walletAPI = new WalletAPI(client);
查询余额
账户余额的类型一般是
BigInteger
,这是把小数部分取整后的一种表示,需要除以Factor
才能得出Token 的实际数量。
查询 NEP17 资产余额查询可以使用字符串参数:
// get the neo balance of account
string tokenHash = NativeContract.NEO.Hash.ToString();
string address = "NZs2zXSPuuv9ZF6TDGSWT1RBmE8rfGj7UW";
BigInteger balance = await walletAPI.GetTokenBalanceAsync(tokenHash, address).ConfigureAwait(false);
也可以使用 ScriptHash 类型的参数:
// Get the NEO balance of account
UInt160 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 balance
uint neoBalance = await walletAPI.GetNeoBalanceAsync(address).ConfigureAwait(false);
// Get the GAS balance
decimal gasBalance = await walletAPI.GetGasBalanceAsync(address).ConfigureAwait(false);
提取 GAS
在 Neo N3 中提取 GAS 的过程是在 NEO 转账时自动进行的,你可以构建一笔给自己转账的交易来提取 GAS。
-
首先查询当前地址可以提取的 GAS 数量,例如:
// Get the claimable GAS of one address string 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);
-
构建一笔给自己转账的交易,自动提取 GAS:
// Claiming GAS needs the KeyPair of account. You can also use wif or private key hex string string 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 address
await walletAPI.TransferAsync(tokenHash, wif, address, 10).ConfigureAwait(false);
// 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}"));
也可以使用 KeyPair
和 UInt160
(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 address
await 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);