Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a9631db
added rpc multiquery service functionality
juampiq6 Jan 5, 2023
8012a7f
tests for rpc multiquery service
juampiq6 Jan 5, 2023
321b755
added optional id to rpcError class for identifing mulitple queries r…
juampiq6 Jan 7, 2023
c1f0af9
documented multiqueryRpcService interface
juampiq6 Jan 7, 2023
89ad692
implemented multiquery client with method multiquery call
juampiq6 Jan 7, 2023
079977b
added multiquery client to main librart exports
juampiq6 Jan 7, 2023
f0dca94
fixed not using query id attribute for rpc body preparation
juampiq6 Jan 7, 2023
11d5839
adjusted and added some tests
juampiq6 Jan 7, 2023
0fd3ea0
added new handy EtherAmount constructor from hex
juampiq6 Jan 12, 2023
07f8fae
cleaned json rpc multiquery class
juampiq6 Jan 12, 2023
ea716ce
eth rpc query handy class for handling specific ethereum rpc queries
juampiq6 Jan 12, 2023
07c292a
finished multiquery client results handling and rpc query id autoadd …
juampiq6 Jan 12, 2023
4ad498e
reorganized factories due to extension not working when trying to use…
juampiq6 Jan 13, 2023
11d5687
added integration tests to multiquery client
juampiq6 Jan 13, 2023
d1d2fa9
start request json rpc id at 0 instead of 1
juampiq6 Feb 26, 2023
d19217e
added multiquery client as a library export
juampiq6 Feb 26, 2023
63b5748
added error throwing when eth client doesnt respond with expected num…
juampiq6 Feb 26, 2023
01dfe3c
updated integration test
juampiq6 Feb 26, 2023
3f8d660
fix camel case name
juampiq6 Feb 27, 2023
7d79f16
fixed import/export directives for part and part of directives
juampiq6 Feb 27, 2023
0677d5c
fixed import in test
juampiq6 Feb 27, 2023
867c8f5
added capacity for parsing amount in hex from different units and not…
juampiq6 Feb 28, 2023
2c1d5b1
fix rename typos
juampiq6 Mar 2, 2023
5fc330a
added documentation for muliquery request
juampiq6 Mar 2, 2023
55e3404
Merge https://github.com/xclud/web3dart into rpc_multiquery
xclud Apr 3, 2025
504ad7e
Format code.
xclud Apr 3, 2025
4df380d
Cleanup.
xclud Apr 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,54 @@ obtained from the connected node when not explicitly specified. If you only need
the signed transaction but don't intend to send it, you can use
`client.signTransaction`.

## Request with multiple querys

As of JSON-RPC specification, one can make several queries in one http request.
For using this feature, instead of using the common Web3Client, you have to instantiate
a `MultiQueryWeb3Client`, which has the same functionality as ao Web3Client, but adds the
`client.multiQueryCall` method.
For usability purposes, some helper classes have been implemented to ask for different required queries inside the same request.

The `multiqueryCall` method requires a list of `ETHRpcQuery` instances
You can easily construct them thanks to these useful custom constructors. For instance, to ask for the balance, you can use `ETHRpcQuery.getBalance`.

All queries inside this list must satisfy a condition: either all of them or none of them should have an rpc query id assigned (to manage the responses id from the server).

For any other not specified rpc method, you can construct it yourself using the normal `ETHRpcQuery` constructor. The trick here would be correctly parsing the result as desired with the `decodeFn` parameter.

Example usage:

```dart
final client = MultiQueryWeb3Client(apiUrl, Client());
final queries = [
EthRPCQuery.getBalance(
id: 2,
address: EthereumAddress.fromHex(
'0x81bEdCC7314baf7606b665909CeCDB4c68b180d6',
),
),
EthRPCQuery.callContract(
id: 1,
contractCallParams: EthContractCallParams(
contract: contract,
function: contract.function('balanceOf'),
params: [
EthereumAddress.fromHex(
'0x81bEdCC7314baf7606b665909CeCDB4c68b180d6',
),
],
),
),
EthRPCQuery.getBlockInformation(
block: BlockNum.exact(8302276),
id: 3,
)
];

final responses = await client.multiQueryCall(queries);
```


### Smart contracts

The library can parse the abi of a smart contract and send data to it. It can also
Expand Down
2 changes: 1 addition & 1 deletion lib/src/core/client.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of 'package:web3dart/web3dart.dart';
part of '../../web3dart.dart';

/// Signature for a function that opens a socket on which json-rpc operations
/// can be performed.
Expand Down
255 changes: 255 additions & 0 deletions lib/src/core/eth_rpc_query/eth_rpc_query.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
part of '../../../web3dart.dart';

/// D stands for decoded result
/// R stands for raw result
/// The idea is to maintain a stable typing when expecting raw results
/// and when using functions to parsing them.
/// Sadly Dart is not flexible with generic constructors nor factories,
/// so all "factories" are static methods (view factories.dart file)
typedef DecodableFunction<D, R> = D Function(R);

class EthQueryResult<T> {
EthQueryResult(this.result, this.id);

final T result;
final int id;

@override
String toString() {
return '{"id": $id , "result": $result}';
}
}

class EthRPCQuery<D, R> extends RPCQuery {
EthRPCQuery._({
required String function,
List<dynamic> params = const [],
int? id,
required DecodableFunction<D, R> decodeFn,
}) : _decodeFunction = decodeFn,
super(function, params, id);

final DecodableFunction<D, R> _decodeFunction;

EthQueryResult decodeResult(R rawResult) =>
EthQueryResult(_decodeFunction(rawResult), id!);

EthRPCQuery copyWithId(
int id,
) =>
EthRPCQuery<D, R>._(
id: id,
function: function,
params: this.params ?? [],
decodeFn: _decodeFunction,
);

/// Returns balance in Ether wei units of the address. (hex)
static EthRPCQuery getBalance({
required EthereumAddress address,
BlockNum atBlock = const BlockNum.current(),
int? id,
}) =>
EthRPCQuery<BigInt, String>._(
function: 'eth_getBalance',
params: [
address.with0x,
atBlock.toBlockParam(),
],
id: id,
decodeFn: (r) => hexToInt(r),
);

/// Returns the amount of Ether in wei typically needed to pay for
/// one unit of gas. (hex)
static EthRPCQuery getGasPrice(int? id) => EthRPCQuery<EtherAmount, String>._(
function: 'eth_gasPrice',
id: id,
decodeFn: (r) => EtherAmount.fromHex(r),
);

static EthRPCQuery estimateGas(
int? id,
) =>
EthRPCQuery<EtherAmount, String>._(
function: 'eth_estimateGas',
id: id,
decodeFn: (r) => EtherAmount.fromHex(r),
);

/// Returns the result of calling a contract as a List of returned results
static EthRPCQuery callContract({
required EthContractCallParams contractCallParams,
BlockNum block = const BlockNum.current(),
int? id,
}) =>
EthRPCQuery<List<dynamic>, String>._(
function: 'eth_call',
params: [
{
'to': contractCallParams.contract.address.with0x,
'data': bytesToHex(
contractCallParams.function.encodeCall(
contractCallParams.params,
),
include0x: true,
padToEvenLength: true,
),
if (contractCallParams.sender != null)
'from': contractCallParams.sender!.with0x,
},
block.toBlockParam(),
],
id: id,
decodeFn: (r) {
return contractCallParams.function.decodeReturnValues(r);
},
);

/// Returns metadata of a certain block. [returnTransactionObjects]
/// parameter defines if txs details should be returned in this call,
/// or only the tx hashes. (map)
static EthRPCQuery getBlockInformation({
required BlockNum block,
bool returnTransactionObjects = false,
int? id,
}) =>
EthRPCQuery<BlockInformation, Map<String, dynamic>>._(
function: 'eth_getBlockByNumber',
params: [
block.toBlockParam(),
returnTransactionObjects,
],
id: id,
decodeFn: (r) => BlockInformation.fromJson(r),
);

static EthRPCQuery getTransactionCount({
required EthereumAddress address,
BlockNum blockNum = const BlockNum.current(),
int? id,
}) =>
EthRPCQuery<int, String>._(
function: 'eth_getTransactionCount',
id: id,
decodeFn: (r) => hexToDartInt(r),
);

static EthRPCQuery sendRawTransaction(
Uint8List signedTransaction,
int? id,
) =>
EthRPCQuery<String, String>._(
function: 'eth_sendRawTransaction',
params: [
bytesToHex(
signedTransaction,
include0x: true,
padToEvenLength: true,
),
],
id: id,
decodeFn: (r) => r,
);

/// Returns the information of a transaction
static EthRPCQuery getTransactionByHash(
String hash,
int? id,
) =>
EthRPCQuery<TransactionInformation?, Map<String, dynamic>?>._(
function: 'eth_getTransactionByHash',
params: [hash],
id: id,
decodeFn: (r) => r != null ? TransactionInformation.fromMap(r) : null,
);

/// Returns a receipt of a transaction
static EthRPCQuery getTransactionReceipt(
String hash,
int? id,
) =>
EthRPCQuery<TransactionReceipt?, Map<String, dynamic>?>._(
function: 'eth_getTransactionReceipt',
params: [hash],
id: id,
decodeFn: (r) => r != null ? TransactionReceipt.fromMap(r) : null,
);

// Returns version of the client (String)
static EthRPCQuery getClientVersion(int? id) => EthRPCQuery<String, String>._(
function: 'web3_clientVersion',
id: id,
decodeFn: (r) => r,
);

/// Returns network id (int)
static EthRPCQuery getNetworkId(int? id) => EthRPCQuery<int, String>._(
function: 'net_version',
id: id,
decodeFn: (r) => int.parse(r),
);

/// Returns chain id (hex)
/// https://chainid.network/chains.json
static EthRPCQuery getChainId(int? id) => EthRPCQuery<BigInt, String>._(
function: 'eth_chainId',
id: id,
decodeFn: (r) => hexToInt(r),
);

/// Returns the version of the Ethereum-protocol (hex)
static EthRPCQuery getEthProtocolVersion(int? id) =>
EthRPCQuery<int, String>._(
function: 'eth_protocolVersion',
id: id,
decodeFn: (r) => hexToDartInt(r),
);

/// Returns the coinbase address (hex)
static EthRPCQuery coinbaseAddress(int? id) =>
EthRPCQuery<EthereumAddress, String>._(
function: 'eth_coinbase',
id: id,
decodeFn: (r) => EthereumAddress.fromHex(r),
);

/// Returns if the client is currently mining (bool)
static EthRPCQuery isMining(int? id) => EthRPCQuery<bool, bool>._(
function: 'eth_mining',
id: id,
decodeFn: (r) => r,
);

/// Returns the amount of hashes per second the connected node is
/// mining with. (int)
static EthRPCQuery getMiningHashrate(int? id) => EthRPCQuery<int, String>._(
function: 'eth_hashrate',
id: id,
decodeFn: (r) => hexToDartInt(r),
);

/// Returns the number of the most recent mined block on the chain.
/// (int)
static EthRPCQuery getBlockNumber(int? id) => EthRPCQuery<int, String>._(
function: 'eth_blockNumber',
id: id,
decodeFn: (r) => hexToDartInt(r),
);

/// Return the code at a specific address (hex)
static EthRPCQuery getCode({
required EthereumAddress address,
BlockNum block = const BlockNum.current(),
int? id,
}) =>
EthRPCQuery<Uint8List, String>._(
function: 'eth_getCode',
params: [
address.with0x,
block.toBlockParam(),
],
id: id,
decodeFn: (r) => hexToBytes(r),
);
}
43 changes: 43 additions & 0 deletions lib/src/core/eth_rpc_query/params_classes.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
part of '../../../web3dart.dart';

class EthContractCallParams {
EthContractCallParams({
this.sender,
required this.contract,
required this.function,
required this.params,
this.atBlock = const BlockNum.current(),
this.rpcId,
});

final EthereumAddress? sender;
final DeployedContract contract;
final ContractFunction function;
final List<dynamic> params;
final BlockNum? atBlock;
final String? rpcId;
}

class EthEstimateGasParams {
EthEstimateGasParams({
this.sender,
this.to,
this.value,
this.amountOfGas,
this.gasPrice,
this.maxPriorityFeePerGas,
this.maxFeePerGas,
this.data,
this.rpcId,
});

final EthereumAddress? sender;
final EthereumAddress? to;
final EtherAmount? value;
final BigInt? amountOfGas;
final EtherAmount? gasPrice;
final EtherAmount? maxPriorityFeePerGas;
final EtherAmount? maxFeePerGas;
final Uint8List? data;
final String? rpcId;
}
2 changes: 1 addition & 1 deletion lib/src/core/filters.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of 'package:web3dart/web3dart.dart';
part of '../../web3dart.dart';

class _FilterCreationParams {
_FilterCreationParams(this.method, this.params);
Expand Down
Loading
Loading