LCOV - code coverage report
Current view: top level - crypto/evm/repositories/rpc - evm_rpc_interface.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 32.7 % 220 72
Test Date: 2025-06-07 01:20:49 Functions: - 0 0

            Line data    Source code
       1              : import 'dart:async';
       2              : import 'dart:typed_data';
       3              : import 'package:walletkit_dart/src/common/logger.dart';
       4              : import 'package:walletkit_dart/src/crypto/evm/entities/block_number.dart';
       5              : import 'package:walletkit_dart/src/crypto/evm/repositories/rpc/queued_rpc_interface.dart';
       6              : import 'package:walletkit_dart/src/utils/int.dart';
       7              : import 'package:walletkit_dart/walletkit_dart.dart';
       8              : 
       9              : const type2Multiplier = 1.5;
      10              : 
      11              : final class EvmRpcInterface {
      12              :   final EVMNetworkType type;
      13              :   final Map<int, int> blockTimestampCache = {};
      14              :   final Map<String, ConfirmationStatus> txStatusCache = {};
      15              :   final RpcManager _manager;
      16              : 
      17            0 :   Future<void> get refreshFuture => _manager.refreshFuture;
      18              : 
      19              :   ///
      20              :   /// [clients] - A list of clients to use for the manager
      21              :   /// [useQueuedManager] - If true, the manager will use a QueuedRpcManager and requests will be queued
      22              :   /// [awaitRefresh] - If true, the manager will wait for the clients to be refreshed before performing a task
      23              :   /// [refreshIntervall] - The rate at which the clients are refreshed if null the clients will only be refreshed once
      24              :   /// [eagerError] - If true a task will throw the first error it encounters, if false it will try all clients before throwing an error
      25              :   ///
      26            5 :   EvmRpcInterface({
      27              :     bool useQueuedManager = true,
      28              :     bool awaitRefresh = true,
      29              :     Duration? refreshIntervall,
      30              :     bool eagerError = false,
      31              :     RefreshType refreshType = RefreshType.onTask,
      32              :     required List<EvmRpcClient> clients,
      33              :     required this.type,
      34              :   }) : _manager = useQueuedManager
      35            4 :             ? QueuedRpcManager(
      36              :                 awaitRefresh: awaitRefresh,
      37              :                 clientRefreshRate: refreshIntervall,
      38              :                 allClients: clients,
      39              :                 eagerError: eagerError,
      40              :                 refreshType: refreshType,
      41              :               )
      42            3 :             : SimpleRpcManager(
      43              :                 awaitRefresh: awaitRefresh,
      44              :                 clientRefreshRate: refreshIntervall,
      45              :                 allClients: clients,
      46              :                 eagerError: eagerError,
      47              :                 refreshType: refreshType,
      48              :               );
      49              : 
      50            5 :   Future<T> performTask<T>(
      51              :     Future<T> Function(EvmRpcClient client) task, {
      52              :     Duration timeout = const Duration(seconds: 30),
      53              :     int? maxTries,
      54              :   }) =>
      55           15 :       _manager.performTask(task, timeout: timeout, maxTries: maxTries).then(
      56            5 :         (valueOrError) {
      57            5 :           return valueOrError.when(
      58           10 :             value: (value) => value.value,
      59            0 :             error: (error) => throw Exception(error),
      60              :           );
      61              :         },
      62              :       );
      63              : 
      64            0 :   Future<R> performTaskForClients<T, R>(
      65              :     Future<T> Function(EvmRpcClient) task, {
      66              :     required R Function(
      67              :       List<ValueOrError<T, EvmRpcClient>> results,
      68              :     ) consilidate,
      69              :     Duration timeout = const Duration(seconds: 30),
      70              :     int maxTriesPerClient = 2,
      71              :     int minClients = 2,
      72              :     int? maxClients,
      73              :     bool enforceParallel = false,
      74              :   }) =>
      75            0 :       _manager.performTaskForClients(
      76              :         task,
      77              :         consilidate: consilidate,
      78              :         timeout: timeout,
      79              :         maxTriesPerClient: maxTriesPerClient,
      80              :         maxClients: maxClients,
      81              :         minClients: minClients,
      82              :         enforceParallel: enforceParallel,
      83              :       );
      84              : 
      85              :   ///
      86              :   /// eth_call
      87              :   ///
      88            3 :   Future<String> call({
      89              :     String? sender,
      90              :     required String contractAddress,
      91              :     required Uint8List data,
      92              :     BlockNum? atBlock,
      93              :   }) {
      94            3 :     return performTask(
      95            6 :       (client) => client.call(
      96              :         sender: sender,
      97              :         contractAddress: contractAddress,
      98              :         data: data,
      99              :         atBlock: atBlock,
     100              :       ),
     101              :     );
     102              :   }
     103              : 
     104              :   ///
     105              :   /// Fetch Balance
     106              :   ///
     107            2 :   Future<Amount> fetchBalance({
     108              :     required String address,
     109              :   }) async {
     110            6 :     final balance = await performTask((client) => client.getBalance(address));
     111            2 :     return Amount(
     112              :       value: balance,
     113            6 :       decimals: type.coin.decimals,
     114              :     );
     115              :   }
     116              : 
     117              :   ///
     118              :   /// Fetch Token Balance
     119              :   ///
     120            1 :   Future<Amount> fetchTokenBalance(
     121              :     String address,
     122              :     ERC20Entity token,
     123              :   ) async {
     124            1 :     final erc20Contract = ERC20Contract(
     125            1 :       contractAddress: token.contractAddress,
     126              :       rpc: this,
     127              :     );
     128            1 :     final balance = await erc20Contract.getBalance(address);
     129            2 :     return Amount(value: balance, decimals: token.decimals);
     130              :   }
     131              : 
     132              :   ///
     133              :   /// Fetch Balance of ERC1155 Token
     134              :   ///
     135            1 :   Future<Amount> fetchERC1155BalanceOfToken({
     136              :     required String address,
     137              :     required BigInt tokenID,
     138              :     required String contractAddress,
     139              :   }) async {
     140            1 :     final erc1155Contract = ERC1155Contract(
     141              :       contractAddress: contractAddress,
     142              :       rpc: this,
     143              :     );
     144            1 :     final balance = await erc1155Contract.balanceOf(
     145              :       address: address,
     146              :       tokenID: tokenID,
     147              :     );
     148              : 
     149            1 :     return Amount(value: balance, decimals: 0);
     150              :   }
     151              : 
     152              :   ///
     153              :   /// Fetch Batch Balance of ERC1155 Tokens
     154              :   ///
     155            1 :   Future<List<BigInt>> fetchERC1155BatchBalanceOfTokens({
     156              :     required List<String> accounts,
     157              :     required List<BigInt> tokenIDs,
     158              :     required String contractAddress,
     159              :   }) async {
     160            1 :     final erc1155Contract = ERC1155Contract(
     161              :       contractAddress: contractAddress,
     162              :       rpc: this,
     163              :     );
     164              : 
     165            1 :     final balances = await erc1155Contract.balanceOfBatch(
     166              :       accounts: accounts,
     167              :       tokenIDs: tokenIDs,
     168              :     );
     169              : 
     170              :     return balances;
     171              :   }
     172              : 
     173              :   ///
     174              :   /// Fetch Uri of ERC115 Token
     175              :   ///
     176            1 :   Future<String> fetchERC1155UriOfToken({
     177              :     required BigInt tokenID,
     178              :     required String contractAddress,
     179              :   }) async {
     180            1 :     final erc1155Contract = ERC1155Contract(
     181              :       contractAddress: contractAddress,
     182              :       rpc: this,
     183              :     );
     184              : 
     185            1 :     final uri = await erc1155Contract.getUri(
     186              :       tokenID: tokenID,
     187              :     );
     188              : 
     189              :     return uri;
     190              :   }
     191              : 
     192            1 :   Future<(Amount, int)> estimateNetworkFees({
     193              :     required String recipient,
     194              :     required String sender,
     195              :     required Uint8List? data,
     196              :     required BigInt? value,
     197              :   }) async {
     198            1 :     final gasPrice = await getGasPrice();
     199            1 :     final gasLimit = await estimateGasLimit(
     200              :       recipient: recipient,
     201              :       sender: sender,
     202              :       data: data,
     203              :       value: value,
     204              :       gasPrice: gasPrice,
     205              :     );
     206              : 
     207            1 :     return (Amount(value: gasPrice, decimals: 18), gasLimit);
     208              :   }
     209              : 
     210              :   ///
     211              :   /// Get Gas Price
     212              :   ///
     213            1 :   Future<BigInt> getGasPrice() async {
     214            1 :     return await performTask(
     215            2 :       (client) => client.getGasPrice(),
     216              :     );
     217              :   }
     218              : 
     219              :   ///
     220              :   /// Get Gas Price
     221              :   ///
     222            3 :   Future<Amount> getGasPriceAmount() => getGasPrice().then(
     223            2 :         (value) => Amount(value: value, decimals: 18),
     224              :       );
     225              : 
     226              :   ///
     227              :   /// Get Transaction Count (Nonce)
     228              :   ///
     229            0 :   Future<BigInt> getTransactionCount(String address) async {
     230            0 :     return await performTask(
     231            0 :       (client) => client.getTransactionCount(address),
     232              :     );
     233              :   }
     234              : 
     235              :   ///
     236              :   /// Get Transaction By Hash
     237              :   ///
     238            1 :   Future<RawEvmTransaction> getTransactionByHash(String hash) async {
     239            1 :     return await performTask(
     240            2 :       (client) => client.getTransactionByHash(hash),
     241              :     );
     242              :   }
     243              : 
     244              :   ///
     245              :   /// Send Currency
     246              :   ///
     247            0 :   Future<String> sendCoin({
     248              :     required TransferIntent<EvmFeeInformation> intent,
     249              :     required String from,
     250              :     required Uint8List privateKey,
     251              :   }) async {
     252            0 :     final tx = await buildTransaction(
     253              :       sender: from,
     254            0 :       recipient: intent.recipient,
     255              :       privateKey: privateKey,
     256            0 :       feeInfo: intent.feeInfo,
     257            0 :       data: intent.encodedMemo,
     258            0 :       value: intent.amount.value,
     259            0 :       accessList: intent.accessList,
     260              :     );
     261            0 :     final balance = await fetchBalance(address: toChecksumAddress(from)).then(
     262            0 :       (amount) => amount.value,
     263              :     );
     264              : 
     265            0 :     if (balance < tx.gasFee + tx.value) {
     266            0 :       throw WKFailure("Insufficient funds to pay native gas fee");
     267              :     }
     268              : 
     269            0 :     return await sendRawTransaction(tx.serialized.toHex);
     270              :   }
     271              : 
     272              :   ///
     273              :   /// Send ERC20 Token
     274              :   ///
     275            0 :   Future<String> sendERC20Token({
     276              :     required TransferIntent<EvmFeeInformation> intent,
     277              :     required String from,
     278              :     required Uint8List privateKey,
     279              :   }) async {
     280            0 :     assert(intent.token is ERC20Entity);
     281            0 :     assert(intent.memo == null);
     282              : 
     283            0 :     final erc20 = intent.token as ERC20Entity;
     284            0 :     final tokenContractAddress = erc20.contractAddress;
     285              : 
     286            0 :     final erc20Contract = ERC20Contract(
     287              :       contractAddress: tokenContractAddress,
     288              :       rpc: this,
     289              :     );
     290              : 
     291            0 :     return erc20Contract.transfer(
     292              :       privateKey: privateKey,
     293              :       sender: from,
     294            0 :       to: intent.recipient,
     295            0 :       value: intent.amount.value,
     296            0 :       feeInfo: intent.feeInfo,
     297            0 :       accessList: intent.accessList,
     298              :     );
     299              :   }
     300              : 
     301              :   ///
     302              :   /// Send ERC1155 Token
     303              :   ///
     304            0 :   Future<String> sendERC1155Token({
     305              :     required TransferIntent<EvmFeeInformation> intent,
     306              :     required String contractAddress,
     307              :     required BigInt tokenID,
     308              :     required String from,
     309              :     required Uint8List privateKey,
     310              :   }) async {
     311            0 :     final erc1155Contract = ERC1155Contract(
     312              :       contractAddress: contractAddress,
     313              :       rpc: this,
     314              :     );
     315              : 
     316            0 :     return erc1155Contract.safeTransferFrom(
     317              :       sender: from,
     318            0 :       to: intent.recipient,
     319              :       tokenID: tokenID,
     320            0 :       amount: intent.amount.value,
     321              :       privateKey: privateKey,
     322            0 :       feeInfo: intent.feeInfo,
     323            0 :       accessList: intent.accessList,
     324              :     );
     325              :   }
     326              : 
     327            1 :   Future<Amount> getPriorityFee() async {
     328            1 :     final priorityFee = await performTask(
     329            2 :       (client) => client.getPriorityFee(),
     330              :     );
     331              : 
     332            1 :     return Amount(value: priorityFee, decimals: 9);
     333              :   }
     334              : 
     335            1 :   Future<EvmType2GasPrice> getType2GasPrice() async {
     336            1 :     final maxFeePerGas = await getGasPriceAmount();
     337            1 :     final maxPriorityFeePerGas = await getPriorityFee();
     338              : 
     339            1 :     return EvmType2GasPrice(
     340            2 :       maxFeePerGas: maxFeePerGas.multiplyAndCeil(type2Multiplier) + maxPriorityFeePerGas,
     341              :       maxPriorityFeePerGas: maxPriorityFeePerGas,
     342              :     );
     343              :   }
     344              : 
     345            1 :   Future<(int gasLimit, EvmGasPrice gasPrice)> fetchNetworkFees({
     346              :     EvmFeeInformation? existing,
     347              :     required String recipient,
     348              :     required String sender,
     349              :     required Uint8List? data,
     350              :     required BigInt? value,
     351              :   }) async {
     352            0 :     var gasLimit = existing?.gasLimit;
     353              :     try {
     354            1 :       gasLimit ??= await estimateGasLimit(
     355              :         recipient: recipient,
     356              :         sender: sender,
     357              :         data: data,
     358              :         value: value,
     359              :       );
     360              :     } catch (e) {
     361            0 :       Logger.logError(e, hint: "Gas estimation failed");
     362              : 
     363              :       // Only Debug
     364            0 :       assert(true, "Gas estimation failed");
     365              : 
     366            0 :       gasLimit = 1E6.toInt();
     367              :     }
     368              : 
     369            0 :     final EvmGasPrice gasPrice = switch (existing?.gasPrice) {
     370            1 :       EvmLegacyGasPrice feeInfo => feeInfo,
     371            1 :       EvmType2GasPrice feeInfo => feeInfo,
     372            3 :       null when type.useEIP1559 => await getType2GasPrice(),
     373            0 :       null => EvmLegacyGasPrice(
     374            0 :           gasPrice: await getGasPriceAmount(),
     375              :         ),
     376              :     };
     377              : 
     378              :     return (gasLimit, gasPrice);
     379              :   }
     380              : 
     381              :   ///
     382              :   /// Used to create a raw Transactions
     383              :   /// Fetches the gasPrice and gasLimit from the network
     384              :   /// Fetches the nonce from the network
     385              :   /// If Transaction Type is not provided, it will use Legacy
     386              :   ///
     387            0 :   Future<RawEvmTransaction> buildUnsignedTransaction({
     388              :     required String sender,
     389              :     required String recipient,
     390              :     required EvmFeeInformation? feeInfo,
     391              :     required Uint8List? data,
     392              :     required BigInt? value,
     393              :     List<AccessListItem>? accessList,
     394              :   }) async {
     395            0 :     final (gasLimit, gasPrice) = await fetchNetworkFees(
     396              :       recipient: recipient,
     397              :       sender: sender,
     398              :       data: data,
     399              :       value: value,
     400              :       existing: feeInfo,
     401              :     );
     402              : 
     403            0 :     final nonce = await performTask(
     404            0 :       (client) => client.getTransactionCount(sender),
     405              :     );
     406              : 
     407              :     return switch (gasPrice) {
     408            0 :       EvmType2GasPrice fee => RawEVMTransactionType2.unsigned(
     409              :           nonce: nonce,
     410            0 :           maxFeePerGas: fee.maxFeePerGas.value,
     411            0 :           maxPriorityFeePerGas: fee.maxPriorityFeePerGas.value,
     412            0 :           gasLimit: gasLimit.toBI,
     413              :           to: recipient,
     414            0 :           value: value ?? BigInt.zero,
     415            0 :           data: data ?? Uint8List(0),
     416            0 :           accessList: accessList ?? [],
     417            0 :           chainId: type.chainId,
     418              :         ),
     419            0 :       EvmLegacyGasPrice fee => accessList != null
     420            0 :           ? RawEVMTransactionType1.unsigned(
     421              :               nonce: nonce,
     422            0 :               gasPrice: fee.gasPrice.value,
     423            0 :               gasLimit: gasLimit.toBI,
     424              :               to: recipient,
     425            0 :               value: value ?? BigInt.zero,
     426            0 :               data: data ?? Uint8List(0),
     427              :               accessList: accessList,
     428            0 :               chainId: type.chainId,
     429              :             )
     430            0 :           : RawEVMTransactionType0.unsigned(
     431              :               nonce: nonce,
     432            0 :               gasPrice: fee.gasPrice.value,
     433            0 :               gasLimit: gasLimit.toBI,
     434              :               to: recipient,
     435            0 :               value: value ?? BigInt.zero,
     436            0 :               data: data ?? Uint8List(0),
     437              :             ),
     438              :     };
     439              :   }
     440              : 
     441              :   ///
     442              :   /// Used to create a raw Transactions
     443              :   /// Fetches the gasPrice and gasLimit from the network
     444              :   /// Fetches the nonce from the network
     445              :   /// Signs the transaction
     446              :   ///
     447            0 :   Future<RawEvmTransaction> buildTransaction({
     448              :     required String sender,
     449              :     required String recipient,
     450              :     required Uint8List privateKey,
     451              :     required EvmFeeInformation? feeInfo,
     452              :     required Uint8List? data,
     453              :     required BigInt? value,
     454              :     List<AccessListItem>? accessList,
     455              :   }) async {
     456            0 :     final unsignedTx = await buildUnsignedTransaction(
     457              :       sender: sender,
     458              :       recipient: recipient,
     459              :       feeInfo: feeInfo,
     460              :       data: data,
     461              :       value: value,
     462              :       accessList: accessList,
     463              :     );
     464              : 
     465            0 :     final signature = Signature.createSignature(
     466              :       switch (unsignedTx) {
     467            0 :         RawEVMTransactionType0() => unsignedTx.serializedUnsigned(type.chainId),
     468            0 :         RawEVMTransactionType1() => unsignedTx.serializedUnsigned,
     469            0 :         RawEVMTransactionType2() => unsignedTx.serializedUnsigned,
     470              :       },
     471              :       txType: switch (unsignedTx) {
     472            0 :         RawEVMTransactionType0() => TransactionType.Legacy,
     473            0 :         RawEVMTransactionType1() => TransactionType.Type1,
     474            0 :         RawEVMTransactionType2() => TransactionType.Type2,
     475              :       },
     476              :       privateKey,
     477            0 :       chainId: type.chainId,
     478              :     );
     479              : 
     480            0 :     final signedTx = unsignedTx.addSignature(signature);
     481              : 
     482              :     return signedTx;
     483              :   }
     484              : 
     485            0 :   Future<String> sendRawTransaction(String serializedTransactionHex) {
     486            0 :     serializedTransactionHex = serializedTransactionHex.startsWith("0x")
     487              :         ? serializedTransactionHex
     488            0 :         : "0x$serializedTransactionHex";
     489            0 :     return performTaskForClients(
     490            0 :       (client) => client.sendRawTransaction(serializedTransactionHex),
     491              :       minClients: 1,
     492              :       maxTriesPerClient: 1,
     493              :       maxClients: 5,
     494              :       enforceParallel: true,
     495            0 :       consilidate: (resultsWithErrors) {
     496              :         final results =
     497            0 :             resultsWithErrors.whereType<Value<String, EvmRpcClient>>().map((v) => v.value);
     498              : 
     499            0 :         if (results.isEmpty) {
     500            0 :           throw Exception(
     501            0 :             "No client was able to send the transaction: ${results}",
     502              :           );
     503              :         }
     504              : 
     505            0 :         final hashMap = results.fold<Map<String, int>>(
     506            0 :           {},
     507            0 :           (acc, hash) {
     508            0 :             acc[hash] = (acc[hash] ?? 0) + 1;
     509              :             return acc;
     510              :           },
     511              :         );
     512              : 
     513            0 :         final hash = hashMap.entries.reduce(
     514            0 :           (a, b) => a.value > b.value ? a : b,
     515              :         );
     516              : 
     517            0 :         return hash.key;
     518              :       },
     519              :     );
     520              :   }
     521              : 
     522            0 :   Future<String> buildAndBroadcastTransaction({
     523              :     required String sender,
     524              :     required String recipient,
     525              :     required Uint8List privateKey,
     526              :     required EvmFeeInformation? feeInfo,
     527              :     required Uint8List? data,
     528              :     required BigInt? value,
     529              :     List<AccessListItem>? accessList,
     530              :   }) async {
     531            0 :     final signedTx = await buildTransaction(
     532              :       sender: sender,
     533              :       recipient: recipient,
     534              :       privateKey: privateKey,
     535              :       feeInfo: feeInfo,
     536              :       data: data,
     537              :       value: value,
     538              :       accessList: accessList,
     539              :     );
     540              : 
     541            0 :     final result = await sendRawTransaction(signedTx.serialized.toHex);
     542              : 
     543              :     return result;
     544              :   }
     545              : 
     546            0 :   Future<String> readContract({
     547              :     required String contractAddress,
     548              :     required LocalContractFunctionWithValues function,
     549              :   }) async {
     550              :     assert(
     551            0 :       function.stateMutability == StateMutability.view ||
     552            0 :           function.stateMutability == StateMutability.pure,
     553              :       "Invalid function",
     554              :     );
     555              : 
     556            0 :     final data = function.buildDataField();
     557              : 
     558            0 :     return await call(
     559              :       contractAddress: contractAddress,
     560              :       data: data,
     561              :     );
     562              :   }
     563              : 
     564              :   ///
     565              :   /// Interact with Contract
     566              :   ///
     567            0 :   Future<String> interactWithContract({
     568              :     required String contractAddress,
     569              :     required LocalContractFunctionWithValues function,
     570              :     required String sender,
     571              :     required Uint8List privateKey,
     572              :     required EvmFeeInformation? feeInfo,
     573              :     BigInt? value,
     574              :   }) async {
     575            0 :     final valid = switch ((function.stateMutability, value)) {
     576            0 :       (StateMutability.nonpayable, BigInt? value) =>
     577            0 :         value == null || value == BigInt.zero, // If nonpayable, value must be 0 or null
     578            0 :       (StateMutability.payable, BigInt? value) =>
     579            0 :         value != null && value != BigInt.zero, // If payable, value must be set
     580              :       _ => false,
     581              :     };
     582            0 :     assert(valid, "Invalid value for state mutability of function");
     583              : 
     584            0 :     final data = function.buildDataField();
     585              : 
     586            0 :     return await buildAndBroadcastTransaction(
     587              :       sender: sender,
     588              :       recipient: contractAddress,
     589              :       privateKey: privateKey,
     590              :       feeInfo: feeInfo,
     591              :       data: data,
     592            0 :       value: value ?? BigInt.zero,
     593              :     );
     594              :   }
     595              : 
     596            1 :   Future<int> estimateGasLimit({
     597              :     required String sender,
     598              :     required String recipient,
     599              :     Uint8List? data,
     600              :     BigInt? value,
     601              :     BigInt? gasPrice,
     602              :   }) async {
     603            2 :     final dataHex = data != null ? "0x${data.toHex}" : null;
     604              : 
     605            1 :     return await performTask(
     606            1 :       (client) => client
     607            1 :           .estimateGasLimit(
     608              :             from: sender,
     609              :             to: recipient,
     610              :             data: dataHex,
     611              :             amount: value,
     612              :             gasPrice: gasPrice,
     613              :           )
     614            1 :           .then(
     615            2 :             (value) => value.toInt(),
     616              :           ),
     617              :     );
     618              :   }
     619              : 
     620              :   ///
     621              :   /// Send ERC721
     622              :   ///
     623            0 :   Future<String> sendERC721Nft({
     624              :     required String recipient,
     625              :     required String from,
     626              :     required int tokenId,
     627              :     required String contractAddress,
     628              :     required Uint8List privateKey,
     629              :   }) async {
     630            0 :     final function = LocalContractFunctionWithValues(
     631              :       name: "transferFrom",
     632            0 :       parameters: [
     633            0 :         FunctionParamWithValue(
     634              :           name: "from",
     635            0 :           type: FunctionParamAddress(),
     636              :           value: from,
     637              :         ),
     638            0 :         FunctionParamWithValue(
     639              :           name: "to",
     640            0 :           type: FunctionParamAddress(),
     641              :           value: recipient,
     642              :         ),
     643            0 :         FunctionParamWithValue(
     644              :           name: "tokenId",
     645            0 :           type: FunctionParamInt(),
     646              :           value: tokenId,
     647              :         ),
     648              :       ],
     649              :       stateMutability: StateMutability.nonpayable,
     650            0 :       outputTypes: [],
     651              :     );
     652              : 
     653            0 :     return await interactWithContract(
     654              :       contractAddress: contractAddress,
     655              :       function: function,
     656              :       sender: from,
     657              :       privateKey: privateKey,
     658              :       feeInfo: null,
     659              :     );
     660              :   }
     661              : 
     662            0 :   Future<ConfirmationStatus> getConfirmationStatus(String hash) async {
     663            0 :     if (txStatusCache[hash] == null || txStatusCache[hash] == ConfirmationStatus.pending) {
     664            0 :       final json = await performTask(
     665            0 :         (client) => client.getTransactionReceipt(hash),
     666              :       );
     667            0 :       txStatusCache[hash] = _confirmationStatusFromJson(json ?? {});
     668              :     }
     669            0 :     return txStatusCache[hash]!;
     670              :   }
     671              : 
     672            0 :   ConfirmationStatus _confirmationStatusFromJson(Json json) {
     673              :     if (json
     674              :         case {
     675            0 :           "status": String status_s,
     676              :         }) {
     677            0 :       final status = status_s.toBigIntOrNull;
     678            0 :       if (status == null) throw Exception('Could not parse status');
     679            0 :       if (status == BigInt.from(0)) return ConfirmationStatus.failed;
     680            0 :       if (status == BigInt.from(1)) return ConfirmationStatus.confirmed;
     681              :     }
     682              : 
     683              :     return ConfirmationStatus.pending;
     684              :   }
     685              : 
     686              :   ///
     687              :   /// Get Current Block
     688              :   ///
     689            0 :   Future<Json> getCurrentBlock() async {
     690            0 :     final blockNumber = await getBlockNumber();
     691            0 :     return await performTask(
     692            0 :       (client) => client.getBlockByNumber(blockNumber),
     693              :     );
     694              :   }
     695              : 
     696              :   ///
     697              :   /// Get Block Number
     698              :   ///
     699            1 :   Future<int> getBlockNumber() async {
     700            1 :     return await performTask(
     701            2 :       (client) => client.getBlockNumber(),
     702              :     );
     703              :   }
     704              : 
     705            0 :   Future<bool> waitForTxConfirmation(
     706              :     String hash, {
     707              :     Duration interval = const Duration(seconds: 5),
     708              :   }) async {
     709              :     while (true) {
     710            0 :       await Future.delayed(interval);
     711              : 
     712            0 :       final receipt = await performTask(
     713            0 :         (client) => client.getTransactionReceipt(hash),
     714              :       );
     715              : 
     716            0 :       switch (receipt?['status']) {
     717            0 :         case '0x1':
     718              :           return true;
     719            0 :         case '0x0':
     720              :           return false;
     721              :         default:
     722              :       }
     723              :     }
     724              :   }
     725              : 
     726            1 :   Future<String?> resolveENS({
     727              :     required String name,
     728              :     required String contractAddress,
     729              :   }) async {
     730            1 :     name = name.toLowerCase();
     731            1 :     final contract = EnsRegistryContract(
     732              :       rpc: this,
     733              :       contractAddress: contractAddress,
     734              :     );
     735              : 
     736            1 :     final resolverAddress = await contract.resolver(name: name);
     737              : 
     738              :     if (resolverAddress == null) {
     739              :       return null;
     740              :     }
     741              : 
     742            1 :     final resolver = EnsResolverContract(
     743              :       contractAddress: resolverAddress,
     744              :       rpc: this,
     745              :     );
     746              : 
     747            1 :     final addr = await resolver.addr(name: name);
     748              : 
     749              :     return addr;
     750              :   }
     751              : }
        

Generated by: LCOV version 2.0-1