tron_fullnode_client.dart 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import 'package:dio/dio.dart';
  2. import '../config/app_config.dart';
  3. /// Tron FullNode HTTP API(与 Web TronWeb transactionBuilder 调用的 REST 一致)
  4. class TronFullNodeClient {
  5. TronFullNodeClient._();
  6. static Dio _dio(String rpcBase) {
  7. final base = _normalizeBase(rpcBase);
  8. final headers = <String, String>{
  9. 'accept': 'application/json',
  10. 'content-type': 'application/json',
  11. };
  12. final key = AppConfig.tronGridApiKey.trim();
  13. if (key.isNotEmpty) {
  14. headers['TRON-PRO-API-KEY'] = key;
  15. }
  16. return Dio(
  17. BaseOptions(
  18. baseUrl: base,
  19. connectTimeout: const Duration(seconds: 25),
  20. receiveTimeout: const Duration(seconds: 25),
  21. headers: headers,
  22. ),
  23. );
  24. }
  25. static String _normalizeBase(String rpcUrl) {
  26. var u = rpcUrl.trim();
  27. if (u.endsWith('/')) {
  28. u = u.substring(0, u.length - 1);
  29. }
  30. return u;
  31. }
  32. /// POST `/wallet/triggersmartcontract`
  33. static Future<Map<String, dynamic>> triggerSmartContract({
  34. required String rpcBase,
  35. required String ownerAddressBase58,
  36. required String contractAddressBase58,
  37. required String functionSelector,
  38. required String parameterHex,
  39. int feeLimitSun = 150000000,
  40. }) async {
  41. final dio = _dio(rpcBase);
  42. final res = await dio.post<dynamic>(
  43. '/wallet/triggersmartcontract',
  44. data: <String, dynamic>{
  45. 'owner_address': ownerAddressBase58,
  46. 'contract_address': contractAddressBase58,
  47. 'function_selector': functionSelector,
  48. 'parameter': parameterHex,
  49. 'fee_limit': feeLimitSun,
  50. 'call_value': 0,
  51. 'visible': true,
  52. },
  53. );
  54. final body = res.data;
  55. if (body is! Map) {
  56. throw StateError(' triggersmartcontract 响应无效');
  57. }
  58. final map = Map<String, dynamic>.from(body);
  59. final result = map['result'];
  60. if (result is Map && result['result'] != true) {
  61. throw StateError(
  62. result['message']?.toString() ?? '构建 TRC20 交易失败',
  63. );
  64. }
  65. final tx = map['transaction'];
  66. if (tx is! Map) {
  67. throw StateError('无 transaction 字段');
  68. }
  69. // 与 Reown AppKit 示例一致:WalletConnect `tron_signTransaction` 的 `transaction`
  70. // 常期望为 triggersmartcontract 整段 JSON(含 result / transaction 等),不单是内层 tx。
  71. return map;
  72. }
  73. /// POST `/wallet/createtransaction`(原生 TRX)
  74. static Future<Map<String, dynamic>> createTrxTransaction({
  75. required String rpcBase,
  76. required String ownerAddressBase58,
  77. required String toAddressBase58,
  78. required int amountSun,
  79. }) async {
  80. final dio = _dio(rpcBase);
  81. final res = await dio.post<dynamic>(
  82. '/wallet/createtransaction',
  83. data: <String, dynamic>{
  84. 'owner_address': ownerAddressBase58,
  85. 'to_address': toAddressBase58,
  86. 'amount': amountSun,
  87. 'visible': true,
  88. },
  89. );
  90. final body = res.data;
  91. if (body is! Map) {
  92. throw StateError('createtransaction 响应无效');
  93. }
  94. return Map<String, dynamic>.from(body);
  95. }
  96. /// POST `/wallet/broadcasttransaction`
  97. static Future<String> broadcastTransaction({
  98. required String rpcBase,
  99. required Map<String, dynamic> signed,
  100. }) async {
  101. final dio = _dio(rpcBase);
  102. final res = await dio.post<dynamic>(
  103. '/wallet/broadcasttransaction',
  104. data: signed,
  105. );
  106. final body = res.data;
  107. if (body is! Map) {
  108. throw StateError('broadcast 响应无效');
  109. }
  110. final map = Map<String, dynamic>.from(body);
  111. final ok = map['result'] == true;
  112. final txid = map['txid']?.toString();
  113. if (!ok || txid == null || txid.isEmpty) {
  114. throw StateError(map['message']?.toString() ?? '广播交易失败');
  115. }
  116. return txid;
  117. }
  118. }