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

Generated by: LCOV version 2.0-1