tron_recharge.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import 'dart:typed_data';
  2. import 'package:bs58/bs58.dart';
  3. import 'package:convert/convert.dart';
  4. import 'package:crypto/crypto.dart';
  5. import 'package:decimal/decimal.dart';
  6. /// Tron 主网 CAIP-2(与 Web `tronRecharge.ts` / WalletConnect 一致)
  7. const String kTronMainnetCaip2 = 'tron:0x2b6653dc';
  8. /// rpcMap key(不含 tron: 前缀)
  9. const String kTronMainnetChainIdHex = '0x2b6653dc';
  10. /// Tether 波场主网 USDT TRC20 合约 Base58(与 Web 一致)
  11. const String kTrc20UsdtOfficialMainnet = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
  12. /// USDT-TRC20 精度
  13. const int kTrc20UsdtDecimals = 6;
  14. String tronMainnetCaip10Account(String base58Address) {
  15. return '$kTronMainnetCaip2:$base58Address';
  16. }
  17. bool isTronDepositNetwork(String protocol, String networkName) {
  18. final p = protocol.toLowerCase().trim();
  19. final n = networkName.toLowerCase().trim();
  20. return p.contains('tron') || n == 'trc20';
  21. }
  22. bool isTronRechargeUsdtSymbol(String coinName) {
  23. final u = coinName.toUpperCase().trim();
  24. return u == 'USDT' || u == 'TUSDT';
  25. }
  26. /// 金额字符串 → TRC20 最小单位整数字符串(与 Web `trc20AmountToMinUnits` 一致)
  27. String trc20AmountToMinUnits(String amountStr, int decimals) {
  28. final s = amountStr.trim().replaceAll(',', '');
  29. if (s.isEmpty) {
  30. throw FormatException('充值金额无效');
  31. }
  32. final d = Decimal.parse(s);
  33. var mult = Decimal.one;
  34. for (var i = 0; i < decimals; i++) {
  35. mult = mult * Decimal.fromInt(10);
  36. }
  37. final v = (d * mult).floor();
  38. return v.toBigInt().toString();
  39. }
  40. /// TRX 人类可读数量 → SUN(与 Web `trxToSunAmount` 一致)
  41. int trxToSunAmount(String amountStr) {
  42. final d =
  43. Decimal.parse(amountStr.trim().replaceAll(',', '')) * Decimal.fromInt(1000000);
  44. final n = d.floor().toBigInt().toInt();
  45. if (n <= 0) {
  46. throw FormatException('充值金额无效');
  47. }
  48. return n;
  49. }
  50. /// 将 Base58 地址编码为 triggerSmartContract 的 parameter 片段(与 Reown 示例一致)
  51. String tronAddressAbiWord(String base58Address) {
  52. final decoded = base58.decode(base58Address);
  53. final fullHex = hex.encode(decoded);
  54. if (fullHex.length < 10) {
  55. throw FormatException('无效的波场地址');
  56. }
  57. final bodyHex = fullHex.substring(2, fullHex.length - 8);
  58. return bodyHex.padLeft(64, '0');
  59. }
  60. String tronTransferTrc20ParameterHex(String toBase58, BigInt amountMinUnits) {
  61. final addrWord = tronAddressAbiWord(toBase58);
  62. final amountHex =
  63. amountMinUnits.toRadixString(16).padLeft(64, '0').toLowerCase();
  64. if (amountHex.length != 64) {
  65. throw FormatException('金额过大');
  66. }
  67. return '$addrWord$amountHex';
  68. }
  69. /// 兼容 `tron:<chain>:T…` / 十六进制形态;对齐 Web `parseTronCaip10Address`。
  70. String? parseTronCaip10Address(String accountStr) {
  71. final trimmed = accountStr.trim();
  72. if (trimmed.isEmpty) {
  73. return null;
  74. }
  75. final parts = trimmed.split(':').map((p) => p.trim()).toList();
  76. if (parts.length < 3 || parts[0].toLowerCase() != 'tron') {
  77. return null;
  78. }
  79. final last = parts.last;
  80. if (last.startsWith('T')) {
  81. try {
  82. final d = base58.decode(last);
  83. if (d.length >= 21) {
  84. return last;
  85. }
  86. } catch (_) {
  87. return null;
  88. }
  89. return null;
  90. }
  91. var hexBody = last;
  92. if (hexBody.startsWith('0x') || hexBody.startsWith('0X')) {
  93. hexBody = hexBody.substring(2);
  94. }
  95. if (!RegExp(r'^[a-fA-F0-9]+$').hasMatch(hexBody)) {
  96. return null;
  97. }
  98. if (hexBody.length == 40) {
  99. hexBody = '41$hexBody';
  100. }
  101. try {
  102. return _tronBytes21ToBase58(Uint8List.fromList(hex.decode(hexBody)));
  103. } catch (_) {
  104. return null;
  105. }
  106. }
  107. String _tronBytes21ToBase58(Uint8List body21) {
  108. if (body21.length != 21 || body21[0] != 0x41) {
  109. throw FormatException('invalid tron raw address');
  110. }
  111. final h1 = sha256.convert(body21);
  112. final h2 = sha256.convert(h1.bytes);
  113. final full = Uint8List(25);
  114. full.setRange(0, 21, body21);
  115. full.setRange(21, 25, h2.bytes.sublist(0, 4));
  116. return base58.encode(full);
  117. }