最近在弄關於 TRX 的項目,因為最近 ETH 的 GAS 真的太高了,所以目標先鎖定 TRX
之後有時間我也會順便把 BSC 也研究一下,這篇開始我們作一系列的文章。

波場幣 (TRX) 就是Tron的區塊鏈代幣,但通常大家會接觸Tron的原因是因為TRC20傳輸通道在傳送USDT時手續費很少,所以現在幾乎都會使用 TRC20 來傳送 USDT
今天,主要是分享怎麼產生 TRX 的錢包。
其實,研究後才發現,之前那篇文章Ethereum 以太坊產生高清錢包 (HD
Wallet)
我們透過 Nethereum.HdWallet 就可以產出高清錢包,後來發現,其實 TRON 錢包算是魔改 ERC20 的錢包,所以透過 產出乙太錢包,也可以產出 TRON 錢包
C# Code :
public void CreateWallet()
{
//Code reference : https://www.796t.com/article.php?id=22311
//Code reference : https://blog.no2don.com/2021/10/c-ethereum-hd-wallet.html
var words = "會不會有人知道當麻在這個孤獨的星球曾這樣的活過過";
var password = "password";
for (var index = 0; index <= 10; index++)
{
var walletAccount = GetHDWalletInfoByIndex(string.Join(" ", words.ToArray()), password, index);
Result += ("編號:" + index + " , 錢包位置:" + GetBase58CheckAddress(walletAccount.Address) + " ,私鑰:" + walletAccount.PrivateKey);
Result += ("
");
}
}
public static Nethereum.Web3.Accounts.Account GetHDWalletInfoByIndex(string words, string password, int index)
{
return new Wallet(words, password).GetAccount(index);
}
public static string GetBase58CheckAddress(string ethAddress)
{
string fixaddress = "0x41" + ethAddress.RemoveHexPrefix();
byte[] addressBytes = fixaddress.HexToByteArray();
byte[] hash0 = SHA256(addressBytes);
byte[] hash1 = SHA256(hash0);
var checkSum = hash1.Take(4).ToArray();
return MetaUtil.Base58Encoding.Encode(addressBytes.Concat(checkSum).ToArray());
}
public static byte[] SHA256(byte[] data)
{
using (var sha256 = new SHA256Managed())
{
return sha256.ComputeHash(data);
}
}
這其中
他其實就是將 0x 拿掉後改成 0x41 這樣就可以確保產出的錢包是 T 開頭的,所以波場錢包都是 T 開頭的錢包
( TRON 地址為大寫字母 T 開頭的 34 字元地址,以太坊地址前附加位元組 0x41 後執行 Base58check 操作所得)
網路上你會看到許多 ETH 轉成 TRX 範例 ,基本上跟我大同小異,但是我這邊不一樣的事我改用另一個 lib ( https://gist.github.com/donma/03d4ed4f0092e749aa7eae6720c004e2/edit
)
這邊也附上程式碼 :
using System;
using System.Diagnostics.Contracts;
using System.Linq;
namespace Merkator.Tools
{
public class ArrayHelpers
{
public static T[] ConcatArrays(params T[][] arrays)
{
//Contract.Requires(arrays != null);
//Contract.Requires(Contract.ForAll(arrays, (arr) => arr != null));
//Contract.Ensures(Contract.Result() != null);
//Contract.Ensures(Contract.Result().Length == arrays.Sum(arr => arr.Length));
var result = new T[arrays.Sum(arr => arr.Length)];
int offset = 0;
for (int i = 0; i < arrays.Length; i++)
{
var arr = arrays[i];
Buffer.BlockCopy(arr, 0, result, offset, arr.Length);
offset += arr.Length;
}
return result;
}
public static T[] ConcatArrays(T[] arr1, T[] arr2)
{
/*
Contract.Requires(arr1 != null);
Contract.Requires(arr2 != null);
Contract.Ensures(Contract.Result() != null);
Contract.Ensures(Contract.Result().Length == arr1.Length + arr2.Length);
*/
var result = new T[arr1.Length + arr2.Length];
Buffer.BlockCopy(arr1, 0, result, 0, arr1.Length);
Buffer.BlockCopy(arr2, 0, result, arr1.Length, arr2.Length);
return result;
}
public static T[] SubArray(T[] arr, int start, int length)
{
/*
Contract.Requires(arr != null);
Contract.Requires(start >= 0);
Contract.Requires(length >= 0);
Contract.Requires(start + length <= arr.Length);
Contract.Ensures(Contract.Result() != null);
Contract.Ensures(Contract.Result().Length == length);
*/
var result = new T[length];
Buffer.BlockCopy(arr, start, result, 0, length);
return result;
}
public static T[] SubArray(T[] arr, int start)
{
/*
Contract.Requires(arr != null);
Contract.Requires(start >= 0);
Contract.Requires(start <= arr.Length);
Contract.Ensures(Contract.Result() != null);
Contract.Ensures(Contract.Result().Length == arr.Length - start);
*/
return SubArray(arr, start, arr.Length - start);
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Merkator.Tools;
namespace Merkator.BitCoin
{
// Implements https://en.bitcoin.it/wiki/Base58Check_encoding
public static class Base58Encoding
{
public const int CheckSumSizeInBytes = 4;
public static byte[] AddCheckSum(byte[] data)
{
//Contract.Requires(data != null);
//Contract.Ensures(Contract.Result().Length == data.Length + CheckSumSizeInBytes);
byte[] checkSum = GetCheckSum(data);
byte[] dataWithCheckSum = ArrayHelpers.ConcatArrays(data, checkSum);
return dataWithCheckSum;
}
//Returns null if the checksum is invalid
public static byte[] VerifyAndRemoveCheckSum(byte[] data)
{
//Contract.Requires(data != null);
//Contract.Ensures(Contract.Result() == null || Contract.Result().Length + CheckSumSizeInBytes == data.Length);
byte[] result = ArrayHelpers.SubArray(data, 0, data.Length - CheckSumSizeInBytes);
byte[] givenCheckSum = ArrayHelpers.SubArray(data, data.Length - CheckSumSizeInBytes);
byte[] correctCheckSum = GetCheckSum(result);
if (givenCheckSum.SequenceEqual(correctCheckSum))
return result;
else
return null;
}
private const string Digits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static string Encode(byte[] data)
{
//Contract.Requires(data != null);
//Contract.Ensures(Contract.Result() != null);
// Decode byte[] to BigInteger
BigInteger intData = 0;
for (int i = 0; i < data.Length; i++)
{
intData = intData * 256 + data[i];
}
// Encode BigInteger to Base58 string
string result = "";
while (intData > 0)
{
int remainder = (int)(intData % 58);
intData /= 58;
result = Digits[remainder] + result;
}
// Append `1` for each leading 0 byte
for (int i = 0; i < data.Length && data[i] == 0; i++)
{
result = '1' + result;
}
return result;
}
public static string EncodeWithCheckSum(byte[] data)
{
//Contract.Requires(data != null);
//Contract.Ensures(Contract.Result() != null);
return Encode(AddCheckSum(data));
}
public static byte[] Decode(string s)
{
//Contract.Requires(s != null);
//Contract.Ensures(Contract.Result() != null);
// Decode Base58 string to BigInteger
BigInteger intData = 0;
for (int i = 0; i < s.Length; i++)
{
int digit = Digits.IndexOf(s[i]); //Slow
if (digit < 0)
throw new FormatException(string.Format("Invalid Base58 character `{0}` at position {1}", s[i], i));
intData = intData * 58 + digit;
}
// Encode BigInteger to byte[]
// Leading zero bytes get encoded as leading `1` characters
int leadingZeroCount = s.TakeWhile(c => c == '1').Count();
var leadingZeros = Enumerable.Repeat((byte)0, leadingZeroCount);
var bytesWithoutLeadingZeros =
intData.ToByteArray()
.Reverse()// to big endian
.SkipWhile(b => b == 0);//strip sign byte
var result = leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray();
return result;
}
// Throws `FormatException` if s is not a valid Base58 string, or the checksum is invalid
public static byte[] DecodeWithCheckSum(string s)
{
//Contract.Requires(s != null);
//Contract.Ensures(Contract.Result() != null);
var dataWithCheckSum = Decode(s);
var dataWithoutCheckSum = VerifyAndRemoveCheckSum(dataWithCheckSum);
if (dataWithoutCheckSum == null)
throw new FormatException("Base58 checksum is invalid");
return dataWithoutCheckSum;
}
private static byte[] GetCheckSum(byte[] data)
{
//Contract.Requires(data != null);
//Contract.Ensures(Contract.Result() != null);
SHA256 sha256 = new SHA256Managed();
byte[] hash1 = sha256.ComputeHash(data);
byte[] hash2 = sha256.ComputeHash(hash1);
var result = new byte[CheckSumSizeInBytes];
Buffer.BlockCopy(hash2, 0, result, 0, result.Length);
return result;
}
}
}
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Merkator.BitCoin.Tests
{
[TestClass]
public class Base58EncodingTests
{
// Test cases from https://github.com/bitcoin/bitcoin/blob/master/src/test/base58_tests.cpp
Tuple[] testCases = new Tuple[]{
Tuple.Create("",new byte[]{}),
Tuple.Create("1112",new byte[]{0x00, 0x00, 0x00, 0x01}),
Tuple.Create("2g",new byte[]{0x61}),
Tuple.Create("a3gV",new byte[]{0x62,0x62,0x62}),
Tuple.Create("aPEr",new byte[]{0x63,0x63,0x63}),
Tuple.Create("2cFupjhnEsSn59qHXstmK2ffpLv2",new byte[]{0x73,0x69,0x6d,0x70,0x6c,0x79,0x20,0x61,0x20,0x6c,0x6f,0x6e,0x67,0x20,0x73,0x74,0x72,0x69,0x6e,0x67}),
Tuple.Create("1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L",new byte[]{0x00,0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47}),
Tuple.Create("ABnLTmg",new byte[]{0x51,0x6b,0x6f,0xcd,0x0f}),
Tuple.Create("3SEo3LWLoPntC",new byte[]{0xbf,0x4f,0x89,0x00,0x1e,0x67,0x02,0x74,0xdd}),
Tuple.Create("3EFU7m",new byte[]{0x57,0x2e,0x47,0x94}),
Tuple.Create("EJDM8drfXA6uyA",new byte[]{0xec,0xac,0x89,0xca,0xd9,0x39,0x23,0xc0,0x23,0x21}),
Tuple.Create("Rt5zm",new byte[]{0x10,0xc8,0x51,0x1e}),
Tuple.Create("1111111111",new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00})
};
[TestMethod]
public void Encode()
{
foreach (var tuple in testCases)
{
var bytes = tuple.Item2;
var expectedText = tuple.Item1;
var actualText = Base58Encoding.Encode(bytes);
Assert.AreEqual(expectedText, actualText);
}
}
[TestMethod]
public void Decode()
{
foreach (var tuple in testCases)
{
var text = tuple.Item1;
var expectedBytes = tuple.Item2;
var actualBytes = Base58Encoding.Decode(text);
Assert.AreEqual(BitConverter.ToString(expectedBytes), BitConverter.ToString(actualBytes));
}
}
[TestMethod]
[ExpectedException(typeof(FormatException))]
public void DecodeInvalidChar()
{
Base58Encoding.Decode("ab0");
}
// Example address from https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses
byte[] addressBytes = new byte[] { 0x00, 0x01, 0x09, 0x66, 0x77, 0x60, 0x06, 0x95, 0x3D, 0x55, 0x67, 0x43, 0x9E, 0x5E, 0x39, 0xF8, 0x6A, 0x0D, 0x27, 0x3B, 0xEE };
string addressText = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM";
string brokenAddressText = "16UwLl9Risc3QfPqBUvKofHmBQ7wMtjvM";
[TestMethod]
public void EncodeBitcoinAddress()
{
var actualText = Base58Encoding.EncodeWithCheckSum(addressBytes);
Assert.AreEqual(addressText, actualText);
}
[TestMethod]
public void DecodeBitcoinAddress()
{
var actualBytes = Base58Encoding.DecodeWithCheckSum(addressText);
Assert.AreEqual(BitConverter.ToString(addressBytes), BitConverter.ToString(actualBytes));
}
[TestMethod]
[ExpectedException(typeof(FormatException))]
public void DecodeBrokenBitcoinAddress()
{
var actualBytes = Base58Encoding.DecodeWithCheckSum(brokenAddressText);
Assert.AreEqual(BitConverter.ToString(addressBytes), BitConverter.ToString(actualBytes));
}
}
}
原文參考中,他轉 base 58 跟做 SHA 的時候,是使用 Tron.Net.Client,但是因為我覺得還是能夠在自己專案中就少用 lib ,所以網路上看到一個不錯的就使用,用起來也是正常。
結果當然我們就是要安裝 tronlink 來試試看, tronlink 安裝連結 : https://chrome.google.com/webstore/detail/tronlink%EF%BC%88%E6%B3%A2%E5%AE%9D%E9%92%B1%E5%8C%85%EF%BC%89/ibnejdfjmmkpcnlpebklmnkoeoihofec
result:
編號:0 , 錢包位置:TNWFKAodrxankYgHg9nJmNbYTsqi8MtCWx
,私鑰:0x8ca224fec50afaffc70e445ae2d01434e666268298548f67597068735562ad10
編號:1 , 錢包位置:TLhTDxNcm9XaSq35mghq6Az35LeHdcn77S ,私鑰:0xcf893594f8c20fc51ec5452169be4691e58f9abe624ba803c95692d5514c08d6
編號:2 , 錢包位置:TWTzoadibCZiCph6y878bTPB7ta8WXaMFR ,私鑰:0x02f49fd0f312f53a35e092f9da6329374acfdbe45bfb8c32de2f4fe3943e90d7
編號:3 , 錢包位置:TEyYFGGYTBSyLfsrbhTMqdqMkM9TTAPsyE ,私鑰:0x018fe3b25668c393bbc4953ef9f8e22201e769f422f6493049b0cafb0c2d2fde
編號:4 , 錢包位置:TDSZ64gn4FV2mQwG7BAqvK2WhZbujukbuJ ,私鑰:0x3881ed87be2ec87069466cd4316087828e0712e592965eaed83ed84fde26dbb0
編號:5 , 錢包位置:TRza1rbWRhgnRKvTSxnkzkz7RP1Xem6TFJ ,私鑰:0x6984206e9e1af8090a120774fd657d88b278f8bde685ef8532dabe9d61f557bc
編號:6 , 錢包位置:TFuvzv1kHX9Kw6kTZtv6p7PUMWvqRKZwYF ,私鑰:0xa93e86896010eabb2dbbdf788cb183630ab3391c85f16e3dacc9beb53faef911
編號:7 , 錢包位置:TBG4p2hMxmdj3a1n2qGGH589uPVj5n29Bm ,私鑰:0x1d7617dcc35e942710b495764681279a19caf5c9c26d824cf093b9162ccef608
編號:8 , 錢包位置:TBa6tz95W5baRro9pXRRRntZNdpvtg3fTw ,私鑰:0x65da4a4e30a8174388d97650869ab6c88f0ff539b1d5f66c369f1554fd5ba01f
編號:9 , 錢包位置:TUbwHugEn5FXZgWiMtCNSa3d9wz1sVqner ,私鑰:0x010e0f160a3c8f3f891858d29dceecedccede8d87a6d29e164d2f42cfc0c1aee
編號:10 , 錢包位置:TQgCvX6NViUZ4p8WqAuAowZSjnujsXmWQ9 ,私鑰:0xfc76bc0ac79f66c982a638b6eeff6c60bad9f5e554394b3f140a281cb3317fb0

只要夠成功加入錢包到 tronlink 就是代表他是一個正常的錢包了。
這是開始,之後我會再弄寫其它應用,至少這是重要的開始
reference:
https://andelf.gitbook.io/tron/introduction/tron-basics
https://www.796t.com/article.php?id=22311