call method
Performs an RPC request, asking the server to execute the function with the given name and the associated parameters, which need to be encodable with the json class of dart:convert.
When the request is successful, an RPCResponse with the request id and the data from the server will be returned. If not, an RPCError will be thrown. Other errors might be thrown if an IO-Error occurs.
Implementation
@override
Future<RPCResponse> call(String function,
[List<dynamic>? params, String? overrideUrl]) async {
try {
params ??= [];
final requestPayload = {
'jsonrpc': '2.0',
'method': function,
'params': params,
'id': _currentRequestId++,
};
final response = await client
.post(
Uri.parse(overrideUrl ?? url),
headers: {'Content-Type': 'application/json'},
body: json.encode(requestPayload),
)
.timeout(_requestTimeoutDuration);
if (RpcInterceptor.instance.onRpcUrlError != null &&
((response.statusCode >= 500 && response.statusCode <= 599) ||
response.statusCode == 404)) {
final overrideUrl =
await RpcInterceptor.instance.onRpcUrlError!.call(url);
if (overrideUrl != null) {
return call(function, params, overrideUrl);
}
}
if (response.statusCode < 200 || response.statusCode >= 300) {
var errorMessage = 'Invalid response data was received by the server';
// Handle common error codes
if (response.statusCode >= 400 && response.statusCode < 500) {
switch (response.statusCode) {
case 400:
errorMessage = 'Bad Request';
break;
case 401:
errorMessage = 'Unauthorized';
break;
case 403:
errorMessage = 'Forbidden';
break;
case 404:
errorMessage = 'Not Found';
break;
case 408:
errorMessage = 'Request Timeout';
break;
case 422:
errorMessage = 'Unprocessable Entity';
break;
case 429:
errorMessage = 'Too Many Requests';
break;
}
} else if (response.statusCode >= 500 && response.statusCode < 600) {
switch (response.statusCode) {
case 500:
errorMessage = 'Internal Server Error';
break;
case 502:
errorMessage = 'Bad Gateway';
break;
case 503:
errorMessage = 'Service Unavailable';
break;
case 504:
errorMessage = 'Gateway Timeout';
break;
}
}
throw HttpException(errorMessage);
}
final data = json.decode(response.body);
if (data is String && params.length == 1) {
final param = params.first?.toString();
if (param != null &&
param.length > 42 &&
RegExp(r'^(0x|0X)?[a-fA-F0-9]+$').hasMatch(param)) {
final txHash =
bytesToHex(keccak256(hexToBytes(param)), include0x: true);
final txInfo = await Future.delayed(const Duration(milliseconds: 500))
.then((_) =>
Web3Client(url, Client()).getTransactionByHash(txHash));
if (txInfo != null) {
throw Exception('Transaction not found');
}
return RPCResponse(_currentRequestId, txHash);
}
}
if (data is! Map) {
throw const RPCError(
-32700, 'Invalid response data was received by the server', null);
}
if (data.containsKey('error')) {
final error = data['error'];
final code = error['code'];
final message = error['message'] ?? error['details'];
if (RpcInterceptor.instance.onRpcUrlError != null &&
((code == -32603 &&
message?.toLowerCase().contains('internal error') ==
true) ||
code == -32005 &&
message?.toLowerCase().contains('limit exceeded') ==
true)) {
final overrideUrl =
await RpcInterceptor.instance.onRpcUrlError!.call(url);
if (overrideUrl != null) {
return call(function, params, overrideUrl);
}
}
final errorData = error['data'];
throw RPCError(code as int?, message as String?, errorData);
}
final id = data['id'] as int;
final result = data['result'];
return RPCResponse(id, result);
// ignore: avoid_catches_without_on_clauses
} catch (e) {
if (RpcInterceptor.instance.onRpcUrlError != null &&
(e is HandshakeException || e is TimeoutException)) {
final overrideUrl =
await RpcInterceptor.instance.onRpcUrlError!.call(url);
if (overrideUrl != null) {
return call(function, params, overrideUrl);
} else {
rethrow;
}
} else if (e is FormatException) {
throw const RPCError(
-32700,
'Invalid response data was received by the server',
null,
);
} else {
rethrow;
}
}
}