import 'dart:typed_data'; import 'package:bs58/bs58.dart'; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; import 'package:decimal/decimal.dart'; /// Tron 主网 CAIP-2(与 Web `tronRecharge.ts` / WalletConnect 一致) const String kTronMainnetCaip2 = 'tron:0x2b6653dc'; /// rpcMap key(不含 tron: 前缀) const String kTronMainnetChainIdHex = '0x2b6653dc'; /// Tether 波场主网 USDT TRC20 合约 Base58(与 Web 一致) const String kTrc20UsdtOfficialMainnet = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'; /// USDT-TRC20 精度 const int kTrc20UsdtDecimals = 6; String tronMainnetCaip10Account(String base58Address) { return '$kTronMainnetCaip2:$base58Address'; } bool isTronDepositNetwork(String protocol, String networkName) { final p = protocol.toLowerCase().trim(); final n = networkName.toLowerCase().trim(); return p.contains('tron') || n == 'trc20'; } bool isTronRechargeUsdtSymbol(String coinName) { final u = coinName.toUpperCase().trim(); return u == 'USDT' || u == 'TUSDT'; } /// 金额字符串 → TRC20 最小单位整数字符串(与 Web `trc20AmountToMinUnits` 一致) String trc20AmountToMinUnits(String amountStr, int decimals) { final s = amountStr.trim().replaceAll(',', ''); if (s.isEmpty) { throw FormatException('充值金额无效'); } final d = Decimal.parse(s); var mult = Decimal.one; for (var i = 0; i < decimals; i++) { mult = mult * Decimal.fromInt(10); } final v = (d * mult).floor(); return v.toBigInt().toString(); } /// TRX 人类可读数量 → SUN(与 Web `trxToSunAmount` 一致) int trxToSunAmount(String amountStr) { final d = Decimal.parse(amountStr.trim().replaceAll(',', '')) * Decimal.fromInt(1000000); final n = d.floor().toBigInt().toInt(); if (n <= 0) { throw FormatException('充值金额无效'); } return n; } /// 将 Base58 地址编码为 triggerSmartContract 的 parameter 片段(与 Reown 示例一致) String tronAddressAbiWord(String base58Address) { final decoded = base58.decode(base58Address); final fullHex = hex.encode(decoded); if (fullHex.length < 10) { throw FormatException('无效的波场地址'); } final bodyHex = fullHex.substring(2, fullHex.length - 8); return bodyHex.padLeft(64, '0'); } String tronTransferTrc20ParameterHex(String toBase58, BigInt amountMinUnits) { final addrWord = tronAddressAbiWord(toBase58); final amountHex = amountMinUnits.toRadixString(16).padLeft(64, '0').toLowerCase(); if (amountHex.length != 64) { throw FormatException('金额过大'); } return '$addrWord$amountHex'; } /// 兼容 `tron::T…` / 十六进制形态;对齐 Web `parseTronCaip10Address`。 String? parseTronCaip10Address(String accountStr) { final trimmed = accountStr.trim(); if (trimmed.isEmpty) { return null; } final parts = trimmed.split(':').map((p) => p.trim()).toList(); if (parts.length < 3 || parts[0].toLowerCase() != 'tron') { return null; } final last = parts.last; if (last.startsWith('T')) { try { final d = base58.decode(last); if (d.length >= 21) { return last; } } catch (_) { return null; } return null; } var hexBody = last; if (hexBody.startsWith('0x') || hexBody.startsWith('0X')) { hexBody = hexBody.substring(2); } if (!RegExp(r'^[a-fA-F0-9]+$').hasMatch(hexBody)) { return null; } if (hexBody.length == 40) { hexBody = '41$hexBody'; } try { return _tronBytes21ToBase58(Uint8List.fromList(hex.decode(hexBody))); } catch (_) { return null; } } String _tronBytes21ToBase58(Uint8List body21) { if (body21.length != 21 || body21[0] != 0x41) { throw FormatException('invalid tron raw address'); } final h1 = sha256.convert(body21); final h2 = sha256.convert(h1.bytes); final full = Uint8List(25); full.setRange(0, 21, body21); full.setRange(21, 25, h2.bytes.sublist(0, 4)); return base58.encode(full); }