Add fetchAsync util and RPCSubprovider
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"ganache-core": "0xProject/ganache-core",
|
||||
"hdkey": "^0.7.1",
|
||||
"json-rpc-error": "2.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"semaphore-async-await": "^1.5.1",
|
||||
"web3-provider-engine": "14.0.6"
|
||||
|
||||
2
packages/subproviders/src/globals.d.ts
vendored
2
packages/subproviders/src/globals.d.ts
vendored
@@ -4,3 +4,5 @@ declare module '*.json' {
|
||||
export default json;
|
||||
/* tslint:enable */
|
||||
}
|
||||
|
||||
declare module 'json-rpc-error';
|
||||
|
||||
@@ -10,6 +10,7 @@ export { FakeGasEstimateSubprovider } from './subproviders/fake_gas_estimate_sub
|
||||
export { SignerSubprovider } from './subproviders/signer';
|
||||
export { RedundantSubprovider } from './subproviders/redundant_subprovider';
|
||||
export { LedgerSubprovider } from './subproviders/ledger';
|
||||
export { RPCSubprovider } from './subproviders/rpc_subprovider';
|
||||
export { GanacheSubprovider } from './subproviders/ganache';
|
||||
export { Subprovider } from './subproviders/subprovider';
|
||||
export { NonceTrackerSubprovider } from './subproviders/nonce_tracker';
|
||||
|
||||
88
packages/subproviders/src/subproviders/rpc_subprovider.ts
Normal file
88
packages/subproviders/src/subproviders/rpc_subprovider.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { assert } from '@0xproject/assert';
|
||||
import { StatusCodes } from '@0xproject/types';
|
||||
import { fetchAsync } from '@0xproject/utils';
|
||||
import { JSONRPCRequestPayload } from 'ethereum-types';
|
||||
import JsonRpcError = require('json-rpc-error');
|
||||
|
||||
import { Callback, ErrorCallback } from '../types';
|
||||
|
||||
import { Subprovider } from './subprovider';
|
||||
|
||||
/**
|
||||
* This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
|
||||
* It forwards on JSON RPC requests to the supplied `rpcUrl` endpoint
|
||||
*/
|
||||
export class RPCSubprovider extends Subprovider {
|
||||
private _rpcUrl: string;
|
||||
constructor(rpcUrl: string) {
|
||||
super();
|
||||
assert.isString('rpcUrl', rpcUrl);
|
||||
this._rpcUrl = rpcUrl;
|
||||
}
|
||||
/**
|
||||
* This method conforms to the web3-provider-engine interface.
|
||||
* It is called internally by the ProviderEngine when it is this subproviders
|
||||
* turn to handle a JSON RPC request.
|
||||
* @param payload JSON RPC payload
|
||||
* @param next Callback to call if this subprovider decides not to handle the request
|
||||
* @param end Callback to call if subprovider handled the request and wants to pass back the request.
|
||||
*/
|
||||
// tslint:disable-next-line:prefer-function-over-method async-suffix
|
||||
public async handleRequest(payload: JSONRPCRequestPayload, next: Callback, end: ErrorCallback): Promise<void> {
|
||||
const finalPayload = Subprovider.createFinalPayload(payload);
|
||||
const headers = new Headers({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
const timeoutMs = 1000;
|
||||
|
||||
let response;
|
||||
try {
|
||||
response = await fetchAsync(
|
||||
this._rpcUrl,
|
||||
{
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(finalPayload),
|
||||
},
|
||||
timeoutMs,
|
||||
);
|
||||
} catch (err) {
|
||||
end(new JsonRpcError.InternalError(err));
|
||||
return;
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
const statusCode = response.status;
|
||||
switch (statusCode) {
|
||||
case StatusCodes.MethodNotAllowed:
|
||||
end(new JsonRpcError.MethodNotFound());
|
||||
return;
|
||||
case StatusCodes.GatewayTimeout:
|
||||
const errMsg =
|
||||
'Gateway timeout. The request took too long to process. This can happen when querying logs over too wide a block range.';
|
||||
const err = new Error(errMsg);
|
||||
end(new JsonRpcError.InternalError(err));
|
||||
return;
|
||||
default:
|
||||
end(new JsonRpcError.InternalError(text));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch (err) {
|
||||
end(new JsonRpcError.InternalError(err));
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.error) {
|
||||
end(data.error);
|
||||
return;
|
||||
}
|
||||
end(null, data.result);
|
||||
}
|
||||
}
|
||||
@@ -9,18 +9,7 @@ import { Callback, ErrorCallback, JSONRPCRequestPayloadWithMethod } from '../typ
|
||||
export abstract class Subprovider {
|
||||
// tslint:disable-next-line:underscore-private-and-protected
|
||||
private engine!: Provider;
|
||||
// Ported from: https://github.com/MetaMask/provider-engine/blob/master/util/random-id.js
|
||||
private static _getRandomId(): number {
|
||||
const extraDigits = 3;
|
||||
const baseTen = 10;
|
||||
// 13 time digits
|
||||
const datePart = new Date().getTime() * Math.pow(baseTen, extraDigits);
|
||||
// 3 random digits
|
||||
const extraPart = Math.floor(Math.random() * Math.pow(baseTen, extraDigits));
|
||||
// 16 digits
|
||||
return datePart + extraPart;
|
||||
}
|
||||
private static _createFinalPayload(
|
||||
public static createFinalPayload(
|
||||
payload: Partial<JSONRPCRequestPayloadWithMethod>,
|
||||
): Partial<JSONRPCRequestPayloadWithMethod> {
|
||||
const finalPayload = {
|
||||
@@ -32,6 +21,17 @@ export abstract class Subprovider {
|
||||
};
|
||||
return finalPayload;
|
||||
}
|
||||
// Ported from: https://github.com/MetaMask/provider-engine/blob/master/util/random-id.js
|
||||
private static _getRandomId(): number {
|
||||
const extraDigits = 3;
|
||||
const baseTen = 10;
|
||||
// 13 time digits
|
||||
const datePart = new Date().getTime() * Math.pow(baseTen, extraDigits);
|
||||
// 3 random digits
|
||||
const extraPart = Math.floor(Math.random() * Math.pow(baseTen, extraDigits));
|
||||
// 16 digits
|
||||
return datePart + extraPart;
|
||||
}
|
||||
// tslint:disable-next-line:async-suffix
|
||||
public abstract async handleRequest(
|
||||
payload: JSONRPCRequestPayload,
|
||||
@@ -47,7 +47,7 @@ export abstract class Subprovider {
|
||||
* @returns JSON RPC response payload
|
||||
*/
|
||||
public async emitPayloadAsync(payload: Partial<JSONRPCRequestPayloadWithMethod>): Promise<JSONRPCResponsePayload> {
|
||||
const finalPayload = Subprovider._createFinalPayload(payload);
|
||||
const finalPayload = Subprovider.createFinalPayload(payload);
|
||||
const response = await promisify<JSONRPCResponsePayload>(this.engine.sendAsync, this.engine)(finalPayload);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -221,3 +221,15 @@ export enum RevertReason {
|
||||
ValueGreaterThanZero = 'VALUE_GREATER_THAN_ZERO',
|
||||
InvalidMsgValue = 'INVALID_MSG_VALUE',
|
||||
}
|
||||
|
||||
export enum StatusCodes {
|
||||
Success = 200,
|
||||
NotFound = 404,
|
||||
InternalError = 500,
|
||||
MethodNotAllowed = 405,
|
||||
GatewayTimeout = 504,
|
||||
}
|
||||
|
||||
export interface FetchRequest extends RequestInit {
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
@@ -35,14 +35,14 @@
|
||||
"typescript": "2.7.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereum-types": "^0.0.2",
|
||||
"@0xproject/types": "^1.0.0",
|
||||
"@0xproject/typescript-typings": "^0.4.2",
|
||||
"@types/node": "^8.0.53",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"bignumber.js": "~4.1.0",
|
||||
"ethereum-types": "^0.0.2",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"ethers": "3.0.22",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"js-sha3": "^0.7.0",
|
||||
"lodash": "^4.17.4"
|
||||
},
|
||||
|
||||
29
packages/utils/src/fetchAsync.ts
Normal file
29
packages/utils/src/fetchAsync.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { FetchRequest } from '@0xproject/types';
|
||||
import 'isomorphic-fetch';
|
||||
|
||||
export const fetchAsync = async (
|
||||
endpoint: string,
|
||||
options: FetchRequest,
|
||||
timeoutMs: number = 20000,
|
||||
): Promise<Response> => {
|
||||
let finalOptions;
|
||||
if ((process as any).browser === true) {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
setTimeout(() => {
|
||||
controller.abort();
|
||||
}, timeoutMs);
|
||||
finalOptions = {
|
||||
signal,
|
||||
...options,
|
||||
};
|
||||
} else {
|
||||
finalOptions = {
|
||||
timeout: timeoutMs,
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
const response = await fetch(endpoint, finalOptions);
|
||||
return response;
|
||||
};
|
||||
@@ -8,3 +8,5 @@ export { logUtils } from './log_utils';
|
||||
export { abiUtils } from './abi_utils';
|
||||
export { NULL_BYTES } from './constants';
|
||||
export { errorUtils } from './error_utils';
|
||||
export { fetchAsync } from './fetchAsync';
|
||||
export { FetchRequest } from '@0xproject/types';
|
||||
|
||||
@@ -4481,9 +4481,9 @@ ethereumjs-wallet@~0.6.0:
|
||||
utf8 "^2.1.1"
|
||||
uuid "^2.0.1"
|
||||
|
||||
ethers@3.0.22:
|
||||
version "3.0.22"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.22.tgz#7fab1ea16521705837aa43c15831877b2716b436"
|
||||
ethers@0xproject/ethers.js#eip-838-reasons, ethers@3.0.22:
|
||||
version "3.0.18"
|
||||
resolved "https://codeload.github.com/0xproject/ethers.js/tar.gz/b91342bd200d142af0165d6befddf783c8ae8447"
|
||||
dependencies:
|
||||
aes-js "3.0.0"
|
||||
bn.js "^4.4.0"
|
||||
@@ -6899,7 +6899,7 @@ json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0:
|
||||
json-rpc-error "^2.0.0"
|
||||
promise-to-callback "^1.0.0"
|
||||
|
||||
json-rpc-error@^2.0.0:
|
||||
json-rpc-error@2.0.0, json-rpc-error@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-rpc-error/-/json-rpc-error-2.0.0.tgz#a7af9c202838b5e905c7250e547f1aff77258a02"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user