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

Generated by: LCOV version 2.0-1