diff --git a/lib/explorer.dart b/lib/explorer.dart index 8356756..1115eeb 100644 --- a/lib/explorer.dart +++ b/lib/explorer.dart @@ -5,32 +5,46 @@ export 'src/crypto/address.dart' show Address; export 'src/network/explorer/explorer_api.dart' show + AddressBlocks, + AddressDataRequestsSolved, + AddressDetails, + AddressInfo, + AddressLabel, + AddressMintInfo, + AddressMints, + AddressValueTransferInfo, + AddressValueTransfers, + BlockDetails, + BlockInfo, + Blockchain, + BurnRate, + DrComposition, + DrHistory, + DrLieRate, + DrSolver, ExplorerException, + HashInfo, Home, + InputMerged, + InputUtxo, + Mempool, + MempoolTransactionType, + Miner, + MintInfo, NetworkStats, NodePool, + PrioritiesEstimate, + PriorityEstimate, + Rollback, + StatisticsParams, Status, SupplyInfo, + SupplyParams, + TallyTxn, Tapi, TapiInfo, - Blockchain, - AddressDetails, - AddressDataRequestsSolved, - InputUtxo, - AddressValueTransfers, - AddressBlocks, - BlockInfo, - BlockDetails, - MintInfo, - HashInfo, - TallyTxn, - ValueTransferInfo, + TransactionStatus, + TransactionType, TransactionUtxo, - InputMerged, - AddressValueTransferInfo, TxStatusLabel, - TransactionType, - TransactionStatus, - PrioritiesEstimate, - PriorityEstimate, - Mempool; + ValueTransferInfo; diff --git a/lib/src/network/explorer/explorer_api.dart b/lib/src/network/explorer/explorer_api.dart index 9581004..e5eacaf 100644 --- a/lib/src/network/explorer/explorer_api.dart +++ b/lib/src/network/explorer/explorer_api.dart @@ -1,5 +1,6 @@ import 'dart:convert' show json; +import 'package:witnet/explorer.dart'; import 'package:witnet/schema.dart'; import 'package:witnet/src/crypto/address.dart'; @@ -81,6 +82,52 @@ class ValueTransferTxn { }; } +class EpochInfo { + EpochInfo({ + required this.responseType, + required this.blockDetails, + required this.mintInfo, + required this.vtTransactions, + required this.drTransactions, + required this.commit, + required this.reveal, + required this.tally, + required this.numberOfCommits, + required this.numberOfReveals, + }); + final String responseType; + final BlockDetails blockDetails; + final MintInfo mintInfo; + final List vtTransactions; + final List drTransactions; + final dynamic commit; + final dynamic reveal; + final List tally; + final int numberOfCommits; + final int numberOfReveals; + + // factory EpochInfo.fromRawJson(String str) => + // EpochInfo.fromJson(json.decode(str)); + + // factory EpochInfo.fromJson(Map json) => EpochInfo( + // blockHash: json['blockHash'], + // epoch: json["epoch"], + // status: json['status'], + // time: json["time"], + // txnAddress: json["txn_address"], + // txnHash: json["txn_hash"], + // ); + + // Map toJson() => { + // "block_hash": blockHash, + // "epoch": epoch, + // "status": status, + // "time": time, + // "txn_address": txnAddress, + // "txn_hash": txnHash, + // }; +} + class Report { Report({ required this.commitTxns, @@ -494,38 +541,6 @@ class Status { } } -class DrSolver { - PublicKeyHash pkh; - int count; - - DrSolver({ - required this.pkh, - required this.count, - }); - - factory DrSolver.fromList(List data) { - return DrSolver(pkh: PublicKeyHash.fromAddress(data[0]), count: data[1]); - } - - List toList() => [pkh.address, count]; -} - -class Miner { - PublicKeyHash pkh; - int count; - - Miner({ - required this.pkh, - required this.count, - }); - - factory Miner.fromList(List data) { - return Miner(pkh: PublicKeyHash.fromAddress(data[0]), count: data[1]); - } - - List toList() => [pkh.address, count]; -} - class RollBack { RollBack({ required this.timestamp, @@ -862,6 +877,218 @@ class AddressBlocks { } } +class Rollback { + Rollback( + {required this.timestamp, + required this.epochFrom, + required this.epochTo, + required this.length}); + + final int timestamp; + final int epochFrom; + final int epochTo; + final int length; + + factory Rollback.fromJson(Map data) { + return Rollback( + timestamp: data['timestamp'], + epochFrom: data['epoch_from'], + epochTo: data['epoch_to'], + length: data['length'], + ); + } + + Map jsonMap() { + return { + 'timestamp': timestamp, + 'epoch_from': epochFrom, + 'epoch_to': epochTo, + 'length': length, + }; + } +} + +class Miner { + Miner({ + required this.address, + required this.amount, + }); + + final String address; + final int amount; + + factory Miner.fromJson(Map data) { + return Miner( + address: data['address'], + amount: data['amount'], + ); + } + + Map jsonMap() { + return { + 'address': address, + 'amount': amount, + }; + } +} + +class DrSolver { + DrSolver({ + required this.address, + required this.amount, + }); + + final String address; + final int amount; + + factory DrSolver.fromJson(Map data) { + return DrSolver( + address: data['address'], + amount: data['amount'], + ); + } + + Map jsonMap() { + return { + 'address': address, + 'amount': amount, + }; + } +} + +class DrHistory { + DrHistory({ + required this.total, + required this.failure, + }); + + final int total; + final int failure; + + factory DrHistory.fromJson(Map data) { + return DrHistory( + total: data['total'], + failure: data['failure'], + ); + } + + Map jsonMap() { + return { + 'total': total, + 'failure': failure, + }; + } +} + +class DrComposition { + DrComposition( + {required this.total, + required this.httpGet, + required this.httpPost, + required this.rng}); + + final int total; + final int httpGet; + final int httpPost; + final int rng; + + factory DrComposition.fromJson(Map data) { + return DrComposition( + total: data['total'], + httpGet: data['http_get'], + httpPost: data['http_post'], + rng: data['rng']); + } + + Map jsonMap() { + return { + 'total': total, + 'http_get': httpGet, + 'http_post': httpPost, + 'rng': rng + }; + } +} + +class DrLieRate { + DrLieRate( + {required this.witnessingActs, + required this.errors, + required this.noRevealLies, + required this.outOfConsensusLies}); + + final int witnessingActs; + final int errors; + final int noRevealLies; + final int outOfConsensusLies; + + factory DrLieRate.fromJson(Map data) { + return DrLieRate( + witnessingActs: data['witnessing_acts'], + errors: data['errors'], + noRevealLies: data['no_reveal_lies'], + outOfConsensusLies: data['out_of_consensus_lies']); + } + + Map jsonMap() { + return { + 'witnessing_acts': witnessingActs, + 'errors': errors, + 'no_reveal_lies': noRevealLies, + 'out_of_consensus_lies': outOfConsensusLies + }; + } +} + +class BurnRate { + BurnRate({ + required this.reverted, + required this.lies, + }); + + final int reverted; + final int lies; + + factory BurnRate.fromJson(Map data) { + return BurnRate( + reverted: data['reverted'], + lies: data['lies'], + ); + } + + Map jsonMap() { + return { + 'reverted': reverted, + 'lies': lies, + }; + } +} + +class AddressLabel { + AddressLabel({ + required this.address, + required this.label, + }); + + final String address; + final String label; + + factory AddressLabel.fromRawJson(String str) => + AddressLabel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(jsonMap()); + + factory AddressLabel.fromJson(Map json) => AddressLabel( + address: json["address"], + label: json["label"], + ); + + Map jsonMap() => { + "address": address, + "label": label, + }; +} + class AddressDetails { AddressDetails({ required this.address, @@ -869,7 +1096,6 @@ class AddressDetails { required this.eligibility, required this.reputation, required this.totalReputation, - required this.type, }); final String address; @@ -877,7 +1103,6 @@ class AddressDetails { final int eligibility; final int reputation; final int totalReputation; - final String type; factory AddressDetails.fromRawJson(String str) => AddressDetails.fromJson(json.decode(str)); @@ -890,7 +1115,6 @@ class AddressDetails { eligibility: json["eligibility"], reputation: json["reputation"], totalReputation: json["total_reputation"], - type: json["type"], ); Map jsonMap() => { @@ -899,7 +1123,64 @@ class AddressDetails { "eligibility": eligibility, "reputation": reputation, "total_reputation": totalReputation, - "type": type, + }; +} + +class AddressDataRequestsCreated { + AddressDataRequestsCreated({ + required this.address, + required this.dataRequestsCreated, + }); + + final String address; + final List dataRequestsCreated; + + factory AddressDataRequestsCreated.fromRawJson(String str) => + AddressDataRequestsCreated.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory AddressDataRequestsCreated.fromJson(Map json) { + return AddressDataRequestsCreated( + address: json["address"], + dataRequestsCreated: List.from( + json["data_requests_created"] + .map((dr) => DataRequestCreatedInfo.fromJson(dr))), + ); + } + + Map toJson() => { + "address": address, + "data_requests_created": + dataRequestsCreated.map((e) => e.jsonMap()).toList().toList(), + }; +} + +class AddressMints { + AddressMints({ + required this.address, + required this.mints, + }); + + final String address; + final List mints; + + factory AddressMints.fromRawJson(String str) => + AddressMints.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory AddressMints.fromJson(Map json) { + return AddressMints( + address: json["address"], + mints: List.from( + json["mints"].map((mint) => AddressMintInfo.fromJson(mint))), + ); + } + + Map toJson() => { + "address": address, + "mints": mints.map((e) => e.jsonMap()).toList().toList(), }; } @@ -1030,38 +1311,38 @@ class AddressValueTransferInfo { } class AddressInfo { - final String address; - final String label; - final int active; - final int block; - final int mint; - final int value_transfer; - final int data_request; - final int commit; - final int reveal; - final int tally; - AddressInfo({ - required this.address, - required this.label, required this.active, + required this.address, required this.block, - required this.mint, - required this.value_transfer, - required this.data_request, required this.commit, + required this.dataRequest, + required this.label, + required this.mint, required this.reveal, required this.tally, + required this.valueTransfer, }); + final int active; + final String address; + final int block; + final int commit; + final int dataRequest; + final String label; + final int mint; + final int reveal; + final int tally; + final int valueTransfer; + factory AddressInfo.fromJson(Map json) => AddressInfo( address: json["address"], label: json["label"], active: json["active"], block: json["block"], mint: json["mint"], - value_transfer: json["value_transfer"], - data_request: json["data_request"], + valueTransfer: json["value_transfer"], + dataRequest: json["data_request"], commit: json["commit"], reveal: json["reveal"], tally: json["tally"], @@ -1073,14 +1354,67 @@ class AddressInfo { "active": active, "block": block, "mint": mint, - "value_transfer": value_transfer, - "data_request": data_request, + "value_transfer": valueTransfer, + "data_request": dataRequest, "commit": commit, "reveal": reveal, "tally": tally, }; } +class AddressMintInfo { + AddressMintInfo({ + required this.confirmed, + required this.epoch, + required this.hash, + required this.miner, + required this.outputValue, + required this.timestamp, + }); + + final bool confirmed; + final int epoch; + final String hash; + final String miner; + final int outputValue; + final int timestamp; + + factory AddressMintInfo.fromRawJson(String str) => + AddressMintInfo.fromJson(json.decode(str)); + + String rawJson() => json.encode(jsonMap()); + + factory AddressMintInfo.fromJson(Map json) { + print('JSON MINT ADDRESS ADATA ${json}'); + return AddressMintInfo( + confirmed: json['confirmed'], + epoch: json['epoch'], + hash: json['hash'], + miner: json['miner'], + outputValue: json['output_value'], + timestamp: json['timestamp']); + } + + Map jsonMap() => { + "confirmed": confirmed, + "epoch": epoch, + "hash": hash, + "miner": miner, + "outputValue": outputValue, + "timestamp": timestamp, + }; + + void printDebug() { + print('AddressMintInfo'); + print('confirmed: ${confirmed}'); + print('epoch: ${epoch}'); + print('hash: ${hash}'); + print('miner: ${miner}'); + print('outputValue: ${outputValue}'); + print('timestamp: ${timestamp}'); + } +} + class MintInfo { MintInfo({ required this.miner, @@ -1244,8 +1578,41 @@ class TransactionUtxo { // TODO: use this enum in all the package enum TxStatusLabel { pending, mined, confirmed, reverted, unknown } +enum MempoolTransactionType { value_transfers, data_requests } + enum TransactionType { value_transfer, data_request, mint } +enum SupplyParams { + blocks_minted, + blocks_minted_reward, + blocks_missing, + blocks_missing_reward, + current_locked_supply, + current_time, + current_unlocked_supply, + epoch, + in_flight_requests, + locked_wits_by_requests, + maximum_supply, + current_supply, + total_supply, + supply_burned_lies +} + +enum StatisticsParams { + list_rollbacks, + num_unique_miners, + num_unique_data_request_solvers, + top_100_miners, + top_100_data_request_solvers, + percentile_staking_balances, + histogram_data_requests, + histogram_data_request_composition, + histogram_data_request_lie_rate, + histogram_burn_rate, + histogram_value_transfers, +} + class TransactionStatus { TxStatusLabel status = TxStatusLabel.pending; TransactionStatus({required this.status}); @@ -1648,6 +2015,76 @@ class BlockDetails { } } +class DataRequestCreatedInfo { + DataRequestCreatedInfo( + {required this.collateral, + required this.consensusPercentage, + required this.epoch, + required this.hash, + required this.numErrors, + required this.numLiars, + required this.result, + required this.success, + required this.timestamp, + required this.totalFee, + required this.witnesses}); + + final int collateral; + final int consensusPercentage; + final int epoch; + final String hash; + final int numErrors; + final int numLiars; + final String result; + final bool success; + final int timestamp; + final int totalFee; + final int witnesses; + + factory DataRequestCreatedInfo.fromJson(Map data) { + return DataRequestCreatedInfo( + collateral: data['collateral'], + consensusPercentage: data["consensus_percentage"], + epoch: data['epoch'], + hash: data['hash'], + numErrors: data['num_errors'], + numLiars: data['num_liars'], + result: data['result'], + success: data['success'], + timestamp: data['timestamp'], + totalFee: data['total_fee'], + witnesses: data['witnesses']); + } + + Map jsonMap() => { + 'collateral': collateral, + 'consensusPercentage': consensusPercentage, + 'epoch': epoch, + 'hash': hash, + 'numErrors': numErrors, + 'numLiars': numLiars, + 'result': result, + 'success': success, + 'timestamp': timestamp, + 'totalFee': totalFee, + 'witnesses': witnesses, + }; + + void printDebug() { + print('collateral: $collateral'); + print('consensusPercentage $consensusPercentage'); + print('epoch $epoch'); + print('hash $hash'); + print('numErrors $numErrors'); + print('numLiars $numLiars'); + print('result $result'); + print('success $success'); + print('timestamp $timestamp'); + print('totalFee $totalFee'); + print('witnesses $witnesses'); + } +} + class DataRequestSolvedInfo { DataRequestSolvedInfo( {required this.success, @@ -2059,14 +2496,16 @@ class Mempool { }); final int timestamp; - final List fee; - final List amount; - - factory Mempool.fromJson(Map json) => Mempool( - timestamp: json["timestamp"], - fee: json["fee"], - amount: json["amount"], - ); + final List fee; + final List amount; + + factory Mempool.fromJson(dynamic json) { + return Mempool( + timestamp: json["timestamp"], + fee: List.from(json["fee"]), + amount: List.from(json["amount"]), + ); + } Map jsonMap() => { "timestamp": timestamp, diff --git a/lib/src/network/explorer/explorer_client.dart b/lib/src/network/explorer/explorer_client.dart index 4566faf..236d069 100644 --- a/lib/src/network/explorer/explorer_client.dart +++ b/lib/src/network/explorer/explorer_client.dart @@ -1,5 +1,5 @@ import 'dart:convert'; - +import 'package:recase/recase.dart'; import 'package:http/http.dart' as http; import 'package:http/retry.dart'; import 'dart:convert' as convert; @@ -12,6 +12,7 @@ import 'explorer_api.dart' show AddressBlocks, AddressDataRequestsSolved, + AddressDataRequestsCreated, AddressValueTransfers, BlockDetails, Mempool, @@ -296,17 +297,133 @@ class ExplorerClient { } } - Future mempool( + Future> mempool( {required String transactionType, int? startEpoch, int? stopEpoch}) async { - // TODO: This was not working on mywitwallet. Should we add a class? try { - return Mempool.fromJson(await client.get(api('network/mempool', { + List data = await client.get(api('network/mempool', { "transaction_type": transactionType, "start_epoch": startEpoch, "stop_epoch": stopEpoch - }))); + })); + return data.map((mempoolTx) => Mempool.fromJson(mempoolTx)).toList(); + } on ExplorerException catch (e) { + throw ExplorerException( + code: e.code, message: '{"mempool": "${e.message}"}'); + } + } + + Future supply({required SupplyParams transactionType}) async { + try { + return await client.get(api('network/supply', { + "key": transactionType.name, + })); + } on ExplorerException catch (e) { + throw ExplorerException( + code: e.code, message: '{"supply": "${e.message}"}'); + } + } + + Future getStats( + {required int startEpoch, + required int stopEpoch, + required StatisticsParams statisticsType}) async { + try { + print('stats ${statisticsType.name.paramCase}'); + return await client.get(api('network/statistics', { + "key": statisticsType.name.paramCase, + "start_epoch": startEpoch, + "stop_epoch": stopEpoch, + })); + } on ExplorerException catch (e) { + throw ExplorerException( + code: e.code, message: '{"statistics": "${e.message}"}'); + } + } + + Future statistics( + {required int startEpoch, + required int stopEpoch, + required StatisticsParams statisticsType}) async { + try { + dynamic data = await getStats( + startEpoch: startEpoch, + stopEpoch: stopEpoch, + statisticsType: statisticsType); + switch (statisticsType) { + case StatisticsParams.histogram_burn_rate: + return List.from(data[StatisticsParams.histogram_burn_rate.name]) + .map((burnRate) => BurnRate.fromJson(burnRate)) + .toList(); + case StatisticsParams.histogram_data_request_composition: + return List.from(data[ + StatisticsParams.histogram_data_request_composition.name]) + .map((drComposition) => DrComposition.fromJson(drComposition)) + .toList(); + case StatisticsParams.histogram_data_request_lie_rate: + return List.from( + data[StatisticsParams.histogram_data_request_lie_rate.name]) + .map((lieRate) => DrLieRate.fromJson(lieRate)) + .toList(); + case StatisticsParams.histogram_data_requests: + return List.from(data[StatisticsParams.histogram_data_requests.name]) + .map((drHistory) => DrHistory.fromJson(drHistory)) + .toList(); + case StatisticsParams.histogram_value_transfers: + return data[StatisticsParams.histogram_value_transfers.name]; + case StatisticsParams.list_rollbacks: + return List.from(data[StatisticsParams.list_rollbacks.name]) + .map((rollback) => Rollback.fromJson(rollback)) + .toList(); + case StatisticsParams.num_unique_data_request_solvers: + int uniqueDrSolvers = + data[StatisticsParams.num_unique_data_request_solvers.name]; + return uniqueDrSolvers; + case StatisticsParams.num_unique_miners: + int uniqueMiners = data[StatisticsParams.num_unique_miners.name]; + return uniqueMiners; + case StatisticsParams.percentile_staking_balances: + List stakingBalances = + List.from(data['staking']['ars']).map((e) => e as int).toList(); + return stakingBalances; + case StatisticsParams.top_100_miners: + List topMiners = + List.from(data[StatisticsParams.top_100_miners.name]) + .map((miner) => Miner.fromJson(miner)) + .toList(); + return topMiners; + case StatisticsParams.top_100_data_request_solvers: + List topDrSolvers = List.from( + data[StatisticsParams.top_100_data_request_solvers.name]) + .map((miner) => DrSolver.fromJson(miner)) + .toList(); + return topDrSolvers; + } + } on ExplorerException catch (e) { + throw ExplorerException( + code: e.code, message: '{"statistics": "${e.message}"}'); + } + } + + Future epoch({required int epoch}) async { + try { + Map data = await client.get(api('search/epoch', { + "value": epoch, + })); + return BlockDetails.fromJson(data); + } on ExplorerException catch (e) { + throw ExplorerException( + code: e.code, message: '{"epoch": "${e.message}"}'); + } + } + + Future> transactionMempool( + {required MempoolTransactionType transactionType}) async { + try { + return (await client.get(api('transaction/mempool', { + "type": transactionType.name, + })))[transactionType.name.substring(0, transactionType.name.length - 1)]; } on ExplorerException catch (e) { throw ExplorerException( code: e.code, message: '{"mempool": "${e.message}"}'); @@ -351,9 +468,38 @@ class ExplorerClient { total: result.total, totalPages: result.totalPages, ); - // case 'details': - // var data = await client.get(api('address', {'address': value})); - // return AddressDetails.fromJson(data); + case 'info': + List data = + await client.get(api('address/info', {'addresses': value})); + return data.length > 0 + ? data.map((info) => AddressInfo.fromJson(info)).toList() + : []; + case 'details': + Map data = + await client.get(api('address/details', {'address': value})); + return AddressDetails.fromJson({...data, 'address': value}); + case 'labels': + List data = await client.get(api('address/labels')); + return data.length > 0 + ? data.map((label) => AddressLabel.fromJson(label)).toList() + : []; + case 'mints': + PaginatedRequest result = await client.get(api( + 'address/mints', + {'address': value, 'page': page, 'page_size': pageSize})); + List data = result.data; + if (findAll) { + data = await getAllResults(result, 'address/mints', + {'address': value, 'page': page, 'page_size': pageSize}); + } + return PaginatedRequest( + data: AddressMints.fromJson({'address': value, 'mints': data}), + firstPage: result.firstPage, + lastPage: result.lastPage, + page: result.page, + total: result.total, + totalPages: result.totalPages, + ); case 'data_requests_solved': PaginatedRequest result = await client.get(api( 'address/data-requests-solved', @@ -373,9 +519,23 @@ class ExplorerClient { totalPages: result.totalPages, ); case 'data_requests_created': - // TODO: implement method - // waiting on the explorer to return valid response - break; + PaginatedRequest result = await client.get(api( + 'address/data-requests-created', + {'address': value, 'page': page, 'page_size': pageSize})); + List data = result.data; + if (findAll) { + data = await getAllResults(result, 'address/data-requests-created', + {'address': value, 'page': page, 'page_size': pageSize}); + } + return PaginatedRequest( + data: AddressDataRequestsCreated.fromJson( + {'address': value, 'data_requests_created': data}), + firstPage: result.firstPage, + lastPage: result.lastPage, + page: result.page, + total: result.total, + totalPages: result.totalPages, + ); case 'value_transfers': PaginatedRequest result = await client.get(api( 'address/value-transfers', @@ -430,8 +590,7 @@ class ExplorerClient { } } - Future send( - {required Map transaction, bool test = false}) async { + Future send({required Map transaction}) async { try { var response = await client.post(api('transaction/send'), transaction); if (response.containsKey('error')) { diff --git a/pubspec.yaml b/pubspec.yaml index b10daff..fd0d9bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,4 +29,5 @@ dependencies: xml: ^6.5.0 protobuf: ^3.1.0 fixnum: ^1.1.0 + recase: ^4.1.0 diff --git a/test/explorer_api_test.dart b/test/explorer_api_test.dart new file mode 100644 index 0000000..65caa52 --- /dev/null +++ b/test/explorer_api_test.dart @@ -0,0 +1,625 @@ +import 'package:witnet/data_structures.dart'; +import 'package:witnet/explorer.dart'; +import 'package:test/test.dart'; +import 'package:witnet/src/network/explorer/explorer_api.dart'; + +String explorerUrl = 'witscan.xyz'; +ExplorerClient explorer = + ExplorerClient(url: explorerUrl, mode: ExplorerMode.production); +void main() async { + test('getValueTransfersfromAddress', () async { + final result = await getValueTransfersfromAddress( + 'wit174la8pevl74hczcpfepgmt036zkmjen4hu8zzs'); + expect(result.runtimeType, PaginatedRequest); + }); + test('getValueBlocksfromAddress', () async { + final result = await getValueBlocksfromAddress( + 'wit174la8pevl74hczcpfepgmt036zkmjen4hu8zzs'); + expect(result.runtimeType, PaginatedRequest); + }); + + test('getDataRequestsSolvedfromAddress', () async { + final result = await getDataRequestsSolvedfromAddress( + 'wit174la8pevl74hczcpfepgmt036zkmjen4hu8zzs'); + expect(result.runtimeType, PaginatedRequest); + }); + + test('getUtxoInfo', () async { + final result = + await getUtxoInfo('wit1uy7kqvg44rsn9pwqgafwskty6dcar4u9muy03m'); + expect(result.runtimeType, List); + }); + + test('block details from hash', () async { + final result = await hash( + '0fc2583304ef73a0f9a9373e9ba35912d084b90d75097377b9afae0e47ecf4ef'); + expect(result.runtimeType, BlockDetails); + }); + + test('value transfer from hash', () async { + final result = await hash( + 'e8c121059ac70e20038e1731474eef6dafbd637e22076ded6a13165e0fcc57fa'); + expect(result.runtimeType, ValueTransferInfo); + }); + + test('priority', () async { + final result = await priority( + 'e6ef538b9d9e1184eac2fc61d32a937502fc4c6e5b231b6f74317e6defd2e511'); + expect(result.runtimeType, PrioritiesEstimate); + }); + + test('status', () async { + final result = await status(); + expect(result.runtimeType, Status); + }); + test('networkBalances', () async { + final result = await networkBalances(page: 1, pageSize: 1); + ; + expect(result.runtimeType, PaginatedRequest); + }); + + test('networkBlockchain', () async { + final result = await networkBlockchain(page: 1, pageSize: 1); + expect(result.runtimeType, PaginatedRequest); + }); + + test('reputation', () async { + final result = await reputation(page: 1, pageSize: 1); + expect(result.runtimeType, NetworkReputation); + }); + + test('tapi', () async { + final result = await tapi(page: 1, pageSize: 1); + expect(result.runtimeType, Tapi); + }); + + test('getDRCreatedfromAddress', () async { + final result = await getDRCreatedfromAddress( + 'wit19cxgkuecj32rl0gekj39h3gpdvthd4damhlhhm'); + expect(result.runtimeType, PaginatedRequest); + }); + + test('getDetailsfromAddress', () async { + final result = await getDetailsfromAddress( + 'wit19cxgkuecj32rl0gekj39h3gpdvthd4damhlhhm'); + expect(result.runtimeType, AddressDetails); + }); + + test('getInfofromAddress', () async { + final result = + await getInfofromAddress('wit12fzl294n4vgzt9lulzk3y790j0t0c828sx30k6'); + expect(result.runtimeType, List); + }); + + test('getLabelsfromAddress', () async { + final result = await getLabelsfromAddress( + 'wit19cxgkuecj32rl0gekj39h3gpdvthd4damhlhhm'); + expect(result.runtimeType, List); + }); + + test('getMintsfromAddress', () async { + final result = + await getMintsfromAddress('wit12fzl294n4vgzt9lulzk3y790j0t0c828sx30k6'); + expect(result.runtimeType, PaginatedRequest); + }); + + test('epoch', () async { + final result = await epoch(epoch: 10); + expect(result.runtimeType, BlockDetails); + }); + + test('transactionMempool data_requests', () async { + final result = await transactionMempool( + transactionType: MempoolTransactionType.data_requests); + expect(result.runtimeType, List); + }); + + test('transactionMempool value_transfers', () async { + final result = await transactionMempool( + transactionType: MempoolTransactionType.value_transfers); + expect(result.runtimeType, List); + }); + + test('mempool value_transfers', () async { + final result = await mempool( + transactionType: MempoolTransactionType.data_requests, + startEpoch: 1000, + stopEpoch: 1010); + expect(result.runtimeType, List); + }); + + test('mempool value_transfers', () async { + final result = await mempool( + transactionType: MempoolTransactionType.value_transfers, + startEpoch: 1000, + stopEpoch: 1010); + expect(result.runtimeType, List); + }); + + test('statistics histogram_burn_rate', () async { + final result = + await statistics(statisticsType: StatisticsParams.histogram_burn_rate); + expect(result.runtimeType, List); + }); + + test('statistics histogram_data_request_composition', () async { + final result = await statistics( + statisticsType: StatisticsParams.histogram_data_request_composition); + expect(result.runtimeType, List); + }); + + test('statistics histogram_data_request_lie_rate', () async { + final result = await statistics( + statisticsType: StatisticsParams.histogram_data_request_lie_rate); + expect(result.runtimeType, List); + }); + test('statistics histogram_data_requests', () async { + final result = await statistics( + statisticsType: StatisticsParams.histogram_data_requests); + expect(result.runtimeType, List); + }); + test('statistics histogram_value_transfers', () async { + final result = await statistics( + statisticsType: StatisticsParams.histogram_value_transfers); + expect(result.runtimeType, List); + }); + test('statistics list_rollbacks', () async { + final result = + await statistics(statisticsType: StatisticsParams.list_rollbacks); + expect(result.runtimeType, List); + }); + test('statistics num_unique_data_request_solvers', () async { + final result = await statistics( + statisticsType: StatisticsParams.num_unique_data_request_solvers); + expect(result.runtimeType, int); + }); + test('statistics num_unique_miners', () async { + final result = + await statistics(statisticsType: StatisticsParams.num_unique_miners); + expect(result.runtimeType, int); + }); + test('statistics percentile_staking_balances', () async { + final result = await statistics( + statisticsType: StatisticsParams.percentile_staking_balances); + expect(result.runtimeType, List); + }); + test('statistics top_100_miners', () async { + final result = + await statistics(statisticsType: StatisticsParams.top_100_miners); + expect(result.runtimeType, List); + }); + test('statistics top_100_data_request_solvers', () async { + final result = await statistics( + statisticsType: StatisticsParams.top_100_data_request_solvers); + expect(result.runtimeType, List); + }); + test('supply', () async { + final result = await supply(supplyParams: SupplyParams.blocks_minted); + expect(result.runtimeType, String); + }); + + test('sendTransaction', () async { + final tx = { + "ValueTransfer": { + "body": { + "inputs": [ + { + "output_pointer": + "240c6ccb33acf100a5ba54040e15af8203f3dfe2a2f63d80db12194556cfd988:0" + }, + { + "output_pointer": + "240c6ccb33acf100a5ba54040e15af8203f3dfe2a2f63d80db12194556cfd988:0" + } + ], + "outputs": [ + { + "pkh": "wit1zl7ty0lwr7atp5fu34azkgewhtfx2fl4wv69cw", + "time_lock": 0, + "value": 1 + } + ] + }, + "signatures": [ + { + "public_key": { + "bytes": + "959fa1639e00d9d6e90ca2c4279a9daef5e80ec46d14e94582aab263e6a1e604", + "compressed": 2 + }, + "signature": { + "Secp256k1": { + "der": + "3045022100edfc1e1769de6fa489a33bf06c819b29e69944bdff58141e8d946724bb3576720220419ec2182ea92c5d2a7027bdca05009899887b4d2a446d37ddaf7731ef8e1b62" + } + } + }, + { + "public_key": { + "bytes": + "959fa1639e00d9d6e90ca2c4279a9daef5e80ec46d14e94582aab263e6a1e604", + "compressed": 2 + }, + "signature": { + "Secp256k1": { + "der": + "3045022100edfc1e1769de6fa489a33bf06c819b29e69944bdff58141e8d946724bb3576720220419ec2182ea92c5d2a7027bdca05009899887b4d2a446d37ddaf7731ef8e1b62" + } + } + } + ] + } + }; + final result = await sendTransaction(transaction: tx, test: true); + expect(result, {'result': 'Value transfer is valid.'}); + }); + // Test pending transactions cannot be deterministic + // await pendingByHash(''); + // TODO: add support for data requests + // await drByHash( + // 'cee144d79d2a229c01e30b4c07fe43684082ab8cd8d54b61ea7113f0173d3679'); + // await commitByHash( + // 'cee144d79d2a229c01e30b4c07fe43684082ab8cd8d54b61ea7113f0173d3679'); + // await revealByHash( + // 'cee144d79d2a229c01e30b4c07fe43684082ab8cd8d54b61ea7113f0173d3679'); + // await tallyByHash( + // 'cee144d79d2a229c01e30b4c07fe43684082ab8cd8d54b61ea7113f0173d3679'); + // await drReportByHash( + // 'cee144d79d2a229c01e30b4c07fe43684082ab8cd8d54b61ea7113f0173d3679'); + // await drHistoryByHash( + // 'cee144d79d2a229c01e30b4c07fe43684082ab8cd8d54b61ea7113f0173d3679'); +} + +// #1 address/value-transfers +Future getValueTransfersfromAddress(String address) async { + try { + PaginatedRequest result = await explorer.address( + tab: "value_transfers", value: address, page: 1, pageSize: 1); + print("getValueTransfersfromAddress: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #2 address/blocks +Future getValueBlocksfromAddress(String address) async { + try { + PaginatedRequest result = await explorer.address( + tab: "blocks", value: address, page: 1, pageSize: 1); + print("getValueBlocksfromAddress: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #3 address/data-requests-solved +Future getDataRequestsSolvedfromAddress(String address) async { + try { + dynamic result = await explorer.address( + tab: "data_requests_solved", value: address, page: 1, pageSize: 1); + print("getDataRequestsSolvedfromAddress: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #4 api/address/utxos +Future getUtxoInfo(String address) async { + try { + List result = await explorer.getUtxoInfo(address: address); + print("getUtxoInfo: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #5 api/home +Future home() async { + try { + dynamic result = await explorer.home(); + print("home: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #6 api/search/hash +Future hash(String hash) async { + try { + dynamic result = await explorer.hash(value: hash); + print("hash: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #6.1 search/hash pending +Future pendingByHash(String hash) async { + try { + dynamic result = await explorer.hash(value: hash); + print("pendingByHash: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #6.2 search/hash data_request +Future drByHash(String hash) async { + try { + dynamic result = await explorer.hash(value: hash); + print("drByHash: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #6.3 search/hash commit +Future commitByHash(String hash) async { + try { + dynamic result = await explorer.hash(value: hash); + print("commitByHash: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #6.4 search/hash reveal +Future revealByHash(String hash) async { + try { + dynamic result = await explorer.hash(value: hash); + print("revealByHash: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #6.5 search/hash tally +Future tallyByHash(String hash) async { + try { + dynamic result = await explorer.hash(value: hash); + print("tallyByHash: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #6.6 search/hash data_request_report +Future drReportByHash(String hash) async { + try { + dynamic result = await explorer.hash(value: hash); + print("drReportByHash: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #6.7 search/hash data_request_report +Future drHistoryByHash(String hash) async { + try { + dynamic result = await explorer.hash(value: hash); + print("drHistoryByHash: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #7 api/transaction/priority +Future priority(String hash) async { + try { + PrioritiesEstimate result = await explorer.valueTransferPriority(); + print("priority: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #8 api/transaction/send +Future sendTransaction( + {required Map transaction, bool test = false}) async { + try { + dynamic result = await explorer.send(transaction: { + 'test': test, + 'transaction': transaction, + }); + print("sendTransaction: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #9 api/status +Future status() async { + try { + Status result = await explorer.status(); + print("status: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #10 api/network/balances +Future networkBalances({int? page, int? pageSize}) async { + try { + PaginatedRequest result = + await explorer.networkBalances(page: page, pageSize: pageSize); + print("networkBalances: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #11 api/network/blockchain +Future networkBlockchain({int? page, int? pageSize}) async { + try { + PaginatedRequest result = + await explorer.blockchain(page: page, pageSize: pageSize); + print("networkBlockchain: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #12 api/network/reputation +Future reputation({int? page, int? pageSize}) async { + try { + NetworkReputation result = await explorer.reputation(); + print("reputation: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #13 api/network/tapi + +Future tapi({int? page, int? pageSize}) async { + try { + Tapi result = await explorer.tapi(); + print("tapi: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #14 api/address/data-requests-created +Future getDRCreatedfromAddress(String address) async { + try { + dynamic result = await explorer.address( + tab: "data_requests_created", value: address, page: 1, pageSize: 1); + print("getDRCreatedfromAddress: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #15 api/address/details +Future getDetailsfromAddress(String address) async { + try { + dynamic result = await explorer.address( + tab: "details", value: address, page: 1, pageSize: 1); + print("getDetailsfromAddress: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #16 api/address/info +Future getInfofromAddress(String address) async { + try { + List result = await explorer.address( + tab: "info", value: address, page: 1, pageSize: 1); + print("getInfofromAddress: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #17 api/address/labels +Future getLabelsfromAddress(String address) async { + try { + List result = await explorer.address( + tab: "labels", value: address, page: 1, pageSize: 1); + print("getLabelsfromAddress: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #18 api/address/mints +Future getMintsfromAddress(String address) async { + try { + dynamic result = await explorer.address( + tab: "mints", value: address, page: 1, pageSize: 1); + print("getMintsfromAddress: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #20 api/transaction/mempool +Future mempool( + {required MempoolTransactionType transactionType, + int? startEpoch, + int? stopEpoch}) async { + try { + List result = await explorer.mempool( + transactionType: transactionType.name, + startEpoch: startEpoch, + stopEpoch: stopEpoch); + print("mempool: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #21 api/network/statistics +Future statistics({required StatisticsParams statisticsType}) async { + try { + dynamic result = await explorer.statistics( + startEpoch: 10, stopEpoch: 15, statisticsType: statisticsType); + print("statistics: ${statisticsType} : ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #22 api/network/supply +Future supply({required SupplyParams supplyParams}) async { + try { + String result = await explorer.supply(transactionType: supplyParams); + print("supply: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #23 api/search/epoch +Future epoch({required int epoch}) async { + try { + dynamic result = await explorer.epoch(epoch: epoch); + print("epoch: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +} + +// #24 api/network/mempool +Future transactionMempool( + {required MempoolTransactionType transactionType}) async { + try { + dynamic result = + await explorer.transactionMempool(transactionType: transactionType); + print("Status: ${result}"); + return result; + } on ExplorerException catch (e) { + print(e.message); + } +}